远程调用
这里要先明确一个概念,就是所谓的远程调用即跨进程调用,安卓通过Binder来实现,其实现的大致原理,每个支持Binder的进程都有一个Binder线程池,当我们通过Binder远程调用另一个进程的方法时,例如A进程远程调用B进程的print方法,其过程是A进程把要调用的方法的方法描述(方法名,参数,身份确认信息等)装进一个Parcel对象发送到Binder驱动,B进程中空闲的Binder线程会去取消息,当它取到消息后开始在B进程里运行,根据传递过来的方法描述取调用对应的方法,这样两个进程间就完成了一次跨进程的调用,屏蔽掉细节就变成了A进程调用了B进程的print方法,这里要注意的是,A进程往Binder驱动写值后,可以选择阻塞或不阻塞,也就是Binder的双向还是单向模式,若调用模式为单向模式,A进程往Binder驱动写入值后会立即返回,开始执行下一条指令,不在乎B进程是否正确处理了这个消息,而如果是双向,则在A进程写值后会一直等待,等待B进程处理完成print方法,再往Binder驱动回一条消息,A进程接收到这个消息后调用返回,开始执行下一条指令,这个过程就很像在同一个进程中调用方法了,想象若我们在A进程中也有同名方法print,这个方法的作用是往Binder驱动写值并等待调用返回,而B进程收到消息并执行了B进程的print并在Binder驱动中写回值,现在A进程收到写回的消息,print返回,执行下一条指令,其过程就像是在A进程里执行了B进程的print方法一样,足以让使用者忽略掉这其实经过了跨进程的通信,而只是调用了一次普通方法。接下来我们这里所涉及到的AMS与应用之间的调用,全是单向调用,调用后直接返回。
几个重要的类:
ActivityManagerService (简称AMS),运行在system_server 进程,其实质为一个Binder服务端,与之对应的是ActivityManagerProxy(简称AMP),运行在各个应用即客户端,封装了Binder操作,应用使用这个类远程调用AMS
ActivityThread,应用进程java层的根节点,其main方法为应用进程java层的起点,完成应用启动初始化工作,并通过Binder和Handler负责应用与system_server进程间通信的消息转发工作
ApplicationThread,工作在应用进程,ActivityThread的成员变量,负责应用与system_server间的通信,与AMS功能对应,其作为Binder服务端,system_server通过ApplicationThreadProxy远程调用ApplicationThread,从而调用ActivityThread的各个方法,经过ActivityThread的转发,从而控制整个应用
Instrumentation,ActivityThread对各个组件生命周期的调用具体通过这个类实现
以下我们通过Launcher启动一个应用来演示一个应用的启动过程:
startActivity
通过Launcher启动一个带视图的应用,即启动一个Activity,此时这个Activity所需要依附的进程还为启动:
首先我们在Launcher的main线程(即所谓UI线程)里调用startActivity,这个方法最终通过Instrumentation调用ActivityManagerProxy的同名方法,通过AMP远程调用AMS(ActivityManagerService的同名方法),
打印main 线程的调用栈,调用从下到上,Activity的startActivity方法最终调到了AMP的startActivity,作为Binder客户端这个startActivity的实现就是打包各种参数并传到Binder驱动
main:
showStartingWindow
调到这里,因为AMS和AMP是实现了同一个接口的,所以在AMS作为服务端也有同名方法,下面是在system_server进程中,一个被启动来处理这个消息的Binder线程的调用栈:
可以看到Binder在倒数第四行,调用了startActivity,下面三个函数启动的作用就是取出并解析传过来的数据,然后交给对应的方法处理,这里根据传过来的方法id调用到了ActivityManagerService的startActivity方法,再经过层层调用,通知WMS显示startingWindow,所谓startingWindow,就是系统为了应用之间的过渡平滑,应用在启动之前,预先启动起来的预览视图,这是应用的MainActivity的theme里面配置的,这个视图的显示速度很快,因为其不属于应用进程,不需要等待应用初始化,往往在点击应用图标的瞬间就能显示。
schedulePauseActivity
再看这次调用中另一个方法的调用栈:
调到了ApplicationThreadProxy(ATP)的schedulePauseActivity方法,看名字,就知道这又是一个Binder客户端,这个Binder客户端对应的Binder服务端在Launcher的进程里,名字叫ApplicationThread,system_server进程通过这个ATP可以跨进程调用Launcher的方法(每个应用进程都有一个ApplicationThread,可见system_server进程存有所有由它管理的进程的ATP,因为它需要通过这些ATP去管理所有应用进程,如控制应用组件的生命周期),到这里,整个调用链从Launcher进程的main线程,到system_server进程,现在又要回到Launcher,但是要注意,通过Binder再调回去之后,ApplicationThread的schedulepauseActivity方法就是在一个另起的Binder线程里了,main线程会继续执行它后续的指令不受影响,我们现在查看Launcher里面这个另起的Binder线程的调用栈:
这里可以看到,在ApplicationThread的schedulepauseActivity方法里的处理,Binder线程往Handler里发送了一条消息,这个消息将由main线程来处理
activityPaused
此时我们查看主线程收到这个这个message之后的处理:
可以看到收到在handleMessage里,主线程调用了ActivityThread的handlePauseActivity,我们来看一下handlePauseActivity的实现,下面是几行关键代码:
performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity"); ... ... // Tell the activity manager we have paused. if (!dontReport) { try { ActivityManagerNative.getDefault().activityPaused(token); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } |
到这里,整个流程还没有走到一个新的Activity的创建甚至连新的应用进程都还没有创建,Launcher与system_server两个进程之间已经经过了来回各一次跨进程的调用。而到现在对应用开发者能实际感知的到的很重要的一点,就是当前Activity和Fragment走到到了生命周期onPause。应用开发者该在这里做状态保存工作了。
Process.start
回到AMP的activityPaused方法,现在Launcher的主线程又要远程调用AMS,system_server端会把这个调用交个一个空闲Binder线程去处理,我们查看其调用栈:
注意 ActivityStackSupervisor中的startSpecificActivityLocked方法,这个方法在这里会进入分水岭,这里会检查这个Activity需要依附的进程有没有被创建,如果已经创建,则会进入realStartActivityLocked方法,远程调会Launcher进程,实例化要启动的Activity,并依次调用其onCreate,onStart,onResume生命周期方法(当然其中各个方法的调用都是经过各个类层层调用,但如果从应用开发的角度来说就是这个调用顺序,期间不需要再由AMS干预),但对应进程如果不存在,则还需要调用AMS的startProcess创建这个进程,如上调用栈栈顶走到Process类的start方法,注意调用这个方法时传递的一个参数,entryPoint,这里是android.app.ActivityThread
// Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. boolean isActivityProcess = (entryPoint == null); if (entryPoint == null) entryPoint = "android.app.ActivityThread"; Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkTime(startTime, "startProcess: asking zygote to start proc"); Process.ProcessStartResult startResult = Process.start(entryPoint, app.processName, uid, uid, gids, debugFlags, mountExternal, app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, app.info.dataDir, entryPointArgs); checkTime(startTime, "startProcess: returned from zygote!"); |
* Starts a new process via the zygote mechanism. * * @param processClass Class name whose static main() to run * @param niceName 'nice' process name to appear in ps * @param uid a POSIX uid that the new process should setuid() to * @param gid a POSIX gid that the new process shuold setgid() to * @param gids null-ok; a list of supplementary group IDs that the * new process should setgroup() to. * @param debugFlags Additional flags. * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SELinux information for the new process. * @param abi the ABI the process should use. * @param instructionSet null-ok the instruction set to use. * @param appDataDir null-ok the data directory of the app. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason */ |
其实现:
/** * See com.android.internal.os.ZygoteInit.readArgumentList() * Presently the wire format to the zygote process is: * a) a count of arguments (argc, in essence) * b) a number of newline-separated argument strings equal to count * * After the zygote process reads these it will write the pid of * the child or -1 on failure, followed by boolean to * indicate whether a wrapper process was used. */ final BufferedWriter writer = zygoteState.writer; final DataInputStream inputStream = zygoteState.inputStream; writer.write(Integer.toString(args.size())); writer.newLine(); for (int i = 0; i < sz; i++) { String arg = args.get(i); writer.write(arg); writer.newLine(); } writer.flush(); --- --- // Always read the entire result from the input stream to avoid leaving // bytes in the stream for future process starts to accidentally stumble // upon. result.pid = inputStream.readInt(); result.usingWrapper = inputStream.readBoolean(); |
往流里写入值各种参数,再从流里读回返回信息,具体的实现就是zygote进程的事了。
至此,一个应用进程就已经启动,我们进入android.app.ActivityThread的main方法,我们可以当这里是应用的起点。
ActivityThread.main
从 ActivityThread 的main方法开始,应用进入 java 层,应用在main里面的关键代码:
public static void main(String[] args) { ... ... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); ... ... Looper.loop(); } |
1.调用Looper.prepareMainLooper 创建mainLooper并赋值给Loop (这个looper的MessageQueue不能quit)静态变量 sMainLooper ,这样应用可以在任何地方拿到主线程的Looper
2.创建ActivityThread实例,并调用attach,这两步很关键,首先,ActivityThread对象创建时,会创建ResourcesManager的单例对象,还会创建 ApplicationThread 对象作为匿名Binder服务端用以跟system_server进程通信,在thread.attach(false) 中通过AMP把 这个对象注册到AMS:
final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } |
AMS 对各应用组件的生命周期管理就是通过这个对象实现的,AMS会通过这个ApplicationThread 对象远程调用应用进程的方法从而实现管理。
另外在attach 方法里ActivityThread对象会把自己赋值给静态变量sCurrentActivityThread,在应用开发中可以通过反射ActivityThread 直接拿到这个静态变量对象或反射后调用静态方法 currentActivityThread()拿到应用的ActivityThread 对象,从而可以拿到其成员变量mAppThread,mInstrumentation,插件化方案可能需要hook这两个对象。
主线程启动loop,进入循环,等待其他线程往消息队列里发消息
attachApplication
远程调用AMS的attachApplication方法后,会辗转到系统进场中AMS的attachApplicationLocked,通过ProcessRecord.makeActive将这个IApplicationThread 传入与进程相关的ProcessRecord,这个对象与每个用户进程相对应, 这样system_server进程就持有了应用的一个Binder,之后就可以通过这个Binder给应用发消息,从而管理应用了。
bindApplication
之后system_server进程通过thread.bindApplication远程调用应用进程的ApplicationThread的同名方法,同之前的其他调用,应用进程里bindApplication这个方法是运行在某个binder线程的,这里这个Binder线程调用这个方法通过handler即成员变量mH把消息丢给了主线程,主线程收到消息转到ActivityThread的handleBindApplication方法,查看调用栈
system_server:
从attachApplication 调用到 bindApplication,整个调用过程由应用发起,将ApplicationThread传递给AMS(attachApplication这个方法最主要的功能就是把ApplicatioThread传递给了system_server进程,从此应用进程和system_server进程可以互相通信),然后AMS拿到这个ApplicationThread的客户端ATP,调用其bindApplication,这样调用又走回了应用,应用会在bindApplication里面做一些初始化工作,如实例化Instrumentation,实例化Application,并赋值给mInitialApplication = app;这个变量的赋值是唯一的,只会有这一次,也就是一个进程应用进程只会有一个mInitialApplication,但是,这并不意味着一个进程只有一个Application实例,一个进程里是有可能创建多个Application实例的,比如下面要讲的handleLaunchActivity方法,每启动一个Activity,都会去判断这个Activity对应的Application有没有被实例化,若没有,则会创建,并添加到mAllApplications列表,这种情况会在两个应用共用进程时出现。mInitialApplication赋值后,会作为参数启动provider。
bindApplication:
// Allow disk access during application and provider setup. This could // block processing ordered broadcasts, but later processing would // probably end up doing the same disk access. final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); try { // If the app is being launched for full backup or restore, bring it up in // a restricted environment with the base application class. //这里只会实例化Application,而不会调用其onCreate Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; // don't bring up providers in restricted mode; they may depend on the // app's custom Application class if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { //初始化ContentProvider installContentProviders(app, data.providers); // For process that contains content providers, we want to // ensure that the JIT is enabled "at some point". mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); } } // Do this after providers, since instrumentation tests generally start their // test thread at this point, and we don't want that racing. try { mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { throw new RuntimeException( "Exception thrown in onCreate() of " + data.instrumentationName + ": " + e.toString(), e); } try { //调用Application.onCreate mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { if (!mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } } finally { StrictMode.setThreadPolicy(savedPolicy); } |
private void installContentProviders( Context context, List<ProviderInfo> providers) { final ArrayList<IActivityManager.ContentProviderHolder> results = new ArrayList<IActivityManager.ContentProviderHolder>(); for (ProviderInfo cpi : providers) { if (DEBUG_PROVIDER) { StringBuilder buf = new StringBuilder(128); buf.append("Pub "); buf.append(cpi.authority); buf.append(": "); buf.append(cpi.name); Log.i(TAG, buf.toString()); } //这个context就是mInitialApplication,这里会实例化provider //installProvider方法里会为每个provider赋值一个context,这个Context有可能是 //mInitialApplication本身,也可能是根据不同包名重新创建的 //这个方法里还会调用ContentProvider的onCreate生命周期方法 //要注意,直到这里,我们还在ActivityThread的bindApplication方法里,这是AMS对应用进程的第 //一个远程调用,我们虽然已经创建了Application实例,但我们还没有调用其onCreate,还没有任何 //Activity和Service组件被实例化,而在这里,所有ContentProvider已经完成创建,调用 //onCreate并注册到AMS了 IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph != null) { cph.noReleaseNeeded = true; results.add(cph); } } //注册到AMS try { ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } |
bindApplication完成后,到这里应用进程已经先后完成了Application的实例化,ContentProvider的实例化和onCreate生命周期,Application的onCreate生命周期。
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。