Android

面试题整理
地址1

1. Android 应用的结构是什么?

  • src 目录
    是源代码目录
  • gen 目录
    保存 ADT 自动生成的 java 文件,例如 R.java 或 AIDL 文件。注意:
    R.java 文件(非常重要)
    a) R.java 文件是 ADT 自动生成的文件,包含对 drawable、layout 和 values 目录内的资源的引用指针,Android 程序能够直接通过 R 类引用目录中的资源
    b) R.java 文件不能手工修改,如果向资源目录中增加或删除了资源文件,则需要在工程名称上右击,选择 Refresh 来更新 R.java 文件中的代码
    c) R 类包含的几个内部类,分别与资源类型相对应,资源 ID 便保存在这些内部类中,例如子类drawable 表示图像资源,内部的静态变量 icon 表示资源名称,其资源 ID 为 0x7f020000。一般情况下,资源名称与资源文件名相同
  • android.jar 文件
    是 Android 程序所能引用的函数库文件,Android 通过平台所支持 API 都包含在这个文件中
  • assets 目录
    用来存放原始格式的文件,例如音频文件、视频文件等二进制格式文件。此目录中的资源不能被 R.java 文件索引。,所以只能以资截流的形式读取。一般情况下为空
  • layout 目录
    用来存放我们为每个界面写的布局文件
  • AndroidManifest.xml
    是 XML 格式的 Android 程序声明文件,包含了 Android 系统运行Android 程序前所必须掌握的重要信息,这些信息包含应用程序名称、图标、包名称、模块组成、授权和 SDK 最低版本等,而且每个 Android 程序必须在根目录下包含一个 AndroidManifest.xml文件
      1. AndroidManifest.xml 文件的根元素是 manifest,包含了 xmlns:android、package、
        android:versionCode 和 android:versionName 共 4 个属性
      1. xmlns:android 定义了 Android 的命名空间,值为http://schemas.android.com/apk/res/android
      1. package 定义了应用程序的包名称
      1. android:versionCode 定义了应用程序的版本号,是一个整数值,数值越大说明版本越新,但仅在程序内部使用,并不提供给应用程序的使用者
      1. android:versionName 定义了应用程序的版本名称,是一个字符串,仅限于为用户提供一个版本标识
      1. manifest 元素仅能包含一个 application 元素, application 元素中能够声明 Android 程序中最重要的四个组成部分,包括 Activity、Service、BroadcastReceiver 和 ContentProvider,所定义的属性将影响所有组成部分
      1. android:icon 定义了 Android 应用程序的图标,其中@drawable/icon 是一种资源引用方式,表示资源类型是图像,资源名称为 icon,对应的资源文件为 res/drawable 目录下的 icon.png
      1. android:label 则定义了 Android 应用程序的标签名称default.properties 文件记录 Android 工程的相关设置,该文件不能手动修改,需右键单击工程名称,选择“Properties”进行修改

2. Android 应用中如何保存数据。

    1. 使用SharedPreferences存储数据 
      SharedPreference 可以保存和检索的任何基本数据类( boolean, float, int, long, string)的持久键-值对(基于XML文件存储的“key-value”键值对数据)。
      其存储在“data/data/程序包名/shared_prefs目录下。
    • 获取 SharedPreferences :
      getSharedPreferences (String name, int mode)
      getPreferences (int mode)
    • Context.MODE_PRIVATE
      该SharedPreferences数据只能被本应用程序读、写。
      Context.MODE_WORLD_READABLE
      该SharedPreferences数据能被其他应用程序读,但不能写。
      Context.MODE_WORLD_WRITEABLE
      该SharedPreferences数据能被其他应用程序读和写。
      Context.MODE_MULTI_PROCESS
      sdk2.3后添加的选项,当多个进程同时读写同一个SharedPreferences时它会检查文件是否修改。
    • 向Shared Preferences中写入值
      通过 SharedPreferences.Editor 获取到 Editor 对象;通过 Editor 的 putBoolean() 或 putString()等方法存入值;
      最后调用 Editor 的 commit() 方法提交;
      -从 SharedPreferences 中读取值
      读取值使用 SharedPreference 对象的 getBoolean() 或 getString() 等方法就行了
    • Preferences 是很轻量级的应用,使用起来也很方便,简洁。但存储数据类型比较单一(只有基本数据类型),无法进行条件查询,只能在不复杂的存储需求下使用,比如保存配置信息等。
    1. 文件存储数据
    • 默认存储位置:/data/data/包名/files/文件名
    • 创建和写入一个内部存储的私有文件:
          String FILENAME = "a.txt";
          String string = "fanrunqi";
          try {
              //Context.MODE_PRIVATE = 0, 文件是私有数据,只能被应用本身访问,写入的内容会覆盖原文件的内容。
              //Context.MODE_APPEND = 32768, 该模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。 
              //Context.MODE_WORLD_READABLE = 1,表示当前文件可以被其他应用读取。
              //MODE_WORLD_WRITEABLE, 表示当前文件可以被其他应用写入。
              //调用Context的openFileOutput()函数,填入文件名和操作模式,它会返回一个FileOutputStream对象。
              FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
              //通过FileOutputStream对象的write()函数写入数据。
              fos.write(string.getBytes());
              //FileOutputStream对象的close ()函数关闭流。
              fos.close();
          } catch (Exception e) {
              e.printStackTrace();
          }
    
    • 读取一个内部存储的私有文件:
          //调用openFileInput( ),参数中填入文件名,会返回一个FileInputStream对象。
          //使用流对象的 read() 方法读取字节
          //调用流的 close() 方法关闭流
          String FILENAME = "a.txt";
          try {
              FileInputStream inStream = openFileInput(FILENAME);
              int len = 0;
              byte[] buf = new byte[1024];
              StringBuilder sb = new StringBuilder();
              while ((len = inStream.read(buf)) != -1) {
                  sb.append(new String(buf, 0, len));
              }
              inStream.close();
          } catch (Exception e) {
              e.printStackTrace();
          } 
    
    • 保存内存缓存文件:
      getCacheDir() 去打开一个文件,文件的存储目录( /data/data/包名/cache )是一个应用专门来保存临时缓存文件的内存目录。
    • 使用外部存储(sdcard)
      <!-- 在SDCard中创建与删除文件权限 --> 
      <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 
      <!-- 往SDCard写入数据权限 --> 
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    

    检测外部存储的可用性:

    //获取外存储的状态
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
      // 可读可写
      mExternalStorageAvailable = mExternalStorageWriteable = true;
    } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
      // 可读
    } else {
      // 可能有很多其他的状态,但是我们只需要知道,不能读也不能写  
    }
    

    访问外部存储器中的文件
    如果 API 版本大于或等于8,使用

        // 类型参数DIRECTORY_MUSIC 和 DIRECTORY_RINGTONES(传null就是你应用程序的文件目录的根目录)通过指定目录的类型,确保Android的媒体扫描仪将扫描分类系统中的文件(例如,铃声被确定为铃声)。如果用户卸载应用程序,这个目录及其所有内容将被删除。
        getExternalFilesDir (String type)
        File file = new File(getExternalFilesDir(null), "fanrunqi.jpg");
        getExternalFilesDir (String type)
    

    如果API 版本小于 8 (7或者更低)

     getExternalStorageDirectory ()
     通过该方法打开外存储的根目录,你应该在以下目录下写入你的应用数据,这样当卸载应用程序时该目录及其所有内容也将被删除。
     /Android/data/<package_name>/files/
    
    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
              File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录  "/sdcard"        
    
                 File saveFile = new File(sdCardDir,"a.txt"); 
    
                 //写数据
                  try {
                      FileOutputStream fos= new FileOutputStream(saveFile); 
                      fos.write("fanrunqi".getBytes()); 
                      fos.close();
                  } catch (Exception e) {
                      e.printStackTrace();
                  } 
    
                  //读数据
                   try {
                      FileInputStream fis= new FileInputStream(saveFile); 
                      int len =0;
                      byte[] buf = new byte[1024];
                      StringBuffer sb = new StringBuffer();
                      while((len=fis.read(buf))!=-1){
                          sb.append(new String(buf, 0, len));
                      }
                      fis.close();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }  
          }
    
    • 其他一些经常用到的方法:
      getFilesDir(): 得到内存储文件的绝对路径
      getDir(): 在内存储空间中创建或打开一个已经存在的目录
      deleteFile(): 删除保存在内部存储的文件。  
      fileList(): 返回当前由应用程序保存的文件的数组(内存储目录下的全部文件)。
    1. SQLite数据库存储数据
      http://blog.csdn.net/amazing7/article/details/51375012
    1. 使用ContentProvider存储数据
      http://blog.csdn.net/amazing7/article/details/51324022
    1. 网络存储数据

3. 如何在 Android 应用中执行耗时操作。

Android 耗时操作放到非 UI 线程中执行,例如:AsyncTask,Handler,Thread,Loaders。

  • AsyncTask 的问题:
    1. cancel方法实现不是很好,一旦执行了 doInBackground,就算调用取消方法,也会将 doInBackground里面的代码执行完毕,才会停止。
    2. 内存泄露问题,在 Activity 中使用非静态匿名内部类,由于 AsyncTask 的生命周期可能会比 Activity 长,当 Activity 销毁时,AsyncTask 还在执行,由于 AsyncTask 持有 Activity 的应用,导致 Activity 无法回收,产生内存泄漏。
    3. 结果丢失:屏幕旋转的时候,Activity 重新创建,还在运行的 AsyncTask 会持有之前 Activity 的非法引用,导致 onPostExecute() 不起作用。

4. 两个 Fragment 之间如何通信。

参考
两个 Fragment 无法直接进行通信,只能通过他们所寄托的 Activity 来进通信。
两个方式:方式一,Fragment 内部直接互相调用(不推荐)。方式二,通过回调实现 Fragment 之间的通信。
方式一:

        // 在一个 Fragment 中
        Button Btn1 = (Button) view.findViewById(R.id.btn_home);// 获取按钮资源  
        Btn1.setOnClickListener(new Button.OnClickListener() {// 创建监听  
            public void onClick(View v) {  
                // 得到当前绑定的 Activity 实例
                MainTab activity = ((MainTab) getActivity());  
                // 拿到 Fragment 实例
                FragmentManager manager = activity.getSupportFragmentManager();  
                // 拿到需要的 Fragment 实例
                FragmentMessage fragment = (FragmentMessager) manager.findFragmentByTag("tab2");  
                if (null != fragment) {  
                    View vw = fragment.getView();  
                    if (null != vw) {  
                        TextView txt = (TextView) vw.findViewById(R.id.tv);  
                        T.showShort(getActivity(), txt.getText().toString());  
                    }  
                } else {  
                    T.showShort(getActivity(), "FragmentMessage还未创建");  
                }  
  
            }  
  
        });  

方式二:
分两步:

  • 在 Fragment1 里定义接口,在 Activity 中实现接口。通过回调实现 Fragment1 与 Activity 的通信。
  • 在 Activity 接口回调中与 Fragment2 通信,将参数传递给 Fragment2。
    定义 Fragmen1 接口:
public interface OnArticleClickedListener{
    public void onArticleSelected(int position); 
}

private void  setOnArticleClickedListener(OnArticleClickedListener listener){  
    this.listener = listener;  
}  

public void onListItemClick(ListView l, View v, int position, long id) {  
    this.listener.onArticleSelected(position);  
} 

Activity :

fragment1.setOnArticleClickedListener(new Fragment1.OnArticleClickedListener(){  
    onArticleSelected(int position){  
        //调用f2的方法,与进行通信  
        fragment2.showArticleDetail(position);  
    }  
);

5. 阐述一下 Android 的通知系统。

参考1,参考2,参考3

通知流程

ServiceManager、SystemSerer和SystemUI是android系统三个非常重要的进程,相信熟悉Android系统框架的亲们已经比较了解了。所有的系统服务都是运行在SystemServer这个进程中,并且将自身注册到ServiceManager中。应用层通过Framework API发送通知时,会通过Binder驱动相应地调用NotificationManagerService的enqueueNotification方法。enqueueNotification方法记录通知内容,调用StatusBarManangerService的updateNotifcation接口,并且执行响铃、震动和呼吸灯效果,最后,如果有注册相应的辅助服务(AccessibilityService),则给辅助服务发送通知事件。StatusBarManagerService会将通知事件再次转发给SystemUI中负责通知栏显示的StatusBar。注意到Android系统内建有两个通知栏,一个是PhoneStatusBar,一个是TabletStatusBar,根据系统配置的类型决定用哪一种。

6. 两个不同的 app 之间如何交互。

  • 使用 componentName。
    componentName 与 Intent 同处在 android.content 包下,用来定义一个应用程序的组件(Activity,Service,BroadcastReceiver或者ContentProvide)。
    构造函数 ComponentName(String pkg,String cls)
    demo: app1 启动 app1
    Intent intent = new Intent();
    ComponentName cn = new ComponentName("com.example.app2","com.example.app2.MainActivity");
    startActivity(intent);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容