一、应用进程启动
Android应用程序的载体是APK文件,说白了就是一堆资源文件,SO库,Class文件的一个压缩包。Android进程本质也是Java虚拟机去执行的这一堆文件。应用开发很少接触进程这种概念,都是已组件的形式,Activity代表界面,Service代表后台,ContentProvider代表数据库,BroadCast代表通信,这种组件的形式刻在每个开发同学的婶婶的脑海里,弱化了进程的概念。BUT,进程有main函数的入口,就在ActivityThread类。源码路径/frameworks/base/core/java/android/app/ActivityThread.java
应用进程的组成
对于一个APP进程,有以下核心的成员类需要关注一下。Activity,Service,Provider都是以ArrayMap的形式来保存。BroadcastReceiver生命周期很短,一次性调用,所以不用保存。mInitialApplication是进程唯一的Application对象。Application提供一些应用进程变化的回调接口:
- onCreate() 应用创建。
- onTerminate() 应用销毁时。
- onConfigurationChanged() 系统配置变化时。
- onLowMemory() 在系统内存不足时调用。
- onTrimMemory(int level) 在系统要求应用释放多余内存时调用。
ApplicationThread是个Binder类型的实体对象,AMS可以通过此对象调用APP接口,将BInder调用转化成消息来排队处理,搭配一个Handler类H,所有的消息都在此处理。
另外有两个保存APK信息的对象:mResourcePackages保存只含有资源的APK文件,mPacakge保存包含代码的APK文件。所以更加坐实,APP进程其实就是运行了几个Java类和一些资源文件。
linux进程启动的方式
启动进程有两种方式。调用fork函数启动子进程,pid为0时代表在子进程返回值,否则是父进程。左边启动方式,默认继承父进程的系统资源。右边execve方式传入启动参数,父进程资源全被清空掉。Android系统采用了Linux内核,因此,Android应用进程的创建与Linux如出一辙。 ps: path代表二进制程序的路径,argv代表需要设置的参数,env代表需要设置的环境变量。
Android 应用进程启动的基本流程与原理
在这里要明白两个问题:
1、什么时候才会触发进程启动?
2、进程是谁来启动?怎么启动?
Android系统里面没有接口可以调用去主动启动一个进程,全是被动的启动。启用组件的时候,发现组件发在的进程不存在,才会去启动进程。这些功能都是在Framework中去做的,AMS在此流程中扮演了很重要的的角色。
java
//ActivityStackSupervisor.java
//查找组件所在的进程
ProcessRecord app = getProcessRecordLocked();
if (app != null && app.thread != null) {
//进程已经启动,可以启动组件
realStartActivityLocked(...);
}
startProcessLocked(r.processname);//发起启动进程
这里重点 app.thread != null条件,这是判断IApplicationThread(AMS拿到的应用binder句柄),实现双向的binder调用。
那IApplicationThread句柄是怎么传给AMS的呢?AMS通过配置Zygote启动的参数,最终通过socket写入到Zygote进程,Zygote进程收到socket请求后会处理请求参数,同时对于该进程,也指定了一个类作为执行的入口类,这个类就是ActivityThread。ActivityThread中main方法,作为应用进程的入口,所有的应用进程的初始化都会走这重要的四行代码。
java
//ActivityThread.java
public static void main(String[] args) {
Looper.prepareMainLooper();//创建主线程looper
ActivityThread thread = new ActivityThread();
thread.attach(false);//让AMS持有应用端的binder句柄。
Looper.loop();
}
//获取ActivityManager句柄
final IActivityManager mgr = ActivityManager.getService();
//告诉AMS应用进程已经启动,并传入应用进程自己的binder句柄IApplicationThread
mgr.attachApplication(mAppThread);
因此,对一个应用进程启动完成的标志就是以下两点:
1、AMS向Zygote发起启动进程的请求,zygote启动完进程之后会给AMS返回进程pid。
2、应用启动后,向AMS注册IApplicationThread。
会不会有一种可能,进程已经启动,但是还没有来得及注册Thread,然后另外一个组件去启动,会不会重复启动进程呢?
java
//startProcessLocked
if(app != null && app.pid > 0){
//如果进程信息不为空,并且已经拿到了Zygote进程返回的应用进程pid
//app.pid > 0 说明AMS已经请求过了,并且Zygote已经响应请求然后fork出进程了
if(app.thread ==. null){
//但是app.thread还是空,说明应用进程还没来得及注册自己的binder句柄给AMS
//即此时进程正在启动,那就直接返回,避免重复创建
return app;
}
}
综上,Android应用进程的启动可以总结成以下步骤:
1、AMS判断APP进程是否存在,不存在则发起socket请求。
2、Zygote进程接收请求并处理参数。
3、Zygote进程fork出应用进程,应用进程继承得到虚拟机实例。
4、应用进程启动binder线程池、运行ActivityThread类的main函数、启动Looper循环。
二、应用中启用的binder机制
在何处启用binder机制。
startProcessLocked方法除了判断进程判断,还会负责进程启动的核心逻辑。
前边可知,AMS通过Socket与Zygote发送请求参数,Zygote收到socket请求后会通过ZygoteConnection,核心逻辑在runOnce方法中,会调用handleChildProc。
java
//ZygoteConnection.java部分代码
boolean runOnce(){
String[] args = readArgumentList();
int pid = Zygote.forkAndSpecialize(...)
if(pid == 0){
handleChildProc(...);
return true;
}else {
return handleParentProc(pid,...);
}
}
handleChildProc方法调用了ZygoteInit的zygoteInit方法,里边主要做了3件事:
1、启动binder线程池;
2、读取请求参数拿到ActivityThread类并执行他的main函数,执行thread.attach告知AMS并回传自己的binder句柄;
3、执行Looper.loop启动消息循环;
怎么启用binder机制
1、打开binder驱动。 2、映射内存,分配缓冲区。 3、注册binder线程。 4、进入binder loop 详细内容后续Binder模块会单独讲解。
源码好无聊,有兴趣自己去看吧,说了那么多,面试的时候你知道该怎么吹了吧......