Android應用框架之應用啟動過程詳解
在Android的應用框架中,ActivityManagerService是非常重要的一個組件,盡管名字叫做ActivityManagerService,但通過之前的博客介紹,我們知道,四大組件的創(chuàng)建都是有AMS來完成的,其實不僅是應用程序中的組件,連Android應用程序本身也是AMS負責啟動的。AMS本身運行在一個獨立的進程中,當系統(tǒng)決定要在一個新的進程中啟動一個Activity或者Service時就會先啟動這個進程。而AMS啟動進程的過程是從startProcessLocked啟動的。
1.ActivityManagerService.startProcessLocked
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
......
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
......
try {
int uid = app.info.uid;
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
app.info.packageName);
} catch (PackageManager.NameNotFoundException e) {
......
}
......
int debugFlags = 0;
......
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null);
......
} catch (RuntimeException e) {
......
}
}
......
}
可以看到,函數會調用Process.start函數來創(chuàng)建一個進程,其中第一個參數”android.app.ActivityThread”是需要加載的類,而在完成這個類的加載之后就會運行ActivityThread.main函數。
2.Process.start
public class Process {
......
public static final int start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags,
String[] zygoteArgs)
{
if (supportsProcesses()) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
......
}
} else {
......
return 0;
}
}
......
}
這個函數最后會調用startViaZygote來創(chuàng)建進程,而Zygote正是Android孵化進程的服務,所有的進程都是通過Zygotefork出來的,所以這里創(chuàng)建進程的任務又落到了Zygote頭上了。
3.Process.startViaZygote
public class Process {
......
private static int startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags,
String[] extraArgs)
throws ZygoteStartFailedEx {
int pid;
synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>();
// --runtime-init, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-init");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
argsForZygote.add("--enable-safemode");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) {
argsForZygote.add("--enable-debugger");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
argsForZygote.add("--enable-checkjni");
}
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
//TODO optionally enable debuger
//argsForZygote.add("--enable-debugger");
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
pid = zygoteSendArgsAndGetPid(argsForZygote);
}
}
......
}
函數里面最為重要的工作就是組裝argsForZygote參數,這些參數將告訴Zygote具體的啟動選項,例如”–runtime-init”就表示要為新啟動的運行程序初始化運行庫。然后調用zygoteSendAndGetPid函數進一步操作。
4.Process.zygoteSendAndGetPid
public class Process {
......
private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
throws ZygoteStartFailedEx {
int pid;
openZygoteSocketIfNeeded();
try {
/**
* See com.android.internal.os.ZygoteInit.readArgumentList()
* Presently the wire format to the zygote process is:
* a) a count of arguments (argc, in essence)
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
* the child or -1 on failure.
*/
sZygoteWriter.write(Integer.toString(args.size()));
sZygoteWriter.newLine();
int sz = args.size();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx(
"embedded newlines not allowed");
}
sZygoteWriter.write(arg);
sZygoteWriter.newLine();
}
sZygoteWriter.flush();
// Should there be a timeout on this?
pid = sZygoteInputStream.readInt();
if (pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
} catch (IOException ex) {
......
}
return pid;
}
......
}
這里的sZygoteWriter
是一個Socket寫入流,是由openZygoteSocketIfNeeded函數打開的。而這個Socket由frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中的ZygoteInit類在runSelectLoopMode函數偵聽的。這個類會返回一個ZygoteConnection實例,并執(zhí)行ZygoteConnection的runOnce函數。
5.ZygoteConnection.runOnce
class ZygoteConnection {
......
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
......
return true;
}
......
/** the stderr of the most recent request, if avail */
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(
new FileOutputStream(descriptors[2]));
}
int pid;
try {
parsedArgs = new Arguments(args);
applyUidSecurityPolicy(parsedArgs, peer);
applyDebuggerSecurityPolicy(parsedArgs);
applyRlimitSecurityPolicy(parsedArgs, peer);
applyCapabilitiesSecurityPolicy(parsedArgs, peer);
int[][] rlimits = null;
if (parsedArgs.rlimits != null) {
rlimits = parsedArgs.rlimits.toArray(intArray2d);
}
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, parsedArgs.debugFlags, rlimits);
} catch (IllegalArgumentException ex) {
......
} catch (ZygoteSecurityException ex) {
......
}
if (pid == 0) {
// in child
handleChildProc(parsedArgs, descriptors, newStderr);
// should never happen
return true;
} else { /* pid != 0 */
// in parent...pid of < 0 means failure
return handleParentProc(pid, descriptors, parsedArgs);
}
}
......
}
真正創(chuàng)建進程的代碼在Zygote.forkAndSpecialize,通過Zygote來fork出一個新的進程作為應用進程。fork函數會有兩個返回,其中一個在父進程,一個在子進程,其中自進程的進程號會為0,所以按照上面的代碼,這里會執(zhí)行handleChildProc。
6.ZygoteConnection.handleChildProc
class ZygoteConnection {
......
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
......
if (parsedArgs.runtimeInit) {
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
} else {
......
}
}
......
}
因為在創(chuàng)建的時候傳入了“–runtime-init”,所以這里會運行RuntimeInit.zygoteInit。
public class RuntimeInit {
......
public static final void zygoteInit(String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
// TODO: Doing this here works, but it seems kind of arbitrary. Find
// a better place. The goal is to set it up for applications, but not
// tools like am.
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
commonInit();
zygoteInitNative();
int curArg = 0;
for ( /* curArg */ ; curArg < argv.length; curArg++) {
String arg = argv[curArg];
if (arg.equals("--")) {
curArg++;
break;
} else if (!arg.startsWith("--")) {
break;
} else if (arg.startsWith("--nice-name=")) {
String niceName = arg.substring(arg.indexOf('=') + 1);
Process.setArgV0(niceName);
}
}
if (curArg == argv.length) {
Slog.e(TAG, "Missing classname argument to RuntimeInit!");
// let the process exit
return;
}
// Remaining arguments are passed to the start class's static main
String startClass = argv[curArg++];
String[] startArgs = new String[argv.length - curArg];
System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
invokeStaticMain(startClass, startArgs);
}
......
}
這里有兩個關鍵的函數調用,一個是zygoteInitNative函數調用,一個是invokeStaticMain函數調用,前者就是執(zhí)行Binder驅動程序初始化的相關工作了,正是由于執(zhí)行了這個工作,才使得進程中的Binder對象能夠順利地進行Binder進程間通信,而后一個函數調用,就是執(zhí)行進程的入口函數,這里就是執(zhí)行startClass類的main函數了,而這個startClass即是我們在Step 1中傳進來的”android.app.ActivityThread”值,表示要執(zhí)行android.app.ActivityThread類的main函數。
7. Zygote.invokeStaticMain
public class ZygoteInit {
......
static void invokeStaticMain(ClassLoader loader,
String className, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
cl = loader.loadClass(className);
} catch (ClassNotFoundException ex) {
......
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
......
} catch (SecurityException ex) {
......
}
int modifiers = m.getModifiers();
......
/*
* 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);
}
......
}
從代碼中可以看到,通過ClassLoader加載對應的android.app.ActivityThread類,然后再獲取到對應的main函數句柄,最后調用該類的main函數。不過這里的調用方式比較有意思,不知直接調用,而是通過拋出一個異常。這樣做的方式是為了清空堆棧,讓系統(tǒng)認為新進程是從ActivityThread的main函數開始的。
8.ActivityThread.main
public final class ActivityThread {
......
public static final void main(String[] args) {
SamplingProfilerIntegration.start();
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = new Handler();
}
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
if (Process.supportsProcesses()) {
throw new RuntimeException("Main thread loop unexpectedly exited");
}
thread.detach();
String name = (thread.mInitialApplication != null)
? thread.mInitialApplication.getPackageName()
: "<unknown>";
Slog.i(TAG, "Main thread of " + name + " is now exiting");
}
......
}
從這里我們可以看出,這個函數首先會在進程中創(chuàng)建一個ActivityThread對象,然后進入消息循環(huán)中,這樣,我們以后就可以在這個進程中啟動Activity或者Service了。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- 分析Android中應用的啟動流程
- Android 啟動activity的4種方式及打開其他應用的activity的坑
- Android應用啟動另外一個apk應用的方法
- Android優(yōu)化應用啟動速度
- Android使用Intent啟動其他非系統(tǒng)應用程序的方法
- android應用實現(xiàn)開機自動啟動方法
- 解析android創(chuàng)建快捷方式會啟動兩個應用的問題
- 解析Android應用啟動后自動創(chuàng)建桌面快捷方式的實現(xiàn)方法
- Android筆記之:App應用之啟動界面SplashActivity的使用
- Android Intent啟動別的應用實現(xiàn)方法
相關文章
Adnroid 自定義ProgressDialog加載中(加載圈)
這篇文章主要介紹了Adnroid 自定義ProgressDialog加載中(加載圈),需要的朋友可以參考下2017-06-06
Android獲取系統(tǒng)儲存以及內存信息的方法(一)
這篇文章主要為大家詳細介紹了Android獲取系統(tǒng)儲存以及內存信息的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10
android 使用 IJKPlayer 播放視頻流的實現(xiàn)代碼
這篇文章主要介紹了android 使用 IJKPlayer 播放視頻流,這需要借助 IAndroidIO 這個接口,也可以用于播放本地文件,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11
Rxjava+Retrofit+Okhttp進行網絡訪問及數據解析
這篇文章主要介紹了Rxjava+Retrofit+Okhttp進行網絡訪問及數據解析,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下2022-08-08

