面试官:你知道APP的启动流程吗
面试者:知道!
当在launcher上点击一个APP的时候他会调用一个startActivity的方法,并给对应的intent设置一个new_task_flag在新的任务栈中启动activity,由于launcher也是一个APP,所以它可以直接调用launcher的activity的startActivity的方法。activity中的startactivity的方法里面其实会调用instrumentation的execstartactivity方法,instrumentation是Instrumentation类的实例,每个APP都拥有一个instrumentation的实例,然后每个activity都有对这个实例的引用。Instrumentation实际上是进程关键,他可以完成对application或者activity的初始化以及生命周期的工具类。所以我们看当activity调用startactivity方法时,实际会调用instrumentation中的方法execstartactivity方法。
这个方法里呢实际上回调用ActivityManagerNative.getDefault().startActivity方法,这个ActivityManagerNative.getDefault方法返回的是ActivityManagerProxy我们简称AMP,ActivityManagerNative简称AMN。这里实际发生是一次Binder的进程间通信。这个AMP是ActivityManagerService简称AMS在客户端里的一个代理,也就是说当我们调用AMN.getDefault().startActivity方法时,我们是通过AMP调用了AMS中的startActivity方法,AMP只是代理,它不实现AMS的方法,只是通过进程间通信传递参数给AMS,有AMS来实现具体的方法。所以我们可以知道现在的流程已经从launcher的进程进入了system_server的进程里了。
在AMS里,经过一系列调用之后,调用ActivityStack.resumeTopActivityInnerLocked方法,这个方法会找出首个还没有结束的activity的ActivityRecord,如果没有就直接开启launcher程序,如果有则需要使收个activity进入pause状态,因为我们是launcher点击APP的,所以这里会通知launcher的activity进入pause状态。所以这里需要AMS和launcher进程进行通信。使用的方法是thread.schedulePauseActivitity方法,这个方法是applicationThreadProxy的方法,这个applicationThreadProxy是applicationThread在客户端的代理,此时AMS是客户端,launcher是服务端。所以通过进程通信最终调用到了applicationThread的方法,这个方法里会调用queueOrSendMessage方法通知需要暂停activity,这个方法里面其实使用mH来发送一个暂停activity的消息,这个方法里做了三件事:
第一件:当userLeaving为true的时候,则调用performUserLeavingActivity来通知activity,用户要离开了;
第二件:perforPauseActivity来调用instrumentation.callActivityOnPause方法来让activity进入pause状态,这里验证了instrumentation是activity生命周期的工具类。
第三件:当activity进入pause状态之后,通知AMS已经进入pause状态成功,这依然是一个进程间通信,通过AMP完成。
这时候进程的跨越已经从launcher进程又回到了system_server进程。在actvityPaused方法中经过一系列调用之后,会调用ActivityStackSupervisor.startSpecificActivityLocked方法,这个方法会获取ProcessRecord来获取APP的进程信息,如果存在进程信息,则说明APP进程已经存在,这时候直接打开APP即可;如果不存在,则调用startprocesslocked创建新的进程,这里面其实会调用Process.start方法创建进程。由于Zygote进程会通过runSelectLoop方法不断的轮训socket请求,当收到创建进程的请求之后会执行runOnce方法fork一个新子进程,之后会调用handleChildProc方法,这个方法里面会调用RuntimeInit.zygoteInit方法,然后使用反射的方法执行ActivityThread的main方法。
在ActvityThread的main方法中,会先初始化mainLooper,然后实例化一个ActivityThread,实例化的时候会创建好一个Handler类mH,最后让消息机制运行起来,开始接收消息事件。其实在在执行Loop.looper方法之前,会先调用ActivityThread的attach方法,这个方法会调用AMN.getDefault()得到AMP,然后绑定一个ApplicationThread类型的Binder对象,用于AMS对这个新APP进程的通信。当AMS得到一个ApplicationThread类型的对象之后,通过调用thread.scheduleLuncherActivity开始真正的启动一个activity,当ApplicationThread收到这个消息之后,创建一个ActivityRecord对象,并初始化它的成员变量,然后调用ActivityThread的queueOrSendMessage方法,通知launcherActivity,这里使用的就是mH发送消息事件,根据ActivityRecord记录的信息,创建activity。此时进程已经切换到了新APP的进程了。执行performLunchActivity方法,这个方法里
1、搜集activity的信息,主要是packageInfo和component;
2、通过classLoader将activity加载起来
3、创建Application,主要解析Mainifest文件里的信息
4、创建activity的上下文信息,并绑定到activity里面;
5、最后调用instrumentation.callActivityOnCreate方法
终于Activity创建成功了,最后通过handleResumeActivity方法回调Activity的onResume方法,Activity终于能出现在眼前了。
总结:app的启动流程设计知识点很多,调用链很长,面试者可以自己总结一套自己熟悉的答案出来会比较好,不然面试官会感觉你的背答案。另外呢,由于知识很多,可能会被面试打断问其他的问题,所以涉及的一些知识也需要掌握。
比如:
刚才你说到了好几次Binder,你熟悉Binder吗?
为什么进程通信要用Binder,不使用linux其他的进程通信方式呢?(Linux也有很进程通信的方式)
刚才你讲到在和zygote通信的时候是用的socket,这里为什么不选择Binder呢?
mH handler是什么时候创建的,你了解Handler吗?
最后祝大家面试顺利,早日拿到自己心仪的offer
岗位内推、学习交流
我们大量需要前端岗位、Java 岗位、Android 和 iOS 的开发岗位,工作地点:杭州阿里巴巴,由于社招最低要求是 P6 起,所以需要至少 3 年以上工作经验。点击我
如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧!
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
网友评论