详解Android应用main函数的调用
启动App进程
Activity启动过程的一环是调用ActivityStackSupervisor.startSpecificActivityLocked,如果App所在进程还不存在,首先调用AMS的startProcessLocked:
voidstartSpecificActivityLocked(ActivityRecordr,
booleanandResume,booleancheckConfig){
//Isthisactivity'sapplicationalreadyrunning?
ProcessRecordapp=mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid,true);
r.task.stack.setLaunchTime(r);
if(app!=null&&app.thread!=null){
//...
}
mService.startProcessLocked(r.processName,r.info.applicationInfo,true,0,
"activity",r.intent.getComponent(),false,false,true);
}
startProcessLocked函数有多个重载,看最长的那个,最重要是调用了Process.start。
if(entryPoint==null)entryPoint="android.app.ActivityThread"; Process.ProcessStartResultstartResult=Process.start(entryPoint, app.processName,uid,uid,gids,debugFlags,mountExternal, app.info.targetSdkVersion,app.info.seinfo,requiredAbi,instructionSet, app.info.dataDir,entryPointArgs);
第一个参数是android.app.ActivityThread,执行的目标,后文用到再说。
Process.start简单地调用了startViaZygote,封装一些参数,再调用zygoteSendArgsAndGetResult。顾名思义,接下来进程的启动工作交给Zygote。
Zygote
Zygote翻译过来意思是“受精卵”,这也是Zygote的主要工作——孵化进程。概括Zygote的主要工作有以下三点,ZygoteInit的main函数也清晰地体现了。Zygote的启动和其他作用另文分析,这次关注Zygote对Socket的监听。
1.registerZygoteSocket:启动Socket的Server端
2.preload:预加载资源
3.startSystemServer:启动system_server进程
publicstaticvoidmain(Stringargv[]){
//Markzygotestart.Thisensuresthatthreadcreationwillthrow
//anerror.
ZygoteHooks.startZygoteNoThreadCreation();
try{
//...
registerZygoteSocket(socketName);
//...
preload();
//...
if(startSystemServer){
startSystemServer(abiList,socketName);
}
//...
runSelectLoop(abiList);
//...
}catch(MethodAndArgsCallercaller){
caller.run();
}catch(Throwableex){
//...
}
}
最后Zygote执行runSelectLoop,无限循环等待处理进程启动的请求。
privatestaticvoidrunSelectLoop(StringabiList)throwsMethodAndArgsCaller{
ArrayListfds=newArrayList();
ArrayListpeers=newArrayList();
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
while(true){
StructPollfd[]pollFds=newStructPollfd[fds.size()];
for(inti=0;i=0;--i){
if((pollFds[i].revents&POLLIN)==0){
continue;
}
if(i==0){
ZygoteConnectionnewPeer=acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
}else{
booleandone=peers.get(i).runOnce();
if(done){
peers.remove(i);
fds.remove(i);
}
}
}
}
}
与Zygote通信使用的IPC方式是socket,类型是LocalSocket,实质是对Linux的LocalSocket的封装。与记忆中socket绑定需要IP和端口不同,LocalSocket使用FileDescriptor文件描述符,它可以表示文件,也可以表示socket。
runSelectLoop维护了两个列表,分别保存文件描述符FileDescriptor和ZygoteConnection,两者一一对应。index=0的FileDescriptor表示ServerSocket,因此index=0的ZygoteConnection用null填充。
在每次循环中,判断fds里哪个可读:
- 当i=0时,表示有新的client,调用acceptCommandPeer创建ZygoteConnection并保存
- 当i>0时,表示已建立连接的socket中有新的命令,调用runOnce函数执行
fork进程
runOnce函数非常长,我们只关注进程创建部分。
booleanrunOnce()throwsZygoteInit.MethodAndArgsCaller{
//...
try{
//...
pid=Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid,parsedArgs.gids,
parsedArgs.debugFlags,rlimits,parsedArgs.mountExternal,parsedArgs.seInfo,
parsedArgs.niceName,fdsToClose,parsedArgs.instructionSet,
parsedArgs.appDataDir);
}catch(ErrnoExceptionex){
//...
}
try{
if(pid==0){
//inchild
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd=null;
handleChildProc(parsedArgs,descriptors,childPipeFd,newStderr);
//shouldnevergethere,thechildisexpectedtoeither
//throwZygoteInit.MethodAndArgsCallerorexec().
returntrue;
}else{
//inparent...pidof<0meansfailure
IoUtils.closeQuietly(childPipeFd);
childPipeFd=null;
returnhandleParentProc(pid,descriptors,serverPipeFd,parsedArgs);
}
}finally{
//...
}
}
首先调用了forkAndSpecialize函数,创建进程返回一个pid。
publicstaticintforkAndSpecialize(intuid,intgid,int[]gids,intdebugFlags,
int[][]rlimits,intmountExternal,StringseInfo,StringniceName,int[]fdsToClose,
StringinstructionSet,StringappDataDir){
VM_HOOKS.preFork();
intpid=nativeForkAndSpecialize(
uid,gid,gids,debugFlags,rlimits,mountExternal,seInfo,niceName,fdsToClose,
instructionSet,appDataDir);
//Enabletracingassoonaspossibleforthechildprocess.
if(pid==0){
Trace.setTracingEnabled(true);
//NotethatthiseventendsattheendofhandleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"PostFork");
}
VM_HOOKS.postForkCommon();
returnpid;
}
在forkAndSpecialize中核心就是利用JNI调用native的fork函数,调用之前会执行VM_HOOKS.preFork(),调用之后执行VM_HOOKS.postForkCommon()。
VM_HOOKS.preFork()的功能是停止Zygote的4个Daemon子线程的运行,确保Zygote是单线程,提升fork效率。当线程停止之后初始化gc堆。VM_HOOKS.postForkCommon()可以看作是逆操作,关于虚拟机更加深入的内容暂不讨论。
nativeprivatestaticintnativeForkSystemServer(intuid,intgid,int[]gids,intdebugFlags, int[][]rlimits,longpermittedCapabilities,longeffectiveCapabilities);
nativeForkSystemServer是一个native函数,对应com_android_internal_os_Zygote.cpp的com_android_internal_os_Zygote_nativeForkAndSpecialize,继续调用了ForkAndSpecializeCommon,最核心一句则是调用fork函数。
pid_tpid=fork();
简单回忆fork函数作用,它复制当前进程,属性和当前进程相同,使用copyonwrite(写时复制)。执行函数后,新进程已经创建,返回的pid=0;对于被复制的进程,返回新进程的pid;出现错误时,返回-1。
因此,执行forkAndSpecialize函数后,runOnce后续的代码分别在两个进程中执行,判断当前的pid,区分是在当前进程还是新进程。
- pid==0:新进程,调用handleChildProc
- pid!=0:当前进程,调用handleParentProc
handleParentProc函数是当前进程进行清理的过程,比较简单。我们重点来看新进程开展工作的handleChildProc函数。
新进程的初始化
privatevoidhandleChildProc(ArgumentsparsedArgs,
FileDescriptor[]descriptors,FileDescriptorpipeFd,PrintStreamnewStderr)
throwsZygoteInit.MethodAndArgsCaller{
//...
if(parsedArgs.invokeWith!=null){
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName,parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd,parsedArgs.remainingArgs);
}else{
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs,null/*classLoader*/);
}
}
两种启动方式:
- WrapperInit.execApplication
- RuntimeInit.zygoteInit
第一种的目的不太懂,先挂起,后续分析。
第二种RuntimeInit.zygoteInit,继续调用applicationInit,离我们的目标越来越近了,最终调用到invokeStaticMain。
privatestaticvoidinvokeStaticMain(StringclassName,String[]argv,ClassLoaderclassLoader)
throwsZygoteInit.MethodAndArgsCaller{
Class>cl;
try{
cl=Class.forName(className,true,classLoader);
}catch(ClassNotFoundExceptionex){
//...
}
Methodm;
try{
m=cl.getMethod("main",newClass[]{String[].class});
}catch(NoSuchMethodExceptionex){
//...
}
intmodifiers=m.getModifiers();
if(!(Modifier.isStatic(modifiers)&&Modifier.isPublic(modifiers))){
thrownewRuntimeException(
"Mainmethodisnotpublicandstaticon"+className);
}
thrownewZygoteInit.MethodAndArgsCaller(m,argv);
}
在这里使用反射获得需要被执行的类和函数,目标函数当然就是main,目标类来自ActivityManagerService.startProcessLocked里的变量entryPoint,前面有说过。
entryPoint="android.app.ActivityThread"
最后一句执行thrownewZygoteInit.MethodAndArgsCaller(m,argv),让人有些费解。
publicstaticclassMethodAndArgsCallerextendsException
implementsRunnable{
//...
publicvoidrun(){
try{
mMethod.invoke(null,newObject[]{mArgs});
}catch(IllegalAccessExceptionex){
//...
}
}
}
通过上面的分析,容易知道MethodAndArgsCaller就是App的主线程,里面的run方法实现了反射的调用。什么时候触发执行,为什么要这样设计?
既然MethodAndArgsCaller是异常,抛出它肯定某个地方会接收,回顾一路的调用链:
- ZytoteInit.main
- ZytoteInit.runSelectLoop
- ZygoteConnection.runOnce
- ZygoteConnection.handleChildProc
- RuntimeInit.zygoteInit
- RuntimeInit.applicationInit
- RuntimeInit.invokeStaticMain
看前面的ZytoteInit.main函数,catch了MethodAndArgsCaller异常,直接调用了run函数。注释里解释了为什么要这样做:
ThisthrowgetscaughtinZygoteInit.main(),whichrespondsbyinvokingtheexception'srun()method.Thisarrangementclearsupallthestackframesthatwererequiredinsettinguptheprocess.
函数在虚拟机是保存在栈中,每调用一个函数,就将函数相关数据压入栈;执行完函数,将函数从栈中弹出。因此,栈底的就是main函数。
在上面的研究中,新进程创建后,经历一系列函数的调用才到main函数,如果直接调用main函数,调用链中关于初始化的函数会一直存在。为了清理这部分函数,使用了抛出异常的方式,没有捕获异常的函数会马上结束,ZytoteInit.main之上的函数都会结束,达到清理的目的。
最后补充一点,从handleChildProc函数开始,一系列过程调用了ActivityThread的main函数,这不是启动App独有的,后续研究启动SystemServer进程时,你会发现逻辑都是一样。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。