慕课网 剖析framework 笔记
3-3 谈谈对Application的理解
考察:
1,是否了解Application作用,刚接触Android开发时都会接触Application
2,是否熟悉Application的类的继承关系,和生命周期
3,是否深入理解Application的初始化原理
从简单的开始
一,Application有什么作用?
Applicaton是一个系统组件,声明周期很长,进程在,它就在,
作用:
1,保存应用进程内的全局变量。但是如果只有这个目的,不如用单例来做,给这些全局变量根据功能划分到不同的单例类里面。
2,AP进程启动后,做一些初始化操作,Applicatoin的创建排在四大组件前面,所以它做init很合适
这两个作用,与Applicaton的特点很有关:生的早,活得长
3,提供应用上下文,
我们调用系统API都需要用context,Application可以提供横跨生命周期的context,而且不怕有内存泄漏,
注意,Application跟着进程走,不跟着AP走,AP开了几个进程,就有几个Application
继承关系:
Application继承自contextWrapper,ContextWrapper是context的包装,
ContextWrapper继承自context,所以可以把Application当做context来用,
class Application externs ContextWrapper implements XXX{
}
public class ContextWrapper extends Context{
//context叫做mBase,有两个地方可以赋值,是典型的静态代理
//Application/ContextWrapper的所有context调用最后都是交给mBase来处理的,
//如果我们通过反射把mBase换掉,那这个application的调用也会换掉。
Context mBase;
public ContextWrapper(Context base){
mBase = base;
}
protected void attachBaseContext(Context base){
mBase = base;
}
}
二,Application的生命周期,
不像Activity的那么复杂,只有启动和结束
启动:构造函数,attachBaseContext, onCreate
结束:只有模拟器有效,手机上无效,不讨论了
1,Application怎么初始化?
看看AP进程启动的入口函数
主要做两件事:准备好主线程的消息循环; 通知AMS自己启动完成(通过thread.attach)
//这里看起来没有和Application init相关的内容,进入attach看看
public static void main(String[] args){
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
//attach主要是AP报告给AMS
private void attach(){
//拿到AcitivityManager的binder对象
final IActivityManager mgr = ActivityManagerNaitve.getDefault();
try{
//向AMS发起binder调用,参数mAppThread是ApplicationTHread类型,也是一个binder对象
mgr.attachApplication(mAppThread);
}catch(RemoteException ex){
//Ignore
}
}
//看看attachApplication,发到AMS,AMS是如何处理的
public final void attachApplication(IApplicationThread thread){
//上锁,因为它跑在binder线程
synchronized(this){
attachApplicationLocked(thread, callingPid);
}
}
//参数是AP端发过来的binder对象
boolean attachApplicationLocked(IApplicationThread thread,...){
....
//IPC调用,会调到AP进程
thread.bindApplication(...);
....
}
//看看AP端怎么处理
//AP端会跑在binder线程
public final void bindApplication(...){
//没有直接干活,先new AppBindData,把所有参数封装到AppBindData里面
AppBindData data = new AppBindData();
....
//封装一个消息丢到AP的主线程处理。
sendMessage(H.BIND_APPLICATION, data);
}
//看看AP主线程如何处理Bind APPLICATION请求
private void handleBinderApplication(AppBindData data){
//获取描述AP安装包信息的对象,对象类型是LoadedApk
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
//创建Application对象
Application app = data.info.makeApplication(...);
//app.onCreate(),调application的onCreate生命周期回调
mInstrumentation.callApplicationOnCreate(app);
}
//看看makeApplication如何创建Applicatoin对象的
public Application makeApplication(..){
if(mApplication != null){
//避免application多次创建
return mApplication;
}
//为Application创建context
//context是一个抽象类,ContextImpl是实现
ContextImpl appContext = ContexImpl.createAppContext(...);
//这里创建了Application对象,会传context进去,
app = mActivityThread.mInsturmentation.newApplication(...);
return app;
}
//再看看如何创建Applicatoin对象
Application
newApplication(ClassLoader cl, String className, Context context){
//用classloader加载Application的类,这个类可能是我们自定义的Application
return newApplication(cl.loadClass(className), context);
}
Static Application newApplication(Class> clazz, Context context){
//然后调用Application类的构造函数,去创造Application对象,通过newInstance
Application app = (Application)clazz.newInstance();
//创建好后,通过attach把context赋给Application
app.attach(context);
return app;
}
final void attach(Context context){
//前面说过Application虽然是context,但是只是空壳,
//里面真正的context是mBase,这里加的就是mBase
attachBaseContext(context);
}
总结流程:
new Application() => application.attachBaseContext() => application.onCreate()
所以要注意,不要在Application的构造函数使用context,因为这时候还没有准备好,
比如在构造函数里面使用getResource()就会挂,因为getResource()会用到context,但是这个时候还没有准备好
问题:不要再Application的生命周期回调里面执行耗时的操作:
//让AP创建Application,AP会让UI thread去创建Application,
//如果Application生命周期里面执行了耗时操作,就阻塞AP的ui thread,
//但是AMS不受影响,因为AMs发起的binder是one way的,one way是指发起调用后立刻返回,不会等待结果
boolean attachApplicationLocked(IApplicationThead thead,...){
...
//让AP创建Application,AP会让UI thread去创建Application,
//如果Application生命周期里面执行了耗时操作,就阻塞AP的ui thread,
//但是AMS不受影响,因为AMs发起的binder是one way的,one way是指发起调用后立刻返回,不会等待结果
thread.binidApplication(...);
...
//处理pending的组件,如StackSupervisor是关于Activity的,mServices就是关于Service的
//Boardcast关于广播的,本来启动这些组件时,AP进程是没有启动的,
//那现在AP进程启动好了,Application也启动好了,就可以去处理这些待启动的组件了
mStackSupervisor.attachApplicationLocked(..);
mService.attachApplcationLocked(app, procesName);
sendPendingBoardcastLocked(app);
...
}
组件的启动最终还是要在AP端执行的,如Application的生命周期就是在AP的ui thread里面调用的,
如果Application里面耗时太久,就会耽误AP自己要启动的组件,而不是AMS
再看另一个问题,Application里面使用静态变量,容易产生的bug:
MainActivity设置name,TestActivity读name,一般没问题,
如果这是把AP切到后台,内存不足可能杀掉了这个进程,
再回来时,Application会恢复TestAcvitiy,但是name是没有初始化的,所以这次testActivity拿到的name是null的,
还有context的东西,后面课程讲,
回到面试题:谈谈对Application的理解
1,作用:3条
2,它的类继承关系和生命周期,生命周期回调顺序
3,它的初始化原理。它是怎么在AP进程启动后init的,这需要把AMS与AP间的通信过程讲清楚,