Android framework : 对Application的理解

陶炫明
2023-12-01

慕课网 剖析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间的通信过程讲清楚,

 

 类似资料: