搞懂Android应用启动过程,再也不怕面试官了

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.java

public 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.java

final 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.java

private final void startProcessLocked(…){

//应用进程的主线程的类名

if (entryPoint == null) entryPoint = “android.app.ActivityThread”;

ProcessStartResult startResult = Process.start(entryPoint, …);

}

//Process.java

public static final ProcessStartResult start(…){

return zygoteProcess.start(…);

}

来到ZygoteProcess。

//ZygoteProcess.java

public final Process.ProcessStartResult start(…){

return startViaZygote(…);

}

private Process.ProcessStartResult startViaZygote(…){

ArrayList argsForZygote = new ArrayList();

//…处理各种参数

return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);

}

其中:

  1. openZygoteSocketIfNeeded打开本地socket

  2. zygoteSendArgsAndGetResult发送请求参数,其中带上了ActivityThread类名

  3. return返回的数据结构ProcessStartResult中会有pid字段

梳理一下:

注意:Zygote进程启动时已经创建好了虚拟机实例,所以由他fork出的应用进程可以直接继承过来用而无需创建。

下面来看Zygote是如何处理socket请求的。

Zygote处理socket请求

从 图解Android系统的启动 一文可知,在ZygoteInit的main函数中,会创建服务端socket。

//ZygoteInit.java

public static void main(String argv[]) {

//Server类,封装了socket

ZygoteServer zygoteServer = new ZygoteServer();

//创建服务端socket,名字为socketName即zygote

zygoteServer.registerServerSocket(socketName);

//进入死循环,等待AMS发请求过来

zygoteServer.runSelectLoop(abiList);

}

看到ZygoteServer。

//ZygoteServer.java

void 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件事:

  1. 启动binder线程池(后面分析)

  2. 读取请求参数拿到ActivityThread类并执行他的main函数,执行thread.attach告知AMS并回传自己的binder句柄

  3. 执行Looper.loop()启动消息循环(代码前面有)

这样应用进程就启动起来了。梳理一下:

下面看下binder线程池是怎么启动的。

启动binder线程池

Zygote的跨进程通信没有使用binder,而是socket,所以应用进程的binder机制不是继承而来,而是进程创建后自己启动的。

前边可知,Zygote收到socket请求后会得到一个ZygoteConnection,他的runOnce会调用handleChildProc。

//ZygoteConnection.java

private void handleChildProc(…){

ZygoteInit.zygoteInit(…);

}

//ZygoteInit.java

public static final void zygoteInit(…){

RuntimeInit.commonInit();

//进入native层

ZygoteInit.nativeZygoteInit();

RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);

}

来到AndroidRuntime.cpp:

//AndroidRuntime.cpp

static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz){

gCurRuntime->onZygoteInit();

}

来到app_main.cpp:

//app_main.cpp

virtual void onZygoteInit() {

//获取单例

sp proc = ProcessState::self();

//在这里启动了binder线程池

proc->startThreadPool();

}

看下ProcessState.cpp:

//ProcessState.cpp

sp 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:KaTeX parse error: Expected group after '_' at position 6: {pid}_̲{自增数字}"

String8 name = makeBinderThreadName();

sp t = new PoolThread(isMain);

//运行binder线程

t->run(name.string());

}

}

ProcessState有两个宏定义值得注意一下:

//ProcessState.cpp

//一次Binder通信最大可以传输的大小是 1MB-4KB*2

#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)

//binder驱动的文件描述符fd被限制了最大线程数15

#define DEFAULT_MAX_BINDER_THREADS 15

我们看下binder线程PoolThread长啥样:

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

//binder驱动的文件描述符fd被限制了最大线程数15

#define DEFAULT_MAX_BINDER_THREADS 15

我们看下binder线程PoolThread长啥样:

分享读者

作者2013年java转到Android开发,在小厂待过,也去过华为,OPPO等大厂待过,18年四月份进了阿里一直到现在。

被人面试过,也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长,而且极易碰到天花板技术停滞不前!

我们整理了一份阿里P7级别的Android架构师全套学习资料,特别适合有3-5年以上经验的小伙伴深入学习提升。

主要包括阿里,以及字节跳动,腾讯,华为,小米,等一线互联网公司主流架构技术。如果你有需要,尽管拿走好了。

[外链图片转存中…(img-tyRQE1GO-1715325790540)]

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值