Android 面试:常见问题总结

一、Activity的生命周期

Activity其实是继承了ApplicationContext这个类

public class Activity extends ApplicationContext {  
    protected void onCreate(Bundle savedInstanceState);  
    protected void onStart();  
    protected void onResume();  
    protected void onPause();  
    protected void onStop();  
    protected void onDestroy();   
}

显示过程:onCreate -> onStart -> onResume
销毁过程:onPause -> onStop -> onDestroy

当前Activity被中断:
若新出的一个Activity是全屏,那么:onPause->onStop ,恢复的时候onReStart->onStart->onResume .
如果打断这个应用程序的是一个Theme 为 Translucent 或者是 Dialog 的Activity 那么只是 onPause ,恢复时 onResume .

  1. 按Back键时:onPause()->onStop()->onDestory()
  2. 按Home键时:onPause()->onStop()
    这时候应用程序并没有销毁,再次回到该应用时:onRestart()->onStart()->onResume()

下面使用一个例子:EditText的内容保存

  1. 问题
    • 在一个EditText里输入如”Hello World !”字符串。
    • 按一下HOME键,然后再次启动刚才的应用程序。
      这时候EditText里并没有我们输入的”Hello World !”字样.
  2. 解决办法
    // 定义一个String 类型用来存取我们EditText输入的值 
    private String mString; 
    //当我们按HOME键时,我在onPause方法里,将输入的值赋给mString 
    @Override  
    protected void onPause() {  
        super.onPause();  
        mString = mEditText.getText().toString();  
    }  
    //再次启动应用时,我们要恢复先前状态 
    @Override  
    protected void onRestart() {  
        super.onRestart();  
        mEditText.setText(mString);  
    }   

二、生命周期方法中系统在做什么和我们应该做什么

   onCreate:  在这里创建界面,做一些数据的初始化工作。

 onStart:  到这一步变成用户可见不可交互的。  onResume: 变成和用户可交互的。 (在activity 栈系统通过栈的方式管理这些个 Activity的最上面,运行完弹出栈,则回到上一个Activity) onPause: 到这一步是可见但不可交互的。 1.系统会停止动画等消耗CPU的事情 2.从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候 你的程序的优先级降低,有可能被系统收回。 3.在这里保存的数据,应该在onResume里读出来, 注意:这个方法里做的事情时间要短,因为下一个activity不会等到这个方法完成才启动。 onstop: 变得不可见 ,被下一个activity覆盖了。 onDestroy: 这是activity被干掉前最后一个被调用方法了,可能是外面类调用finish方法 或者是系统为了节省空间将它暂时性的干掉,可以用isFinishing()来判断它, 如果你有一个Progress Dialog在线程中转动,请在onDestroy里把他cancel掉, 不然等线程结束的时候,调用Dialog的cancel方法会抛异常的。

onPause,onstop, onDestroy 三种状态 下Activity都有可能被系统干掉。

  • 为了保证程序的正确性,你 要在onPause()里写上持久层操作的代码,将用户编辑的内容都保存到存储介质上(一般是数据库 )。
  • 实际工作中因为生命周期的变化而带来的问题也很多,比如你的应用程序起了新的线程在跑,这时候中断了,你还要去维护那个线程,是暂停还是杀掉还是数据回滚,是吧?因为Activity可能被杀掉,所以线程中使用的变量和一些界面元素就千万要注意了,一般我都是采用Android的消息机制 [Handler,Message]来处理多线程和界面交互的问题。

三、如何让Activity变成一个窗口

  1. 在AndroidManifest.xml 中设置Activity的主题
<!-- 使得Activity对话框的形式弹出来 -->
android :theme="@android:style/Theme.Dialog"
android:theme="@android:style/Theme.Dialog" 

<!-- 变成半透明 -->
android:theme="@android:style/Theme.Translucent"
android:theme="@android:style/Theme.Translucent" 

类似这种Activity的属性可以在android.R.styleable 类的AndroidManifestActivity 方法中看到,AndroidManifest.xml中所有元素的属性的介绍都可以参考这个类 android.R.styleable.

四、Service使用场景和生命周期

1. 概念

Service不能与用户交互,不能自己启动,运行在后台。
如果我们退出应用时,Service进程并没有结束,它仍然在后台运行。

2. 使用场景

  • 当我们想音乐在后台播放,就得Service,否则就听不到歌。
  • 当应用的数据是通过网络获取的,不同时间段的数据是不同的
    我们可以用Service在后台定时更新,而不用每打开应用的时候在去获取。

3. Service生命周期

Service的生命周期比Activity简单,它只继承了onCreate(),onStart(),onDestroy()三个方法。

  • 第一次启动Service时,先后调用了onCreate() , onStart()
  • 停止Service时,则执行onDestroy()
  • 如果Service已经启动,当我们再次启动Service时,不会在执行onCreate(),而是直接执行onStart()

五、JNI 的书写步骤

  1. 声明 native 方法
  2. 编译该java类
  3. 使用 javah ? jni java 类名 生成扩展名为.h的头文件
  4. 使用 C/C++实现本地方法
  5. 将 C/C++编写的文件生成动态连接库

这里以HelloWorld为例:

class HelloWorld {
    // 声明native方法,并且不能实现
    public native void displayHelloWorld();

    /* 加载动态库, 参数“hello”是动态库的名字 * 可以这样理解:我们的方法displayHelloWorld()没有实现, * 但是我们在下面就直接使用了,所以必须在使用之前对它进行初始化 * 这里一般是以static块进行加载 */
    static {
        System.loadLibrary("hello");
    }

    public static void main(String[] args) {
        new HelloWorld().displayHelloWorld();
    }
}

六、JNI 调用中考虑的问题

1. java和c是如何互通的?

  • 不能互通的原因主要是 数据类型 的问题。
  • 但jni解决了这个问题,例如c文件中的 jstring 数据类型就是 java传入的String对象 ,经过jni函数的转化就能成为c的 char*

对应数据类型关系如下表:

Java 类型 本地c类型 说明
boolean jboolean 无符号,8 位
byte jbyte 无符号,8 位
char jchar 无符号,16 位
short jshort 有符号,16 位
int jint 有符号,32 位
long jlong 有符号,64 位
float jfloat 32 位
double jdouble 64 位
void void N/A

2. 如何将java传入的String参数转换为c的char*,然后使用?

  • java 传入的String参数,在c文件中被jni转换为jstring的数据类型
  • 在c文件中声明 char* test,然后
    test = (char*)(*env)->GetStringUTFChars(env, jstring, NULL);
  • 注意:test使用完后,通知虚拟机平台相关代码无需再访问:
    (*env)->ReleaseStringUTFChars(env, jstring, test);

3. 将c中获取的一个char*的buffer传递给java?

  1. 这个char*如果是一般的字符串的话,作为 string 传回去就可以了。
  2. 如果是含有’/0’的buffer,最好作为 bytearray 传出,
    因为可以制定copy的length,如果copy到string,可能到’/0’就截断了。

有两种方式传递得到的数据:

  1. 在jni中直接new一个byte数组,然后调用函数(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);将buffer的值copy到bytearray中,函数直接return bytearray就可以了。
  2. return 错误号,数据作为参数传出,但是java的基本数据类型是传值,对象是传递的引用,所以将这个需要传出的byte数组用某个类包一下,如下:
class RetObj {
    public byte[] bytearray;
}

这个对象作为函数的参数retobj传出,通过如下函数将retobj中的byte数组赋值便于传出。代码如下:

jclass cls;
jfieldID fid;
jbyteArray bytearray;
bytearray = (*env)->NewByteArray(env,len);
(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer);
cls = (*env)->GetObjectClass(env, retobj);
fid = (*env)->GetFieldID(env, cls, "retbytes", "[B"]);
(*env)->SetObjectField(env, retobj, fid, bytearray);

4. 不知道占用多少空间的buffer,如何传递出去呢?

  • 在 jni 的c文件中new出空间,传递出去。
  • java的数据不初始化,指向传递出去的空间即可。

七、Android中的动画有哪几类,它们的特点和区别是什么?

  1. Tween动画:使视图组件移动、放大、缩小以及产生透明度的变化
    给出两个关键帧,通过一些算法将给定属性值在给定的时间内在两个关键帧间渐变。
  2. Frame动画:传统的动画方法,通过顺序的播放排列好的图片来实现。
  3. 属性动画:真正的改变属性。

八、Handler机制的原理

Andriod 提供了 Handler 和 Looper 来满足线程间的通信。

  1. Looper : 一个线程可以产生一个Looper对象,管理此线程里的MessageQueue(消息队列)。
  2. Handler : 构造Handler对象来与Looper沟通,
    如push新消息到MessageQueue里,或者接收Looper从MessageQueue取出的消息。
  3. MessageQueue : 消息队列,用来存放线程放入的消息。
  4. 线程 : UI线程通常就是主线程,程序启动时会替它建立一个MessageQueue。

九、mvc模式的原理以及在Android中的运用

松耦合,组件的重用。

  1. view:视图层,一般采用xml文件进行界面的描述,也可以使用javascript+html等的方式作为view层。
  2. controller:一般指Acitvity,所以不要在acitivity中数据操作等耗时操作的代码,要通过activity交割model业务逻辑层处理。
  3. model:对数据库的操作、对网络等的操作都应该在model里面处理,当然对业务计算等操作也是必须放在的该层的。

十、介绍下Android的数据存储方式

  1. 使用SharedPreferences存储数据;
  2. 文件存储数据;
  3. SQLite数据库存储数据;
  4. 使用ContentProvider存储数据;
  5. 网络存储数据;
    Android 中的数据存储都是私有的,其他应用程序都是无法访问的,除非通过ContentResolver获取其他程序共享的数据。

十一、介绍下ContentProvider是如何实现数据共享的

ContentProvider:为存储和获取数据提供统一的接口,可以在不同的应用程序之间共享数据。
无论数据的来源是什么,ContentProvider都会认为是一种表,然后把数据组织成表格。

  1. 定义一个继承ContentProvider的类
  2. 实现ContentProvider的所有方法 (query、insert、update、delete、getType、onCreate)
  3. 在AndroidMinifest.xml中进行声明

十二、如何启用Service,如何停用Service

  1. 通过调用Context.startService()启动,调用Context.stopService()结束,
    startService()可以传递参数给Service.
  2. 第二种方式是通过调用Context.bindService()启动,调用Context.unbindservice()结束
    这种方式可以通过ServiceConnection访问Service。
    • 在Service每一次的开启关闭过程中,只有onStartCommand()可被多次调用(通过多次startService调用),
    • 其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。

十三、注册广播接收器有几种方式,这些方式有何优缺点?

Android中,不同 进程之间 传递信息要用到广播。

方式1. 在Manifest.xml中注册广播

是一种比较推荐的方法,不需要手动注销广播。
(如果广播未注销,程序退出时可能会出错),范例代码:

<receiver android:name = ".MyBroadcastReceiver" <intent-filter>
        <action android:name = "com.zhuanghongji.broadcastreceiver.test" />
    <intent-filter/>
<receiver/>

上面两个android:name分别是 广播名广播动作名
如果要自己发送一个广播,在代码中为:

Intent i = new Intent(“com.zhuanghongji.broadcastreceiver.test”);
sendBroadcast(i);

广播就发出去了,然后是接收。
新建一个类,继承至BroadcastReceiver,也可以建一个BroadcastReceiver的实例,然后得写onReceive()方法,实现如下:

protected BroadcastReceiver mEvtReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals("com.zhuanghongji.broadcastreceiver.test")) {
            //Do something
        }
    }
};

方式2. 接在代码中实现,但需要手动注册注销

通过IntentFilter设置action,使用 registerReceiver() 方法进行注册。

IntentFilter filter = new IntentFilter();
filter.addAction("com.zhuanghongji.broadcastreceiver.test");
registerReceiver(mEvtReceiver, filter); 
// 这时注册了一个recevier ,名为mEvtReceiver,
// 然后同样用上面的方法以重写onReceiver,
// 最后在程序的onDestroy中要注销广播,实现如下:

@Override
public void onDestroy() {
    super.onDestroy();
    unregisterReceiver(mPlayerEvtReceiver);
}

十四、请谈谈Android引入广播机制的用意

1. 解答

  • Android系统中的广播是广泛用于 应用程序之间通信 的一种手段。
    类似于事件处理机制,不同的地方就是广播的处理是系统级别的事件处理过程(一般事件处理是控件级别的)。在此过程中仍然是离不开Intent对象,理解广播事件的处理过程,灵活运用广播处理机制,在关键之处往往能实现特别的效果。

  • 在Android 中如果要发送一个广播必须使用sendBroadCast(Intent intent) 向系统发送对其感兴趣的广播接收器中。

  • 使用广播必须要有一个intent 对象必设置其action动作对象使用广播必须在配置文件中显式的指明该广播对象。
  • 每次接收广播 都会重新生成一个接收广播的对象,在 BroadCast中 尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理.

补充:

2. BroadcastReceiver的生命周期:

这里写图片描述
大意为:如果一个广播处理完onReceive 那么系统将认定此对象将不再是一个活动的对象,也就会finished掉它。

3. 如何在一个广播接收器中处理多个动作呢?

  • 同样的你必须在 Intent-filter 里面注册该动作,可以是系统的广播动作也可以是自己需要的广播。
  • 之后你之需要在onReceive 方法中,通过 intent.getAction() 判断传进来的动作即可做出不同的处理,不同的动作。

小结:

  • 在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。
  • 使用广播必须要有一个intent 对象必设置其action动作对象
  • 使用广播必须在配置文件中显式的指明该广播对象
  • 每次接收广播都会重新生成一个接收广播的对象
  • 在BroadCast 中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理

十五、单线程模型中Message,Handler,MessageQueue,Looper之间的关系

  • Message:Handler接收和处理的消息对象。
  • Handler:发送和处理消息。
  • MessageQueue:消息队列,以”先进先出”的方式管理Message。
  • Looper:读取MessageQueue的消息,然后把消息交给”发送该消息的Handler”进行处理。

详细内容可以看我的另一篇总结:Android 总结:Message,MessageQueue,Looper,Handler 消息机制

十六、AIDL 如何工作?能处理哪些类型的数据?

1. 概述

AIDL:Android Interface Definition Language(安卓接口描述语言)

  • 编译器可以通过 aidl文件 生成一段代码,通过预先定义的接口达到 两个进程内部通信,进程间跨界对象访问的目的。
  • AIDL的IPC机制是基于接口的,是轻量级的。使用代理类在客户端和实现层间传递值.。

2. 使用AIDL, 需要完成2件事情

  1. 引入AIDL的相关类
  2. 调用aidl产生的class
    理论上, 参数可以传递基本数据类型和String, 还有就是Bundle的派生类。

3. 具体实现步骤

  1. 创建AIDL文件,
    在这个文件里面定义接口, 该接口定义了可供客户端访问的方法和属性
  2. 编译AIDL文件
    用Ant的话, 可能需要手动, 使用Eclipse plugin的话,可以根据adil文件自动生产java文件并编译, 不需要人为介入。
  3. 在Java文件中, 实现AIDL中定义的接口
    编译器会根据AIDL接口, 产生一个JAVA接口。这个接口有一个名为Stub的内部抽象类,它继承扩展了接口并实现了远程调用需要的几个方法。
  4. 向客户端提供接口 ITaskBinder
    如果写的是service,扩展该Service并重载 onBind () 方法来返回一个实现上述接口的类的实例。
  5. 在服务器端回调客户端的函数
    前提是当客户端获取的IBinder接口的时候,要去注册回调函数, 只有这样, 服务器端才知道该调用那些函数。

4. AIDL语法和支持的数据类型

AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。
由于远程调用的需要, 这些参数和返回值并不是任何类型。
下面是些AIDL支持的数据类型:

  1. 不需要import声明的简单Java编程语言类型(int,boolean等)
  2. String, CharSequence 不需要特殊声明
  3. List, Map和Parcelables类型, 这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型.

5. 实现接口时有几个原则

  1. 抛出的异常不要返回给调用者. 跨进程抛异常处理是不可取的.
  2. IPC调用是同步的。
    如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。
    也就是说IPC调用会挂起应用程序导致界面失去响应。
    这种情况应该考虑单起一个线程来处理.
  3. 不能在AIDL接口中声明静态属性。

6. IPC的调用步骤

  1. 声明一个接口类型的变量,该接口类型在.aidl文件中定义。
  2. 实现ServiceConnection。
  3. 调用ApplicationContext.bindService(),并在ServiceConnection实现中进行传递.
  4. 在ServiceConnection.onServiceConnected()实现中,
    你会接收一个IBinder实例(被调用的Service).
    调用YourInterfaceName.Stub.asInterface((IBinder)service)将参数转换为YourInterface类型。
  5. 调用接口中定义的方法。 你总要检测到DeadObjectException异常,该异常在连接断开时被抛出。它只会被远程方法抛出。
  6. 断开连接,调用接口实例中的ApplicationContext.unbindService()

十七、什么是ANR?如何避免它?

1. 什么是ANR?

  1. ANR:Application Not Responding

    • 在Android中,活动管理器和窗口管理器这两个系统服务负责监视应用程序的响应。
    • Android应用程序完全运行在一个独立的线程中当出现下列情况时,任何在主线程中运行的,需要消耗大量时间的操作都会引发ANR。
    • 因为此时,你的应用程序已经没有机会去响应输入事件和意向广播(Intent broadcast)。
  2. 当出现下列情况时,Android就会显示ANR对话框了:

    • 对输入事件(如按键、触摸屏事件)的响应超过5秒
    • 意向接受器(intentReceiver)超过10秒钟仍未执行完毕

2. 如何避免它?

  • 潜在的比较耗时的操作,如访问网络和数据库;
  • 或者是开销很大的计算,比如改变位图的大小;
  • 需要在一个单独的子线程中完成 (或者是使用异步请求,如数据库操作)

主线程为子线程提供了 Handler,让子线程在即将结束的时候调用它与主线程通信。

十八、谈谈你对Intent的理解

一个intent是一个Intent对象,它保存了消息的内容。

  1. 对于activity和service来说,它指定了请求的操作名称和待操作数据的URI
  2. Intent对象可以显式的指定一个目标组件。
    • 如果这样的话,android会找到这个组件 (基于manifest文件中的声明)并激活它。
    • 但如果一个目标不是显式指定的,android必须找到响应intent的最佳组件。
      它是通过将Intent对象和目标的intent filter相比较来完成这一工作的。一个组件的intent filter告诉android该组件能处理的intent。intent filter也是在manifest文件中声明的。

十九、如何将SQLite数据库(dictionary.db文件)与apk文件一起发布?

  • 可以将.db文件复制到res的raw目录中。
  • 所有在raw目录中的文件不会被压缩,这样可以直接提取该目录中的文件。

二十、如何操作raw目录中的数据库文件?

在Android中不能直接打开raw目录中的数据库文件

  1. 需要在程序第一次启动时将该文件复制到手机内存或SD卡的某个目录中,然后再打开该数据库文件。
  2. 复制的基本方法是使用 getResources().openRawResource() 方法获得raw目录中资源的 InputStream 对象,然后将该InputStream对象中的数据写入其他的目录中相应文件中。
  3. 在Android SDK中可以使用 SQLiteDatabase.openOrCreateDatabase() 方法来打开任意目录中的SQLite数据库文件。

二十一、Android引入广播机制的用意?

  1. 在同一个应用中,方便几大组件的信息和数据交互。
  2. 在应用间,实现互相通信 (例如在自己的应用程序内监听系统来电)。
  3. 效率上(参考UDP的广播协议在局域网的方便性)。
  4. 设计模式上(反转控制的一种应用,类似监听者模式)

二十二、DDMS和TraceView的区别?

  • DDMS:一个程序执行查看器,在里面可以看见线程和堆栈等信息
  • TraceView:程序性能分析器

二十三、谈谈Android的IPC机制

IPC:内部进程通信

  • 是为了让Activity和Service之间可以随时的进行交互
    且只适用于Activity和Service之间的通信。
  • 通过定义AIDL接口文件来定义IPC接口
    Servier端实现IPC接口
    Client端调用IPC接口本地代理。

二十四、描述一下android的系统架构

android系统架构分“自上而下”为:

  • 应用程序层
  • 应用程序框架层
  • 运行库
  • linux 内核层

    1. applications : 该层是java的应用程序层
      android内置的googlemaps、e-mail、即时通信工具、浏览器、mp3播放 器等处于该层,java开发人员开发的程序也处于该层,而且和内置的应用程序具有平等的位置,可以调用内置的应用程序,也可以替换内置的应用程序。
    2. application framework:应用软件架构
      java应用程序开发人员主要是使用该层封装好的api进行快速开发。
    3. libraries和 androidruntime:
      • libraries:即c/c++函数库部分 :
        大多数都是开放源代码的函数库,例如webkit,该函数库负责 android网页浏览器的运行,例如标准的c函数库libc、openssl、sqlite等,当然也包括支持游戏开发2dsgl和 3dopengles,在多媒体方面有mediaframework框架来支持各种影音和图形文件的播放与显示,例如mpeg4、h.264、mp3、 aac、amr、jpg和png等众多的多媒体文件格式。
      • android的runtime : 负责解释和执行生成的dalvik格式的字节码。
    4. linuxkernel:负责硬件的驱动程序、网络、电源、系统安全以及内存管理等功能
  • Android应用程序使用框架的api并在框架下运行,这就带来了程序开发的高度一致性。

  • 另一方面也告诉我们,要想写出优质高效的程序就必须对整个 applicationframework进行非常深入的理解
  • 精通application framework,你就可以真正的理解android的设计和运行机制,也就更能够驾驭整个应用层的开发。

二十五、某公司高级面试题(2015-03-14)

  1. 详述Android系统架构,包括层与层之间调用、binder、jni、底层文件读写方法。
  2. 描述自己的一个项目,要求画出结构图,UML图,详细描述项目种的技术点,技术难点以及解决方案。
  3. 一道算法。
  4. 谈谈自己项目管理的方法、对敏捷软件开发的理解

二十六、请解释下在单线程模型中Message,Handler,Message Queue,Looper之间的关系。

  1. 拿主线程来说,主线程启动时会调用Looper.prepare()方法,会初始化一个Looper,放入Threadlocal中,接着调用Looper.loop()不断遍历Message Queue 。
  2. Handler的创建依赖与当前线程中的Looper,如果当前线程没有Looper则必须调用Looper.prepare()。
  3. Handler通过sendMessage到MessageQueue,Looper不断从MessageQueue中取出消息,回调handleMessage方法。

二十七、如果有个100M大的文件,需要上传至服务器中,而服务器form表单最大只能上传2M,可以用什么方法?

  1. 这个问题不是很明确我觉得,首先来说使用http协议上传数据,特别在android下,跟form没什么关系。
  2. 传统的在web中,在form中写文件上传,其实浏览器所做的就是将我们的数据进行解析组拼成字符串,以流的方式发送到服务器,且上传文件用的都是POST方式,POST方式对大小没什么限制。
  3. 回到题目,可以说假设每次真的只能上传2M,那么可能我们只能把文件截断,然后分别上传了。

二十八、内存溢出和内存泄漏有什么区别?何时会产生内存泄漏?内存优化有哪些方法?

  1. 内存溢出通俗理解就是软件(应用)运行需要的内存,超出了它可用的最大内存。
    内存泄漏就是我们对某一内存空间的使用,使用完成后没有释放。

  2. 内存优化:Android中容易内存溢出的部分,就是图片的加载,我们可以使用图片的压缩加上使用LruCache缓存的目的来控制图片所能够使用的内存。

    • 还有对于比较耗资源的对象及时的关闭,例如Database Conn , 各种传感器 , Service 等等。

二十九、AsyncTask使用在哪些场景?它的缺陷是什么?如何解决?

  1. 场景:比如我们需要进行一些耗时的操作,耗时操作完成后更新主线程,或者在操作过程中对主线程的UI进行更新。

  2. 缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程,缓冲队列已满时,如果此时向线程提交任务,将会抛出RejectedExecutionException。

  3. 解决:由一个控制线程来处理AsyncTask的调用判断线程池是否满了,如果满了则线程睡眠否则请求AsyncTask继续处理。

三十、assest文件夹里放文件,对于文件的大小有没有限制?

  1. Android不会为这个目录中的文件生成ID并保存在R类当中,因此它与Android中的一些类和方法兼容度更低。
  2. 同时,由于你需要一个字符串路径来获取这个目录下的文件描述符,访问的速度会更慢。
    • 但是把一些文件放在这个目录下会使一些操作更加方便,比方说拷贝一个数据库文件到系统内存中。
    • 要注意的是,你无法在Android XML文件中引用到assets目录下的文件,只能通过AssetManager来访问这些文件。
    • 数据库文件和游戏数据等放在这个目录下是比较合适的。

另外,网上关于assets和raw的资料都千篇一律了,因此关于这两者中单个文件大小不能超过1M的错误描述也在传播,即如果读取超过1M的文件会报”Data exceeds UNCOMPRESS_DATA_MAX (1314625 vs 1048576)”的IOException,还引申出种种解决方案。

  • 个人认为不应该有这样的限制,为了验证这个说法写了个Demo,发现将近5M的压缩包在assets和raw中都能正常访问。
  • 因此在这里纠正一下,理论上只要打包不超过Android APK 50M大小的限制都是没有问题的。
  • 当然了,不排除是Android很早期的时候因为设备硬件原因aapt在编译的时候对这两个文件夹大小做出了限制,如果是这样,较新版的ADT应该不会出现这种情。

三十一、启动一个程序,可以主界面点击图标进入,也可以从一个程序中跳转过去,二者有什么区别?

是因为启动程序,发现了在这个程序中存在一个设置为<category android:name="android.intent.category.LAUNCHER" />的activity (主界面也是一个app)。

所以这个launcher会把icon提出来,放在主界面上。
当用户点击icon的时候,发出一个Intent:

Intent intent = mActivity.getPackageManager().getLaunchIntentForPackage(packageName);

mActivity.startActivity(intent);   

跳过去可以跳到任意允许的页面,如一个程序可以下载,那么真正下载的页面可能不是首页(也有可能是首页),这时还是构造一个Intent进行startActivity.

  • 这个intent中的action可能有多种view,download都有可能。系统会根据第三方程序向系统注册的功能,为你的Intent选择可以打开的程序或者页面。
  • 所以唯一的一点不同的是从icon的点击启动的intent的action是相对单一的,从程序中跳转或者启动可能样式更多一些。本质是相同的。

三十二、程序之间的亲和性的理解。

  1. 默认情况下一个应用的所有Activity都是具有相同的affinity,都是从application中继承,application的affinity默认就是manifest的包名。
  2. affinity对Activity来说,就像是身份证一样,可以告诉所在的Task,自己属于其中的一员。
  3. 应用场合:
    • 根据affinity重新为Activity选择合适的宿主Task;
    • 与allowTaskReparenting属性配合;
    • 启动Activity使用Intent设置了FLAG_ACTIVITY_NEW_TASK标记。

三十三、同一个程序,但不同的Activity是否可以放在不同的Task任务栈中?

可以放在不同的Task中。

  • 需要为不同的activity设置不同的affinity属性,启动activity的Intent需要包含FLAG_ACTIVITY_NEW_TASK标记。

三十四、横竖屏切换时候Activity的生命周期?

  1. 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  2. 设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  3. 设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

三十五、dvm的进程和Linux的进程, 应用程序的进程是否为同一个概念?

Dvm的进程是dalivk虚拟机进程:

  • 每个android程序都运行在自己的进程里面,
  • 每个android程序系统都会给他分配一个单独的liunx uid(user id),
  • 每个dvm都是linux里面的一个进程.

所以说这两个进程是一个进程。

三十六、

参考文章:
1. Android 面试精华题目总结

你可能感兴趣的:(android,总结,面试,笔记)