今年经过两次阿里电面和美团外卖的面试,被虐的不要不要的。经过这几年的Android开发,基本上所有该遇到过的情况都遇到了,面试的时候有些却说不上来,究其原因,发现自己没有细细整理过,归纳过。就比如性能优化这一块,一问我还蒙了,没答上来,往下一问,不就是ListView adapter缓存,overdraw,布局嵌套和选择,include,merge,viewstub,一些内存的管理等,实际开发中都已经当做标准了。下面是从网上一些同学分享的以及自己总结补充的,在这里归纳总结一下。
Activity
生命周期
- 完整:onCreate->(onRestart)->onStart->onResume->onPause->onStop->onDestory
- 当前Activity从不可见重新变为可见状态时,onRestart->onsSart->onResume
- 当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause -> onStop,但是如果新Activity采用了透明主题,那么onStop方法不会被回调。当用户再次回到原来的Activity时,回调如下:onRestart -> onStart -> onResume。
- onStart和onStop对应,它们是从Activity是否可见这个角度来回调的;onPause和onResume方法对应,它们是从Activity是否位于前台这个角度来回调的。
- 从Activity A进入到Activity B,回调顺序是onPause(A) -> onCreate(B) -> onStart(B) -> onResume(B) -> onStop(A),所以不能在onPause方法中做重量级的操作。
-
onWindowFocusChanged
- 窗口焦点改变时被调用
- 在onResume之后获得焦点,onPause之后失去焦点
异常情况下生命周期回调->onSaveInstanceState
- 用于保存Activity的状态存储一些临时数据 只会出现在Activity被异常终止
- Activity被覆盖或进入后台,由于系统资源不足被kill会被调用
- 用户改变屏幕方向会被调用
- 它的调用时机是在onStop之前,它和onPause方法没有既定的时序关系,可能在它之前,也可能在它之后。
- 当Activity被重新创建的时候,onRestoreInstanceState会被回调,它的调用时机是onStart之后。
系统只会在Activity即将被销毁并且有机会重新显示的情况下才会去调用onSaveInstanceState方法。 当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前Activity的视图结构,并且在Activity重启后为我们恢复这些数据,比如文本框中用户输入的数据、listview滚动的位置等,这些view相关的状态系统都会默认为我们恢复。具体针对某一个view系统能为我们恢复哪些数据可以查看view的源码中的onSaveInstanceState和onRestoreInstanceState方法。
启动模式
- standard 标准模式(activity默认的): 每次调用startActivity, 都会把activity给创建.
- singleTop 单一顶部模式(栈顶复用): 每次调用startActivity, 需要判断当前的activity是否已经被创建过并且查看任务栈的顶部是否是当前的 activity, 如果是, 调用onNewIntent方法, 如果不是, 就创建一个新的activity实例(常用于MainActivity,清栈回到首页)。
- singleTask 单一任务栈模式(栈内复用): 如果任务栈中已经存在当前activity, 再去调用startActivity, 会调用当前任务栈的onNewIntent方法. 同时 , 会把所有以上的activity都给清除出栈.
- singleInstance 单一实例模式: activity会在一个新的任务栈中实例化, 并且其他的activity不会创建在新的任务栈中. 始终在整个系统中 会被初始化一次.
- 设置启动模式既可以使用xml属性
android:launchMode
,也可以使用代码intent.addFlags()
。
关于栈:
- 当任务栈中没有任何Activity的时候,系统就会回收这个任务栈。
- 从非Activity类型的Context(例如ApplicationContext、Service等)中以standard模式启动新的Activity是不行的,因为这类context并没有任务栈,所以需要为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标志位。
- 任务栈分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换将后台任务栈再次调到前台。
- 参数TaskAffinity用来指定Activity所需要的任务栈,意为任务相关性。默认情况下,所有Activity所需的任务栈的名字为应用的包名。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。
当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中;
Fragment生命周期
onAttach
->onCreate
->onCreateView
->onActivityCreated
->onStart
->onResume
->onPause
->onStop
->onDestroyView
->onDestroy
->onDetach
Handler
Handler 是 Android提供的用于实现线程间通信
- MessageQueue:以单链表的数据结构存储消息列表但是以队列的形式对外提供插入和删除消息操作的消息队列。
- Looper:一个线程可以产生一个Looper对象,由它来处理此线程里中MessageQueue(消息队列)的消息。
- Handler :主要作用是将一个任务切换到某个指定的线程中去执行.
MessageQueue只是消息的存储单元,而Looper则是以无限循环的形式去查找是否有新消息,如果有的话就去处理消息,否则就一直等待着。
为一个线程创建Looper的方法:
new Thread("test"){
@Override
public void run() {
Looper.prepare();//创建looper
Handler handler = new Handler();//可以创建handler了
Looper.loop();//开始looper循环
}
}.start();
在子线程创建一个UI线程的Handler:
new Handler(Looper.getMainLooper());
关于接收的消息的回调: 一般都是通过创建Handler子类重写handleMessage方法来处理消息。
new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
另外可以通过给handler传一个Callback来避免创建Handler子类:
new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
ListView卡顿原因
- Adapter的getView方法里面convertView没有缓存,导致每次都要findViewById
- 在getView方法里面ViewHolder初始化后的赋值或者是多个控件的显示状态和背景的显示没有优化好,抑或是里面含有复杂的计算和耗时操作;
- 在getView方法里面 inflate的row 嵌套太深(布局过于复杂)或者是布局里面有大图片(未开启硬件加速)或者背景所致;
- Adapter多余或者不合理的notifySetDataChanged;
- listview 被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为fill_parent. 如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都是fill_parent的;
- listview 被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为fill_parent. 如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都是fill_parent的;
- 滑动的时候 可以不加载图片
进程优先级
- 前台进程
- 可视进程
- 服务进程
- 后台进程
- 空进程
自定义View
- 对现有控件进行扩展
- 通过组合来实现新的控件
- 重写View来实现全新的控件
onMeasure :测量自身的高度和宽度,ViewGroup还会调用子View的onMesure,可能会调用多次,可以在onSizeChanged回调里面取到最终的宽和高 onLayout:ViewGroup的onLayout调用子View的layout方法,layout再调用onLayout方法,直到View不再是ViewGroup,用于决定View的位置。 dispatchDraw:ViewGroup绘制子View,一般不用重写 onDraw:绘制View
其他整理:
http://d.zhangningning.com.cn/static/files/job_java.pdf
http://d.zhangningning.com.cn/static/files/job_android.pdf
http://android.jobbole.com/82796/
http://android.jobbole.com/82731/
未完待续。。。