Android-Framework学习笔记(六)应用程序进程启动过程

runSelectLoop(abiList); //4
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
//通过反射调用SystemServer#main()
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, “Zygote died with exception”, ex);
closeServerSocket();
throw ex;
}
}

注释1处通过registerZygoteSocket函数来创建一个Server端的Socket,这个name为”zygote”的Socket用来等待AMS来请求Zygote来创建新的应用程序进程。
注释2处用来预加载类和资源。
注释3处用来启动SystemServer进程,这样系统的关键服务也会由SystemServer进程启动起来。
注释4处调用runSelectLoop函数来等待AMS的请求。

ZygoteInit#runSelectLoop()

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList fds = new ArrayList();
ArrayList peers = new ArrayList();

fds.add(sServerSocket.getFileDescriptor()); //1
peers.add(null);

while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) { //2
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException(“poll failed”, ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) { //3
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList); //4
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done = peers.get(i).runOnce(); //5
if (done) {
peers.remove(i);
fds.remove(i);
}
}
}
}
}

private static ZygoteConnection acceptCommandPeer(String abiList) {
try {
return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {

}
}

注释1处中的sServerSocket就是我们在registerZygoteSocket函数中创建的服务端Socket,调用sServerSocket.getFileDescriptor()用来获得该Socket的fd字段的值并添加到fd列表fds中。接下来无限循环用来等待AMS请求Zygote进程创建新的应用程序进程。
注释2处通过遍历将fds存储的信息转移到pollFds数组中。
注释3处对pollFds进行遍历。
注释4如果i==0则说明服务端Socket与客户端连接上,也就是当前Zygote进程与AMS建立了连接,则通过acceptCommandPeer函数得到ZygoteConnection类并添加到Socket连接列表peers中,接着将该ZygoteConnection的fd添加到fd列表fds中,以便可以接收到AMS发送过来的请求。
注释5如果i的值大于0,则说明AMS向Zygote进程发送了一个创建应用进程的请求,则调用ZygoteConnection的runOnce函数来创建一个新的应用程序进程。并在成功创建后将这个连接从Socket连接列表peers和fd列表fds中清除。

frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

ZygoteConnection#runOnce()

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList(); //1
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}

try {
parsedArgs = new Arguments(args);//2

//3
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
} catch (ErrnoException ex) {

}
try {
//4
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
return true;
} else {
// in parent…pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}

注释1处调用readArgumentList函数来获取应用程序进程的启动参数。注释2处将readArgumentList函数返回的字符串封装到Arguments对象parsedArgs中。
注释3处调用Zygote的forkAndSpecialize函数来创建应用程序进程,参数为parsedArgs中存储的应用进程启动参数,返回值为pid。
注释4处forkAndSpecialize函数主要是通过fork当前进程来创建一个子进程的,如果pid等于0,则说明是在新创建的子进程中执行的,就会调用handleChildProc函数来启动这个子进程也就是应用程序进程。

ZygoteConnection#handleChildProc()

private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {

//1
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 */);
}
}
}

注释1处由于parsedArgs.invokeWith属性默认为null,最后调用RuntimeInit.zygoteInit函数。

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

RuntimeInit#zygoteInit()

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, “RuntimeInit: Starting application from zygote”);

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “RuntimeInit”);
redirectLogStreams();

commonInit();
nativeZygoteInit(); //1
applicationInit(targetSdkVersion, argv, classLoader); //2
}

注释1处会在新创建的应用程序进程中创建Binder线程池。
注释2处调用了applicationInit函数。

RuntimeInit#applicationInit()

private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {

// 初始化虚拟机环境
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);

final Arguments args;
try {
args = new Arguments(argv);
} catch (IllegalArgumentException ex) {
Slog.e(TAG, ex.getMessage());
// let the process exit
return;
}

// Remaining arguments are passed to the start class’s static main
invokeStaticMain(args.startClass, args.startArgs, classLoader); //1
}

注释1处applicationInit函数中主要调用了invokeStaticMain函数,需要注意的是第一个参数args.startClass,这里指的就是前面赋值的android.app.ActivityThread。

RuntimeInit#invokeStaticMain()

private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader) throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;

try {
cl = Class.forName(className, true, classLoader); //1
} catch (ClassNotFoundException ex) {
throw new RuntimeException("Missing class when invoking static main " + className, ex);
}

Method m;
try {
// 获取main方法
m = cl.getMethod(“main”, new Class[] { String[].class }); //2
} catch (NoSuchMethodException ex) {
throw new RuntimeException("Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException("Problem getting static main on " + className, ex);
}
// 判断修饰符
int modifiers = m.getModifiers(); //3
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException("Main method is not public and static on " + className);
}

/*

  • This throw gets caught in ZygoteInit.main(), which responds
  • by invoking the exception’s run() method. This arrangement
  • clears up all the stack frames that were required in setting
  • up the process.
    */
    throw new ZygoteInit.MethodAndArgsCaller(m, argv); //4
    }

注释1处通过反射来获得android.app.ActivityThread类。
注释2处来获得ActivityThread的main函数。
注释3判断修饰符,必须是static而且必须是public类型。
注释4将找到的main函数传入到MethodAndArgsCaller异常中并抛出该异常。这个异常在ZygoteInit#main()方法中捕获。这么做的作用是清除应用程序进程创建过程的调用栈。

ZygoteInit#main()

public static void main(String argv[]) {
try {

startSystemServer(abiList, socketName);

} catch (MethodAndArgsCaller caller) {
caller.run(); //1
}
}

在注释1处调用了MethodAndArgsCaller的run函数。

MethodAndArgsCaller

public static class MethodAndArgsCaller extends Exception
implements Runnable {
/** method to call */
private final Method mMethod;

/** argument array */
private final String[] mArgs;

public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}

public void run() {
try {
mMethod.invoke(null, new Object[] { mArgs }); //1
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}

注释1处通过反射调用了android.app.ActivityThread#main(String[] args)。至此,Zygote进程fork出ActivityThread进程,并成功调用ActivityThread#main()。

frameworks/base/core/java/android/app/ActivityThread.java

ActivityThread#main()

public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, “ActivityThreadMain”);
SamplingProfilerIntegration.start();

Looper.prepareMainLooper();//1
ActivityThread thread = new ActivityThread();//2
thread.attach(false); //3
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler(); //4
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, “ActivityThread”));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();//5
throw new RuntimeException(“Main thread loop unexpectedly exited”);
}

注释1处在当前应用程序进程中创建消息循环。
注释2处创建ActivityThread实例。
注释4处从ActivityThread获取handler,这样就将ActivityThread关联到Looper和MessageQueue了。
注释5处调用Looper的loop,使得Looper开始工作,开始处理消息。可以看出,系统在应用程序进程启动完成后,就会创建一个消息循环,用来方便的使用Android的异步消息处理机制。
关于Android的异步消息处理机制大家可以参考我这篇文章:Android 异步消息处理机制:Looper、Handler、Message。这里就不多说了。

如果你看过Framework学习(三)SyetemServer进程启动过程
这篇文章,你肯定会发现创建应用程序进程和创建SyetemServer进程的步骤如此类似,其实他们都是通过Zygote进程fork自身来实现的,当然步骤差不多了。唯一区别是一个最终调用到SyetemServer的main函数,另一个最终调用到ActivityThread的main函数。

之前说过:在Android系统中,启动四大组件中的任何一个都可以启动应用程序。但实际上应用程序入口方法只有ActivityThread#main()一个。接着看ActivityThread#main()方法。
注释3处调用了ActivityThread#attach(false)。

ActivityThread#attach()

final ApplicationThread mAppThread = new ApplicationThread();

private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {

final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread); //1
} catch (RemoteException ex) {
// Ignore
}

}

}

前面文章分析过AMS中的Binder机制:ActivityManagerNative.getDefault(),返回的其实是AMS。注释1其实调用的是AMS的attachApplication方法。

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

ActivityManagerService#attachApplication()

public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid); //1
Binder.restoreCallingIdentity(origId);
}
}

注释1调用了两个参数的attachApplicationLocked()方法

ActivityManagerService#attachApplicationLocked()

private final boolean attachApplicationLocked(IApplicationThread thread,int pid) {

if (normalMode) {
try {
if (mStackSupervisor.attachApplicationLocked(app)) { //1
didSomething = true;
}
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}

}

注释1调用ActivityStackSupervisor的attachApplicationLocked方法。

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

ActivityStackSupervisor#attachApplicationLocked()

boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
final String processName = app.processName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (!isFrontStack(stack)) {
continue;
}
ActivityRecord hr = stack.topRunningActivityLocked(null);
if (hr != null) {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
try {
if (realStartActivityLocked(hr, app, true, true)) {
didSomething = true;
}
} catch (RemoteException e) {

}
}
}
}
}

return didSomething;
}

首先遍历所有stack,之后找到目前被置于前台的stack。之后通过topRunningActivityLocked()获取最上面的Activity。最后调用realStartActivityLocked()方法来真正的启动目标Activity。
之后就是应用程序启动过程了,具体请看Framework学习(五)应用程序启动过程这篇文章。

下面看看应用程序进程启动时序图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总体流程

1.AMS通知Process使用Socket和Zygote进程通信,请求创建一个新进程.
2.Zygote收到Socket请求,fork出一个进程,会在新的进程中创建Binder线程池并反射调用ActivityThread#main().
3.ActivityThread通过Binder通知AMS启动应用程序.

推荐阅读:做了六年Android,终于熬出头了,15K到31K全靠这份高级面试题+解析

原文作者:huaxun66

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的14套腾讯、字节跳动、阿里、百度等2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

2020面试真题解析
腾讯面试真题解析

阿里巴巴面试真题解析

字节跳动面试真题解析
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

[外链图片转存中…(img-wH45wOWv-1711912226915)]

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的14套腾讯、字节跳动、阿里、百度等2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

[外链图片转存中…(img-X2JUrq5Y-1711912226916)]
[外链图片转存中…(img-tRwGiUcY-1711912226916)]

[外链图片转存中…(img-E4JLxmoy-1711912226916)]

[外链图片转存中…(img-TeM1Lo9s-1711912226917)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

[外链图片转存中…(img-9L8b3Zyd-1711912226917)]

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值