Android 應用的真正入口是哪裡?

是 ActivityThread.main 呢 還是 ZygoteInit 呢?

經常會看到文章說 ActivityThread.main 是入口,但是從調用路徑來看:

at android.app.ActivityThread.main(ActivityThread.java:5151)

at java.lang.reflect.Method.invokeNative(Method.java)

at java.lang.reflect.Method.invoke(Method.java:515)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)

at dalvik.system.NativeStart.main(NativeStart.java)

是 ZygoteInit.main 先執行,並且 ZygoteInit 調用了 ActivityThread.main

那麼是不是說 ZygoteInit 是 APP的入口更加合適一些?


先說結論:「入口」是一個太籠統的概念,如果說Android應用進程是如何被啟動的,那毫無疑問是ZygoteInit,但如果說Android應用中主線程的執行入口,則可以說是ActivityThread.main(),不過其實ActivityThread.main()也是在ZygoteInit中被反射調用的。

由於其中涉及的代碼非常多,這裡只羅列出一些重要的點。另外,下面的分析中涉及較多Zygote的知識,對Zygote不了解的童鞋,可以看我的Zygote系列博客:Zygote完全解析(1)

下面以點擊Launcher中某個icon啟動應用為例,系統地分析Android中應用的啟動過程。

首先,Launcher其實也是一個Activity,點擊icon的事件處理如下:

public void onClick(View v) {
...
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));

//here is where to start activity
boolean success = startActivitySafely(v, intent, tag);

if (success v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
FolderIcon fi = (FolderIcon) v;
handleFolderClick(fi);
}
} else if (v == mAllAppsButton) {
...
}
}

startActivitySafely()最終會通過Binder通信調用到AMS中的startActivity(),如下為AMS中對應的方法:

@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
resultWho, requestCode, startFlags, profilerInfo, options,
UserHandle.getCallingUserId());
}

為了啟動App並使其Activity進入resumed狀態,需要先使當前運行的Activity(也就是Launcher)進入Paused狀態,在AMS中最終會調用到以下方法:

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
boolean dontWait) {
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity);
completePauseLocked(false);
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
if (!resuming) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
mStackSupervisor.resumeTopActivitiesLocked();
}
return false;
}

if (mActivityContainer.mParentActivity == null) {
// Top level stack, not a child. Look for child stacks.
mStackSupervisor.pauseChildStacks(prev, userLeaving, uiSleeping, resuming, dontWait);
}

if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
else if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
mResumedActivity = null;
mPausingActivity = prev;
mLastPausedActivity = prev;
mLastNoHistoryActivity = (prev.intent.getFlags() Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
|| (prev.info.flags ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
prev.state = ActivityState.PAUSING;
prev.task.touchActiveTime();
clearLaunchTime(prev);
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
if (mService.mHasRecents (next == null || next.noDisplay || next.task != prev.task)) {
prev.updateThumbnail(screenshotActivities(prev), null);
}
stopFullyDrawnTraceIfNeeded();

mService.updateCpuStats();

if (prev.app != null prev.app.thread != null) {
if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
try {
EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
prev.userId, System.identityHashCode(prev),
prev.shortComponentName);
mService.updateUsageStats(prev, false);
prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
userLeaving, prev.configChangeFlags, dontWait);
} catch (Exception e) {
// Ignore exception, if process died other code will cleanup.
Slog.w(TAG, "Exception thrown during pause", e);
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mLastNoHistoryActivity = null;
}

// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
if (!mService.isSleepingOrShuttingDown()) {
mStackSupervisor.acquireLaunchWakelock();
}

if (mPausingActivity != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we"re pausing the activity just because
// the screen is being turned off and the UI is sleeping, don"t interrupt
// key dispatch; the same activity will pick it up again on wakeup.
if (!uiSleeping) {
prev.pauseKeyDispatchingLocked();
} else {
if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
}

if (dontWait) {
// If the caller said they don"t want to wait for the pause, then complete
// the pause now.
completePauseLocked(false);
return false;

} else {
// Schedule a pause timeout in case the app doesn"t respond.
// We don"t give it much time because this directly impacts the
// responsiveness seen by the user.
Message msg = mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);
msg.obj = prev;
prev.pauseTime = SystemClock.uptimeMillis();
mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
return true;
}

} else {
// This activity failed to schedule the
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
if (!resuming) {
mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
}
return false;
}
}

之後又通過Binder通信調用到ApplicationThread的如下方法:

public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) {
sendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
(userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
configChanges);
}

之後會通過H(Handler的子類)發送消息,處理消息的邏輯在ActivityThread中:

public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">&>&> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, false, (msg.arg11) != 0, msg.arg2,
(msg.arg12) != 0);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
}

執行該方法後,最終會調用Activity的performPause()方法:

inal void performPause() {
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
onPause();
mResumed = false;
if (!mCalled getApplicationInfo().targetSdkVersion &>= android.os.Build.VERSION_CODES.GINGERBREAD) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() + " did not call through to super.onPause()");
}
mResumed = false;
}

這裡可以看到onPause()這個回調。之後在handlePauseActivity()中會調用ActivityManagerNative.getDefault().activityPaused(token);通過Binder機制調用AMS中的activityPaused()方法,如下:

public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
}
Binder.restoreCallingIdentity(origId);
}

之後會有很多的跳轉,最終調用startProcessLocked()方法:

final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}

最終調用的重載方法中有以下代碼,注意這裡傳入了ActivityThread類的路徑:

entryPoint = "android.app.ActivityThread";
checkTime(startTime, "startProcess: asking zygote to start proc");
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);

Process.start()實際上是調用Zygote來fork一個新的進程,並且在最後會調用ActivityThread的main()方法.AMS 所在的進程和 Zygote 所在的進程通過Socket通信,ZygoteInit中循環偵聽Socket連接:

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList& fds = new ArrayList&();
ArrayList& peers = new ArrayList&();
FileDescriptor[] fdArray = new FileDescriptor[4];

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

int loopCount = GC_LOOP_COUNT;
while (true) {
int index;

/*
* Call gc() before we block in select().
* It"s work that has to be done anyway, and it"s better
* to avoid making every child do it. It will also
* madvise() any free memory as a side-effect.
*
* Don"t call it every time, because walking the entire
* heap is a lot of overhead to free a few hundred bytes.
*/
if (loopCount &<= 0) { gc(); loopCount = GC_LOOP_COUNT; } else { loopCount--; } try { fdArray = fds.toArray(fdArray); index = selectReadable(fdArray); } catch (IOException ex) { throw new RuntimeException("Error in select()", ex); } if (index &< 0) { throw new RuntimeException("Error in select()"); } else if (index == 0) { ZygoteConnection newPeer = acceptCommandPeer(abiList); peers.add(newPeer); fds.add(newPeer.getFileDescriptor()); } else { boolean done; done = peers.get(index).runOnce(); if (done) { peers.remove(index); fds.remove(index); } } } }

一旦建立一個ZygoteConnection,就調用runOnce()進行處理,runOnce()中的核心代碼如下:

pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);

...

try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);

// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
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); }

Zygote.forkAndSpecialize()最終會調用到一個naitve方法來fork進程,最後在子進程(即App進程)中執行handleChildProc()方法,代碼如下:

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

/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
* objects still need to be closed properly.
*/

closeSocket();
ZygoteInit.closeServerSocket();

if (descriptors != null) {
try {
ZygoteInit.reopenStdio(descriptors[0],
descriptors[1], descriptors[2]);

for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (IOException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}

if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}

if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
String className;
try {
className = parsedArgs.remainingArgs[0];
} catch (ArrayIndexOutOfBoundsException ex) {
logAndPrintError(newStderr,
"Missing required class name argument", null);
return;
}

String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length);

if (parsedArgs.invokeWith != null) {
WrapperInit.execStandalone(parsedArgs.invokeWith,
parsedArgs.classpath, className, mainArgs);
} else {
ClassLoader cloader;
if (parsedArgs.classpath != null) {
cloader = new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
} else {
cloader = ClassLoader.getSystemClassLoader();
}

try {
ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
} catch (RuntimeException ex) {
logAndPrintError(newStderr, "Error starting.", ex);
}
}
}
}

注意最後面的ZygoteInit.invokeStaticMain()方法,就是在這裡調用ActivityThread的main()方法,而main()方法的代碼如下:

public static void main(String[] args) {
SamplingProfilerIntegration.start();

// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());

Security.addProvider(new AndroidKeyStoreProvider());

// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);

Process.setArgV0("&

");

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

AsyncTask.init();

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}

從這裡開始,準備主線程的消息隊列,並為App配置一個ActivityThread,再通過thread.attach()將ActivityThead,ApplicationThread與AMS關聯起來,注意這裡的ActivityThread才是屬於剛剛啟動的App進程的,之前的ApplicationThread和ActivityThread是屬於Launcher所在的進程的,後續AMS通過Binder通知ActivityThread進行App中主線程的調度工作。

綜上,說ActivityThread.main()是應用的入口也可以,但是不太嚴謹,因為App的啟動是Zygote,AMS,ApplicationThread,ActivityThread等一起配合完成的。


謝邀。

1. 從源碼位置上來講,這個其實蠻明顯的,我們可以看源碼中 ActivityThread的位置:

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

可以看到是位於 app 這個包裡面的。

而ZygoteInit 的源碼位置如下:

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

是放在internal/os下面的。(internal的意思是:內部的;內在的;國內的)

2. 從功能上來講,ZygoteInit 的幾大函數已經很能說明他是幹什麼的了:

private static void registerZygoteSocket(String socketName)

static void preload() // 預載入資源

private static void handleSystemServerProcess()

private static PathClassLoader createSystemServerClassLoader(String systemServerClasspath, int targetSdkVersion)

private static boolean startSystemServer(String abiList, String socketName) 、、啟動SystemServer進程。

簡單來說就是建立Socket通信,預載入資源,啟動SystemServer. 人如其名,就是初始化 Zygote用的。而進程的fork,是Zygote乾的事情。

而ActivityThread 則是 App的管家,個人覺得叫 AppProcessManager 更貼切一些。具體內容可以參考其代碼。

其main函數主要幹了兩件事:

1. 創建 MainLooper.

2. 創建 ActivityThread 對象.

ActivityThread 乾的事情才和具體的 App 有關係,而ZygoteInit 只是做Zygote的初始化工作,儘管一個App的進程是從 Zygote fork 出來的。

總之,多看源碼總是沒錯的。


Android菜鳥來一答。純爪機手打。

個人認為應該是ZygoteInit.我以前也一直認為是ActivityThread,但是看到這個異常很明顯列印的是函數調用棧,從底層調用往上拋的,這讓我想起了linux應用編程中的fork原理,Zygote進程fork時父進程是Zygote,子進程是app進程,父進程會把資源拷貝到子進程中,ZygoteInit應該是這個時候拷貝到了app進程中,而ActivityThread只是UI主線程,駐留在app進程中。

當用戶在Launcher應用中點擊app圖標,Launcher會向AMS發送是否啟動app進程,AMS向Zygote進程請求fork應用進程,Zygote拷貝一系列應用初始化所需要的資源,然後在子進程中運行,然後初始化ActivityThread,創建好了以後通知AMS,並把IAplicationThread傳遞進去作為AMS回調的對象,然後,AMS開始控制Activity的方法調用。


推薦閱讀:

有哪些大齡非 CS 科班出身的青年轉行程序員,結果失敗的例子?
怎樣搭高質量的Android項目框架,框架的結構具體描述?
Android的UI底層是用CPU繪圖的還是GPU繪圖的呢?以及surfaceview,window,普通view是如何實現的?
智能手機感測器里的線性加速度器、陀螺儀、重力感應儀,有什麼區別?

TAG:Android開發 | Android |