Android 應(yīng)用進(jìn)程啟動(dòng)流程

本文是 Android 系統(tǒng)學(xué)習(xí)系列文章中的第一章節(jié)第二小節(jié)內(nèi)容炸卑,從源碼出發(fā)說明了 Android 應(yīng)用進(jìn)程是如何啟動(dòng)的亡蓉,經(jīng)過哪些進(jìn)程的通力合作,它們是如何是設(shè)計(jì)的瞳浦。担映。對(duì)此系列感興趣的同學(xué),可以收藏這個(gè)鏈接 Android 系統(tǒng)學(xué)習(xí)叫潦,也可以使用 RSS 進(jìn)行訂閱蝇完。


閱讀的收益

討論的內(nèi)容也就是一個(gè)應(yīng)用進(jìn)程是如何啟動(dòng)的,私以為這一部分的內(nèi)容頗為重要,即便不了解細(xì)節(jié)短蜕,也要知道其中的大體步驟氢架。特別是針對(duì)我們應(yīng)用開發(fā)者而言,理應(yīng)了解我們的 App 是如何被啟動(dòng)的朋魔,App 中的組件是如何被系統(tǒng)服務(wù)調(diào)用和組織的岖研。

講應(yīng)用進(jìn)程啟動(dòng)的文章不是很多,也都沒有說到點(diǎn)上铺厨,大抵都是對(duì)源碼的堆疊缎玫,沒有個(gè)人的理解在里面。如果非要看調(diào)用棧的話解滓,在合適的地方掛上斷點(diǎn),或者通過輸出異常棧的方式都可以看到筝家,如下圖所示洼裤。老羅的文章 Android應(yīng)用程序啟動(dòng)過程源代碼分析 事無巨細(xì),但感覺還是沒有說到點(diǎn)子上溪王,因而這篇文章只做到拋磚引玉的作用腮鞍,希望有更好的文章出現(xiàn)。細(xì)節(jié)是復(fù)雜的莹菱,原理是簡(jiǎn)單的移国,這里盡可能地從原理角度出發(fā)進(jìn)行說明,加深大家的理解道伟。

對(duì)這篇文章閱讀后迹缀,你能了解從用戶點(diǎn)擊 Launcher 上的 App 圖標(biāo),到顯示出 App 界面時(shí)主要發(fā)生的事情蜜徽,而通過對(duì)這個(gè)一過程的了解祝懂,將知曉以下知識(shí)點(diǎn)。

  • Android Process 的創(chuàng)建過程拘鞋,以及 Activity Manager Service 是如何參與這個(gè)步驟砚蓬,以及在其中扮演的角色?
  • Android 中所謂的主線程是怎么回事盆色?主線程是誰灰蛙?又如何被創(chuàng)建的。
  • Android 系統(tǒng)是如何節(jié)省進(jìn)程創(chuàng)建開銷的隔躲?

應(yīng)用進(jìn)程簡(jiǎn)介

在 Android 中每一個(gè)應(yīng)用程序都被設(shè)計(jì)為單獨(dú)的進(jìn)程摩梧,應(yīng)用程序也可以根據(jù)自己的需要去決定是否需要啟用多個(gè)進(jìn)程,不過總而言之都與其他應(yīng)用程序和系統(tǒng)服務(wù)是相互獨(dú)立的蹭越。從解耦和系統(tǒng)穩(wěn)定性的角度上看都應(yīng)該運(yùn)行在不同的進(jìn)程上障本,畢竟不能因?yàn)閼?yīng)用程序的崩潰就影響到其他應(yīng)用進(jìn)程或者系統(tǒng)服務(wù)進(jìn)程。

應(yīng)用進(jìn)程不同于其他 Android 系統(tǒng)中的守護(hù)進(jìn)程,當(dāng)內(nèi)存不夠的時(shí)候驾霜,某些應(yīng)用進(jìn)程可能會(huì)被系統(tǒng)回收掉案训,因而應(yīng)用進(jìn)程也是有其生命周期的,更多信息參考 Android Developer 官網(wǎng)對(duì)于 Process 的教程 粪糙。Android 應(yīng)用組件不一定要運(yùn)行在單獨(dú)的進(jìn)程上强霎,也可以運(yùn)行在多個(gè)進(jìn)程上,通過對(duì) Android 組件指定運(yùn)行的進(jìn)程 android:process蓉冈,即可讓其運(yùn)行在其他線程上城舞。

每個(gè)應(yīng)用進(jìn)程都相當(dāng)于一個(gè) Sandbox 沙箱,Android 通過對(duì)每一個(gè)應(yīng)用分配一個(gè) UID寞酿,注意這里的 UID 不同于 Linux 系統(tǒng)的 User ID家夺,可以將每個(gè)應(yīng)用理解為一個(gè) User
,只能對(duì)其目錄下的內(nèi)容具有訪問和讀寫權(quán)限伐弹,這樣就從根源上保護(hù)了其他應(yīng)用程序拉馋,下圖說明了其隔離效果。

App Process Isolate

Zygote 進(jìn)程

Zygote 的中文意思是受精卵惨好,從這個(gè)意思里也可以看出 Zygote 進(jìn)程是用來分裂復(fù)制(fork)的煌茴,實(shí)際上所有的 App 進(jìn)程都是通過對(duì) Zygote 進(jìn)程的 Fork 得來的。當(dāng) app_process 啟動(dòng) Zygote 時(shí)日川,Zygote 會(huì)在其啟動(dòng)后蔓腐,預(yù)加載必要的 Java Classes(相關(guān)列表查看 預(yù)加載文件) 和 Resources,并啟動(dòng) System Server 龄句,并打開 /dev/socket/zygote socket 去監(jiān)聽啟動(dòng)應(yīng)用程序的請(qǐng)求回论,日后。在下面的代碼中撒璧,顯示了 Zygote 進(jìn)程如何啟動(dòng)透葛,和加載 System Server 的。

zygote 受精卵

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
public static void main(String argv[]) {
  // ...
  registerZygoteSocket(socketName); // 開啟 Zygote socket.
  Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload");
  EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
      SystemClock.uptimeMillis());
  preload(); // 預(yù)加載資源
  EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
      SystemClock.uptimeMillis());
  Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
  // Finish profiling the zygote initialization.
  SamplingProfilerIntegration.writeZygoteSnapshot();
  // Do an initial gc to clean up after startup
  Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC");
  gcAndFinalize(); // 觸發(fā) GC
  if (startSystemServer) { // 啟動(dòng) System Server.
      startSystemServer(abiList, socketName);
  }
  // ...
}
Fork Zygote 交互圖

那么為何要做這種設(shè)計(jì)呢卿樱?每個(gè)應(yīng)用程序的運(yùn)行僚害,都需要依托于相應(yīng)的運(yùn)行環(huán)境,而這個(gè)就是 Davlik (ART) 虛擬機(jī)繁调,但每次啟動(dòng)的開銷較大萨蚕,而通過對(duì) Zygote 進(jìn)程的 Fork,能夠提升不小的效率蹄胰。并且在這個(gè)工程中岳遥,采用了 Copy-on-Write 的方式,極大程度上地復(fù)用了 Zygote 上面的資源裕寨。更多信息也可以參考我這篇博文 詳解 Android 是如何啟動(dòng)的浩蓉。


App 應(yīng)用進(jìn)程啟動(dòng)

接下來的內(nèi)容涉及到很多 Binder 通信相關(guān)的東西派继,因此在閱讀本文前,建議查閱下 Binder 相關(guān)的文章捻艳,這里有下列文章供查考驾窟。

ActivityManager 架構(gòu)

在我們編程過程中,涉及到許多 Activity 跳轉(zhuǎn)的事情认轨,在 Launcher 中點(diǎn)擊 Icon 進(jìn)行跳轉(zhuǎn)也是同樣的道理绅络,調(diào)用 context.startActivity(intent) 方法。Launcher 出于一個(gè)線程嘁字,而啟動(dòng)的 App 則運(yùn)行在另一個(gè)進(jìn)程中恩急,在這其中勢(shì)必牽涉到跨進(jìn)程 (IPC) 調(diào)用,這樣復(fù)雜的過程顯然需要一種中介者纪蜒,或者一個(gè)系統(tǒng)來進(jìn)行中轉(zhuǎn)和管理衷恭,而這個(gè)服務(wù)就是 ActivityManagerService

ActivityManagerService 作為一個(gè)守護(hù)進(jìn)程運(yùn)行在 Android Framework 中霍掺,如果讓開發(fā)者直接接觸這個(gè)類的話匾荆,就需要開發(fā)者自行處理 IPC 調(diào)用的問題,且這有不利于 Android 系統(tǒng)進(jìn)行安全校驗(yàn)等工作杆烁。因而 Android 系統(tǒng)實(shí)現(xiàn)了 ActivityManager,通過這個(gè) ActivityManager 作為一個(gè)入口简卧,變相地和 ActivityManagerService 打交道兔魂。這種模式在 Android 系統(tǒng)中極為常見,類似的還有 WifiManager, LocationManager举娩,WindowsManager 等等析校。而這些 Manger 在背后調(diào)用的東西就是前面提及的 Binder 機(jī)制。下面以 ActivityManger 為例看看其背后的運(yùn)作方式铜涉。

Binder 體系架構(gòu)可以分為 Client 和 Server 兩端智玻,為了更方便 Client 的調(diào)用,這次采用了 AIDL 的方式芙代,具體參考鏈接 Android Binder 完全解析(三)AIDL實(shí)現(xiàn)原理分析 吊奢。這里再用類比的方式來說明,方便大家理解纹烹。歷史上有不少垂簾聽政的故事页滚,背后操作的人實(shí)際是通過控制傀儡來控制朝政,通過給傀儡皇帝傳遞命令铺呵,傀儡皇帝只是復(fù)述命令裹驰,起到傳遞的作用。更有甚者片挂,不想去上朝的控權(quán)者幻林,會(huì)通過手下的太監(jiān)或者婢女贞盯,轉(zhuǎn)述給傀儡皇上。這種模式被我們稱為代理模式沪饺,ActivityManger 所使用的就是這種模式躏敢。

首先這里要針對(duì)要執(zhí)行的命令進(jìn)行抽象,這樣掌權(quán)者随闽、太監(jiān)父丰、皇上和朝政才能聽懂。IActivityManager 就是對(duì)這個(gè)進(jìn)行的抽象掘宪,點(diǎn)擊查看 源碼蛾扇,這幾種就包括常見的 startActivity, showWaitingForDebugger, finishActivity 等等。ActivityManagerProxy 就相當(dāng)于其他的太監(jiān)或者婢女魏滚,Proxy 不需要懂具體的業(yè)務(wù)镀首,只需要把指令傳遞過去就行。ActivityManagerService 就是具體的執(zhí)行者鼠次,就是大臣們更哄。ActivityManger 則就是具體的業(yè)務(wù)邏輯的外觀類(參加GOF的設(shè)計(jì)模式),也就是具體的掌權(quán)者們腥寇。它們的關(guān)系如下圖所示:

ActivityManager Service

源碼分析從 Launcher 到 ActivityManager 啟動(dòng)步驟

接下來分析下成翩,在源碼里是具體操作的,也驗(yàn)證我們前面的說法赦役。

(1) 點(diǎn)擊 Launcher 的圖標(biāo)麻敌,會(huì)調(diào)用到 Activity 的 startActivity 方法。在繼續(xù)往下看過去掂摔,這個(gè)里面會(huì)調(diào)用到 startActivityForResult 方法术羔,在 startActivityForResult 方法中,疏通同歸乙漓,最后會(huì)調(diào)用 mInstrumentation.execStartActivity 方法级历。

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
    if (mParent == null) {
        // ### ...
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
    } else {
        // ### ...
    }
}

(2) Instrumentation 執(zhí)行 execStartActivity 方法。參數(shù)里面中的 contextThread 和 token 對(duì)象都是 IBinder 類型叭披,而 Binder 可以在跨進(jìn)程調(diào)用中依舊充當(dāng) Token 的角色寥殖,在多進(jìn)程中由 Binder Driver 保證依然可以是唯一的。

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    // monitor ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess();
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
    }
    return null;
}

(3) ActivityManagerNative.getDefault().startActivity趋观。getDefault 中實(shí)際返回的就是 Proxy 對(duì)象扛禽,在實(shí)際中只起到代理的重要,并不進(jìn)行邏輯處理皱坛。

先看看 ActivityManagerNative.getDefault() 中的實(shí)現(xiàn)编曼。

/**
 * Retrieve the system's default/global activity manager.
 */
static public IActivityManager getDefault() {
    return gDefault.get();
}

IActivityManager 根據(jù)前文的描述即是對(duì)于可操作接口的抽象,Singleton 則是對(duì)單例對(duì)象的封裝剩辟。也就是說 gDefault 返回了實(shí)現(xiàn) IActivityManager 的單例掐场。

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        IBinder b = ServiceManager.getService("activity");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};

/**
 * Cast a Binder object into an activity manager interface, generating
 * a proxy if needed.
 */
static public IActivityManager asInterface(IBinder obj) {
    if (obj == null) {
        return null;
    }
    IActivityManager in =
        (IActivityManager)obj.queryLocalInterface(descriptor);
    if (in != null) {
        return in;
    }

    return new ActivityManagerProxy(obj);
}

在 asInterface 中返回了 ActivityManagerProxy, 這就是前文提及的 太監(jiān)和婢女 角色往扔,我們?cè)倏纯?ActivityManagerProxy 內(nèi)部是如何工作的。

(4) ActivityManagerProxy 的實(shí)現(xiàn)熊户。從源碼里面可以看出萍膛,ActivityManagerProxy 將遠(yuǎn)程 Binder 作為構(gòu)造函數(shù)的參數(shù),而在 startActivity 方法中嚷堡,通過遠(yuǎn)程 Binder 對(duì)象的 transact 方法蝗罗,將參數(shù)寫入到 data 中,在遠(yuǎn)程執(zhí)行完畢后蝌戒,結(jié)果寫入到 reply 里串塑。這里實(shí)實(shí)在在地起到了 Proxy 的作用,只負(fù)責(zé)數(shù)據(jù)的傳輸北苟。重點(diǎn)在下面這行代碼:

mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
class ActivityManagerProxy implements IActivityManager {

  public ActivityManagerProxy(IBinder remote) {
        mRemote = remote;
  }

  public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
              String resolvedType, IBinder resultTo, String resultWho, int requestCode,
              int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
          Parcel data = Parcel.obtain();
          Parcel reply = Parcel.obtain();
          data.writeInterfaceToken(IActivityManager.descriptor);
          data.writeStrongBinder(caller != null ? caller.asBinder() : null);
          data.writeString(callingPackage);
          intent.writeToParcel(data, 0);
          data.writeString(resolvedType);
          data.writeStrongBinder(resultTo);
          data.writeString(resultWho);
          data.writeInt(requestCode);
          data.writeInt(startFlags);
          if (profilerInfo != null) {
              data.writeInt(1);
              profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
          } else {
              data.writeInt(0);
          }
          if (options != null) {
              data.writeInt(1);
              options.writeToParcel(data, 0);
          } else {
              data.writeInt(0);
          }
          mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
          reply.readException();
          int result = reply.readInt();
          reply.recycle();
          data.recycle();
          return result;
    }

    // other methods.

}

(5) ActivityManagerService 的調(diào)用桩匪。

在上部分提及的 ActivityManagerProxy 中在構(gòu)造函數(shù)里傳入的 mRemote 遠(yuǎn)程Binder 是什么了?答案就在前面提及的 gDefault 里面友鼻。

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        IBinder b = ServiceManager.getService("activity");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};
IBinder b = ServiceManager.getService("activity");

上面這段代碼返回的即是 ActivityManagerService傻昙。所有的系統(tǒng)服務(wù)都是 IBinder 對(duì)象,即他們必須支持遠(yuǎn)程調(diào)用彩扔。而每個(gè)系統(tǒng)服務(wù)都會(huì)通過在 ServiceManager 注冊(cè)別名的方式妆档,告知 ServiceManager 通過相應(yīng)的別名即可訪問到我。而 activity 正是 ActivityManagerService 的別名虫碉。

從 ActivityManagerService 到 進(jìn)程啟動(dòng)

ActivityManagerService 在接受到相應(yīng)的 Intent 請(qǐng)求后(Activity过吻、Broadcast、Service蔗衡、ContentProvider),會(huì)查看是否需要進(jìn)行新建進(jìn)程的工作乳绕,這里以 Activity 為例绞惦,其他組件的步驟與此原理相同,就不再贅述洋措。

(1) ActivityManagerService 在啟動(dòng) Activity 之前济蝉,首先通過 resolveIntent 方法,來得到相應(yīng)的 ResolveInfo菠发,其后通過調(diào)用 startActivityLocked 往下啟動(dòng) Activity王滤。

try {
    ResolveInfo rInfo =
        AppGlobals.getPackageManager().resolveIntent(
                intent, null,
                PackageManager.MATCH_DEFAULT_ONLY
                | ActivityManagerService.STOCK_PM_FLAGS, userId);
    aInfo = rInfo != null ? rInfo.activityInfo : null;
    aInfo = mService.getActivityInfoForUser(aInfo, userId);
} catch (RemoteException e) {
    aInfo = null;
}

int res = startActivityLocked(caller, intent, resolvedType, aInfo,
        voiceSession, voiceInteractor, resultTo, resultWho,
        requestCode, callingPid, callingUid, callingPackage,
        realCallingPid, realCallingUid, startFlags, options,
        componentSpecified, null, container, inTask);

(2) startSpecificActivityLocked 方法,判斷是否需要新建進(jìn)程滓鸠。從代碼中看出雁乡,這里對(duì) ProcessRecord 進(jìn)行了判斷,ProcessRecord 就是響應(yīng)的進(jìn)程記錄糜俗,如果存在相應(yīng)的進(jìn)程踱稍,就啟動(dòng)相應(yīng)的 Activity, 否則將創(chuàng)建進(jìn)程曲饱。mService.startProcessLocked 這個(gè)方法實(shí)現(xiàn)了開啟進(jìn)程,下面再看看里面的實(shí)現(xiàn)珠月。

void startSpecificActivityLocked(ActivityRecord r,
        boolean andResume, boolean checkConfig) {
    // Is this activity's application already running?
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,
            r.info.applicationInfo.uid, true);

    r.task.stack.setLaunchTime(r);

    if (app != null && app.thread != null) {
        try {
            // ignore some code...
            realStartActivityLocked(r, app, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }

        // If a dead object exception was thrown -- fall through to
        // restart the application.
    }

    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
            "activity", r.intent.getComponent(), false, false, true);
}

(3) startProcessLocked 在內(nèi)部調(diào)用了 Process.start 方法扩淀,并且指定了 android.app.ActivityThread 作為進(jìn)程的入口,進(jìn)程啟動(dòng)后啤挎,將調(diào)用 android.app.ActivityThread 的 main 方法驻谆。

private final void startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
        // ...
        // Start the process.  It will either succeed and return a result containing
        // the PID of the new process, or else throw a RuntimeException.
        boolean isActivityProcess = (entryPoint == null);
        if (entryPoint == null) 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);
        // ...
}

(4) 在 Process.start 方法中,實(shí)際調(diào)用的是 startViaZygote 方法庆聘,在這個(gè)方法里通過 openZygoteSocketIfNeeded 打開 Zygote 的 socket胜臊,并通過 zygoteSendArgsAndGetResult 進(jìn)行交互。

根據(jù)前文提及的內(nèi)容掏觉,zygote 開啟了一個(gè) socket 監(jiān)聽功能区端,監(jiān)聽需要?jiǎng)?chuàng)建 Process 的請(qǐng)求,因而在這里澳腹,我們?nèi)ゲ榭?Zygote 的代碼织盼,看其是怎么監(jiān)聽請(qǐng)求的。

return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);

(5) ZygoteInit.runSelectLoop 方法酱塔,從源碼的實(shí)現(xiàn)中可以看出沥邻,這里是不斷地取 Socket 建立的鏈接(ZygoteConnection),然后調(diào)用 ZygoteConnection 中的 runOnce 方法羊娃。

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    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);
            }
        }
    }
}

(6) ZygoteConnection.runOnce 方法里唐全,fork 了 Zygote 進(jìn)程,這就是 應(yīng)用進(jìn)程 了蕊玷,并返回相應(yīng)的 process id邮利,其具體實(shí)現(xiàn)是本地方法,這里就不再深究了垃帅。如果得到相應(yīng)的 Pid延届,接下來看看應(yīng)用進(jìn)程是如何初始化的。

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);
}

(7) handleChildProc 方法贸诚,其中的重點(diǎn)就在 RuntimeInit.zygoteInit 方法 和 ZygoteInit.invokeStaticMain 方法方庭。一個(gè)調(diào)用初始化了相應(yīng)的進(jìn)程,另一個(gè)在調(diào)用了進(jìn)程的 main 方法酱固。

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

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

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

}

在 RuntimeInit.zygoteInit 方法里實(shí)現(xiàn)了相應(yīng)的 AndroidRuntime 初始化械念,并初始化 Binder Driver 相關(guān)的文件,同時(shí)設(shè)置了 UncaughtHandler(應(yīng)用程序崩潰)运悲。在這個(gè)方法調(diào)用結(jié)束后龄减,應(yīng)用進(jìn)程就具備與相應(yīng)系統(tǒng)服務(wù)進(jìn)行 IPC 通信的能力起宽。

(8) ZygoteInit.invokeStaticMain 通過反射調(diào)用了 ZygoteInit.MethodAndArgsCaller猴娩,而這個(gè)就是相應(yīng)的 ActivityThread.java 中的 main 方法录择,其所運(yùn)行的進(jìn)程诗箍,就是大家耳熟能祥的主線程,也成為 UI 線程脖苏。

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

    try {
        cl = loader.loadClass(className);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(
                "Missing class when invoking static main " + className,
                ex);
    }

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

到此為止程拭,整個(gè)應(yīng)用進(jìn)程啟動(dòng)完畢。

get process

應(yīng)用進(jìn)程啟動(dòng)總結(jié)

Android Process 啟動(dòng)

Launcher 中的 Icon 點(diǎn)擊棍潘,broadcast 發(fā)送恃鞋,啟動(dòng) Service 等組件見的跳轉(zhuǎn),都會(huì)通過 AndroidManagerProxy 來進(jìn)行中轉(zhuǎn)亦歉,而 AndroidManagerProxy 通過向 SystemServer 請(qǐng)求名為 ActivityActivityManagerService 的 Binder 對(duì)象恤浪,這個(gè) Binder 對(duì)象可以粗略地看作是 ActivityManagerService 的句柄,從 Binder 對(duì)象可實(shí)際操作 ActivityManagerService肴楷。

ActivityManagerService 在實(shí)際啟動(dòng)相應(yīng)組件時(shí)水由,會(huì)先判斷是否有相應(yīng)的 ProcessRecord,如果不存在赛蔫,就需要新建進(jìn)程砂客,這個(gè)進(jìn)程就是相應(yīng)的應(yīng)用進(jìn)程。ActivityManagerService 通過 Socket 通信的方式和 Zygote 進(jìn)行協(xié)商呵恢,Zygote 在其監(jiān)聽的 /dev/socket/zygote socket 中發(fā)現(xiàn)有需要?jiǎng)?chuàng)建進(jìn)程的請(qǐng)求后鞠值,會(huì) fork 自身,并返回相應(yīng)的 Process Id渗钉。這個(gè) Process 會(huì)進(jìn)行相應(yīng)的初始化彤恶,使得其具備與系統(tǒng)服務(wù)進(jìn)行 IPC 通信的能力,在此之后鳄橘,調(diào)用 ActivityThread 中的 main 方法声离,開啟 Looper,主線程啟動(dòng)瘫怜。到此為止抵恋,整個(gè)應(yīng)用進(jìn)程啟動(dòng)完畢。


參考文獻(xiàn)

  1. Android應(yīng)用程序進(jìn)程啟動(dòng)過程的源代碼分析
  2. Android應(yīng)用程序線程消息循環(huán)模型分析
  3. startActivity流程分析(一)
  4. Android四大組件 Activity啟動(dòng)過程源碼分析
  5. Android Application Launch

文檔信息


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宝磨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盅安,更是在濱河造成了極大的恐慌唤锉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件别瞭,死亡現(xiàn)場(chǎng)離奇詭異窿祥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蝙寨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門晒衩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嗤瞎,“玉大人,你說我怎么就攤上這事听系”雌妫” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵靠胜,是天一觀的道長(zhǎng)掉瞳。 經(jīng)常有香客問我,道長(zhǎng)浪漠,這世上最難降的妖魔是什么陕习? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮址愿,結(jié)果婚禮上该镣,老公的妹妹穿的比我還像新娘。我一直安慰自己响谓,他們只是感情好损合,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著歌粥,像睡著了一般塌忽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上失驶,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天土居,我揣著相機(jī)與錄音,去河邊找鬼嬉探。 笑死擦耀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的涩堤。 我是一名探鬼主播眷蜓,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼胎围!你這毒婦竟也來了吁系?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤白魂,失蹤者是張志新(化名)和其女友劉穎汽纤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體福荸,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蕴坪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片背传。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呆瞻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出径玖,到底是詐尸還是另有隱情痴脾,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布挺狰,位于F島的核電站明郭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏丰泊。R本人自食惡果不足惜薯定,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞳购。 院中可真熱鬧话侄,春花似錦、人聲如沸学赛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盏浇。三九已至变丧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绢掰,已是汗流浹背痒蓬。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留滴劲,地道東北人攻晒。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像班挖,于是被迫代替她去往敵國(guó)和親鲁捏。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容