详解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 (); ArrayList peers=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进程时,你会发现逻辑都是一样。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。