init进程fork出Zygote进程后,Zygote进程会创建一个做事端socket,等待AMS发起socket要求。
同时,由Zygote进程fork出的SystemServer进程会启动各项系统做事,个中就包含了AMS,AMS会启动Launcher桌面,此时就可以等待用户点击App图标来启动运用进程了。
然后看下系统做事的启动,不管是由init进程启动的独立进程的系统做事如SurfaceFlinger,还是由SystemServer进程启动的非独立进程的系统做事如AMS,都是在ServiceManager进程中完成注册和获取的,在跨进程通信上利用了Android的binder机制。
ServiceManager进程本身也是一个别系做事,经由启动进程、启动binder机制、发布自己和等待要求4个步骤,就可以处理其他系统做事的获取和注册需求了。
AMS发送socket要求Android运用进程的启动是被动式的,在Launcher桌面点击图标启动一个运用的组件如Activity时,如果Activity所在的进程不存在,就会创建并启动进程。
点击App图标后经由层层调用会来到ActivityStackSupervisor的startSpecificActivityLocked方法。
//ActivityStackSupervisor.javafinal ActivityManagerService mService;void startSpecificActivityLocked(...) { //查找Activity所在的进程,ProcessRecord是用来封装进程信息的数据构造 ProcessRecord app = mService.getProcessRecordLocked(...); //如果进程已启动,并且binder句柄IApplicationThread也拿到了,那就直接启动Activity if (app != null && app.thread != null) { realStartActivityLocked(r, app, andResume, checkConfig); return; } //否则,让AMS启动进程 mService.startProcessLocked(...);}
app.thread并不是线程,而是一个binder句柄。运用进程利用AMS须要拿到AMS的句柄IActivityManager,而系统须要关照运用和管理运用的生命周期,以是也须要持有运用进程的binder句柄IApplicationThread。
也便是说,他们相互持有彼此的binder句柄,来实现双向通信。
那IApplicationThread句柄是怎么传给AMS的呢?Zygote进程收到socket要求后会处理要求参数,实行ActivityThread的入口函数main。
//ActivityThread.javapublic static void main(String[] args) { //创建主线程的looper Looper.prepareMainLooper(); //ActivityThread并不是线程,只是普通的java工具 ActivityThread thread = new ActivityThread(); //见告AMS,运用已经启动好了 thread.attach(false); //运行looper,启动循环 Looper.loop();}private void attach(boolean system) { //获取AMS的binder句柄IActivityManager final IActivityManager mgr = ActivityManager.getService(); //见告AMS运用进程已经启动,并传入运用进程自己的binder句柄IApplicationThread mgr.attachApplication(mAppThread);}
以是对付AMS来说:
1.AMS向Zygote发起启动运用的socket要求,Zygote收到要求fork出进程,返回进程的pid给AMS;
2.运用进程启动好后,实行入口main函数,通过attachApplication方法见告AMS已经启动,同时传入运用进程的binder句柄IApplicationThread。
完成这两步,运用进程的启动过程才算完成。
下面看AMS的startProcessLocked启动运用进程时都做了些什么。
//ActivityManagerService.javafinal ProcessRecord startProcessLocked(...){ ProcessRecord app = getProcessRecordLocked(processName, info.uid, keepIfLarge); //如果进程信息不为空,并且已经拿到了Zygote进程返回的运用进程pid //解释AMS已经要求过了,并且Zygote已经相应要求然后fork出进程了 if (app != null && app.pid > 0) { //但是app.thread还是空,解释运用进程还没来得及注书籍身的binder句柄给AMS //即此时进程正在启动,那就直接返回,避免重复创建 if (app.thread == null) { return app; } } //调用重载方法 startProcessLocked(...);}
之以是要判断app.thread,是为了避免当运用进程正在启动的时候,如果又有另一个组件须要启动,导致重复拉起(创建)运用进程。
连续看重载方法startProcessLocked:
//ActivityManagerService.javaprivate final void startProcessLocked(...){ //运用进程的主线程的类名 if (entryPoint == null) entryPoint = "android.app.ActivityThread"; ProcessStartResult startResult = Process.start(entryPoint, ...);}//Process.javapublic static final ProcessStartResult start(...){ return zygoteProcess.start(...);}
来到ZygoteProcess。
//ZygoteProcess.javapublic final Process.ProcessStartResult start(...){ return startViaZygote(...);}private Process.ProcessStartResult startViaZygote(...){ ArrayList<String> argsForZygote = new ArrayList<String>(); //...处理各种参数 return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);}
个中:
openZygoteSocketIfNeeded打开本地socketzygoteSendArgsAndGetResult发送要求参数,个中带上了ActivityThread类名return返回的数据构造ProcessStartResult中会有pid字段梳理一下:
把稳:Zygote进程启动时已经创建好了虚拟机实例,以是由他fork出的运用进程可以直接继续过来用而无需创建。
下面来看Zygote是如何处理socket要求的。
Zygote处理socket要求从 图解Android系统的启动 一文可知,在ZygoteInit的main函数中,会创建做事端socket。
//ZygoteInit.javapublic static void main(String argv[]) { //Server类,封装了socket ZygoteServer zygoteServer = new ZygoteServer(); //创建做事端socket,名字为socketName即zygote zygoteServer.registerServerSocket(socketName); //进入去世循环,等待AMS发要求过来 zygoteServer.runSelectLoop(abiList);}
看到ZygoteServer。
//ZygoteServer.javavoid registerServerSocket(String socketName) { int fileDesc; //socket真正的名字被加了个前缀,即 "ANDROID_SOCKET_" + "zygote" final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName; String env = System.getenv(fullSocketName); fileDesc = Integer.parseInt(env); //创建文件描述符fd FileDescriptor fd = new FileDescriptor(); fd.setInt$(fileDesc); //创建LocalServerSocket工具 mServerSocket = new LocalServerSocket(fd);}void runSelectLoop(String abiList){ //进入去世循环 while (true) { for (int i = pollFds.length - 1; i >= 0; --i) { if (i == 0) { //... } else { //得到一个连接工具ZygoteConnection,调用他的runOnce boolean done = peers.get(i).runOnce(this); } } }}
来到ZygoteConnection的runOnce。
boolean runOnce(ZygoteServer zygoteServer){ //读取socket要求的参数列表 String args[] = readArgumentList(); //创建运用进程 int pid = Zygote.forkAndSpecialize(...); if (pid == 0) { //如果是运用进程(Zygote fork出来的子进程),处理要求参数 handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); return true; } else { return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); }}
handleChildProc方法调用了ZygoteInit的zygoteInit方法,里边紧张做了3件事:
启动binder线程池(后面剖析)读取要求参数拿到ActivityThread类并实行他的main函数,实行thread.attach奉告AMS并回传自己的binder句柄实行Looper.loop()启动循环(代码前面有)这样运用进程就启动起来了。梳理一下:
下面看下binder线程池是怎么启动的。
启动binder线程池Zygote的跨进程通信没有利用binder,而是socket,以是运用进程的binder机制不是继续而来,而是进程创建后自己启动的。
前边可知,Zygote收到socket要求后会得到一个ZygoteConnection,他的runOnce会调用handleChildProc。
//ZygoteConnection.javaprivate void handleChildProc(...){ ZygoteInit.zygoteInit(...);}//ZygoteInit.javapublic static final void zygoteInit(...){ RuntimeInit.commonInit(); //进入native层 ZygoteInit.nativeZygoteInit(); RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);}
来到AndroidRuntime.cpp:
//AndroidRuntime.cppstatic void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv env, jobject clazz){ gCurRuntime->onZygoteInit();}
来到app_main.cpp:
//app_main.cppvirtual void onZygoteInit() { //获取单例 sp<ProcessState> proc = ProcessState::self(); //在这里启动了binder线程池 proc->startThreadPool();}
看下ProcessState.cpp:
//ProcessState.cppsp<ProcessState> ProcessState::self(){ //单例模式,返回ProcessState工具 if (gProcess != NULL) { return gProcess; } gProcess = new ProcessState("/dev/binder"); return gProcess;}//ProcessState布局函数ProcessState::ProcessState(const char driver) : mDriverName(String8(driver)) , mDriverFD(open_driver(driver)) //打开binder驱动 ,//...{ if (mDriverFD >= 0) { //mmap是一种内存映射文件的方法,把mDriverFD映射到当前的内存空间 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); }}//启动了binder线程池void ProcessState::startThreadPool(){ if (!mThreadPoolStarted) { mThreadPoolStarted = true; spawnPooledThread(true); }}void ProcessState::spawnPooledThread(bool isMain){ if (mThreadPoolStarted) { //创建线程名字"Binder:${pid}_${自增数字}" String8 name = makeBinderThreadName(); sp<Thread> t = new PoolThread(isMain); //运行binder线程 t->run(name.string()); }}
ProcessState有两个宏定义值得把稳一下:
//ProcessState.cpp//一次Binder通信最大可以传输的大小是 1MB-4KB2#define BINDER_VM_SIZE ((1 1024 1024) - sysconf(_SC_PAGE_SIZE) 2)//binder驱动的文件描述符fd被限定了最大线程数15#define DEFAULT_MAX_BINDER_THREADS 15
我们看下binder线程PoolThread长啥样:
class PoolThread : public Thread {public: explicit PoolThread(bool isMain) : mIsMain(isMain){}protected: virtual bool threadLoop() { //把binder线程注册进binder驱动程序的线程池中 IPCThreadState::self()->joinThreadPool(mIsMain); return false; } const bool mIsMain;};
来到IPCThreadState.cpp:
//IPCThreadState.cppvoid IPCThreadState::joinThreadPool(bool isMain){ //向binder驱动写数据:进入去世循环 mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); status_t result; do { //进入去世循环,等待指令的到来 result = getAndExecuteCommand(); } while (result != -ECONNREFUSED && result != -EBADF); //向binder驱动写数据:退出去世循环 mOut.writeInt32(BC_EXIT_LOOPER);}status_t IPCThreadState::getAndExecuteCommand(){ //从binder驱动读数据,得到指令 cmd = mIn.readInt32(); //实行指令 result = executeCommand(cmd); return result;}
梳理一下binder的启动过程:
打开binder驱动映射内存,分配缓冲区运行binder线程,进入去世循环,等待指令总结综上,Android运用进程的启动可以总结成以下步骤:
点击Launcher桌面的App图标AMS发起socket要求Zygote进程吸收要求并处理参数Zygote进程fork出运用进程,运用进程继续得到虚拟机实例运用进程启动binder线程池、运行ActivityThread类的main函数、启动Looper循环完全流程图:
口试前的知识梳理,储备提升
自己的知识准备得怎么样,这直接决定了你能否顺利通过一壁和二面,以是在口试前来一个知识梳理,看需不须要提升自己的知识储备是很有必要的。
关于知识梳理,这里再分享一下我口试这段韶光的复习路线:(以下体系的复习资料是我从各路大佬网络整理好的)
架构师筑基必备技能:深入Java泛型+表明深入浅出+并发编程+数据传输与序列化+Java虚拟机事理+反射与类加载+动态代理+高效IOAndroid高等UI与FrameWork源码:高等UI晋升+Framework内核解析+Android组件内核+数据持久化360°全方面性能调优:设计思想与代码质量优化+程序性能优化+开拓效率优化解读开源框架设计思想:热修复设计+插件化框架解读+组件化框架设计+图片加载框架+网络访问框架设计+RXJava相应式编程框架设计+IOC架构设计+Android架构组件JetpackNDK模块开拓:NDK根本知识体系+底层图片处理+音视频开拓微信小程序:小程序先容+UI开拓+API操作+微信对接Hybrid 开拓与Flutter:Html5项目实战+Flutter进阶知识梳理完之后,就须要进行查漏补缺,以是针对这些知识点,我手头上也准备了不少的电子书和条记,这些条记将各个知识点进行了完美的总结。
《507页Android开拓干系源码解析》
《379页Android开拓口试宝典》
3.项目复盘
实际上,口试的一二轮所问到的技能问题,很多都是环绕着你的项目展开,因此在口试前末了要做好的一件事情便是项目复盘。关于项目复盘,我个人的思路如下,可供参考:
你在这个项目中承担了什么样的角色?这个项目的背景是什么,如果是技能项目,为什么要做?有哪些技能难点,是怎么办理的,是否还有更好的方案?你认为项目中是否有可以改进的点?这个项目办理了什么问题,最好用数据说话,这个数据又是怎么得出来的?提前把思路捋一捋,上面这些问题好好思考或准备一下,做到心中有谱往后,自然能够口试官聊得融洽,保持一个好的心态,通过的几率就会更大一些。
以上文章中的资料,均可以免费分享给大家来学习,
资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图;
须要的朋友,直接转发+点赞+私信回答【资料】一键领取!
!
!