1. 程式人生 > >Android 應用程序啟動流程

Android 應用程序啟動流程

閱讀的收益

討論的內容也就是一個應用程序是如何啟動的,私以為這一部分的內容頗為重要,即便不瞭解細節,也要知道其中的大體步驟。特別是針對我們應用開發者而言,理應瞭解我們的 App 是如何被啟動的,App 中的元件是如何被系統服務呼叫和組織的。

講應用程序啟動的文章不是很多,也都沒有說到點上,大抵都是對原始碼的堆疊,沒有個人的理解在裡面。如果非要看呼叫棧的話,在合適的地方掛上斷點,或者通過輸出異常棧的方式都可以看到,如下圖所示。老羅的文章 Android應用程式啟動過程原始碼分析 事無鉅細,但感覺還是沒有說到點子上,因而這篇文章只做到拋磚引玉的作用,希望有更好的文章出現。細節是複雜的,原理是簡單的,這裡儘可能地從原理角度出發進行說明,加深大家的理解。

對這篇文章閱讀後,你能瞭解從使用者點選 Launcher 上的 App 圖示,到顯示出 App 介面時主要發生的事情,而通過對這個一過程的瞭解,將知曉以下知識點。

  • Android Process 的建立過程,以及 Activity Manager Service 是如何參與這個步驟,以及在其中扮演的角色?
  • Android 中所謂的主執行緒是怎麼回事?主執行緒是誰?又如何被建立的。
  • Android 系統是如何節省程序建立開銷的?

應用程序簡介

在 Android 中每一個應用程式都被設計為單獨的程序,應用程式也可以根據自己的需要去決定是否需要啟用多個程序,不過總而言之都與其他應用程式和系統服務是相互獨立的。從解耦和系統穩定性的角度上看都應該執行在不同的程序上,畢竟不能因為應用程式的崩潰就影響到其他應用程序或者系統服務程序。

應用程序不同於其他 Android 系統中的守護程序,當記憶體不夠的時候,某些應用程序可能會被系統回收掉,因而應用程序也是有其生命週期的,更多資訊參考 Android Developer 官網對於 Process 的教程 。Android 應用元件不一定要執行在單獨的程序上,也可以執行在多個程序上,通過對 Android 元件指定執行的程序 android:process,即可讓其執行在其他執行緒上。

每個應用程序都相當於一個 Sandbox 沙箱,Android 通過對每一個應用分配一個 UID,注意這裡的 UID 不同於 Linux 系統的 User ID,可以將每個應用理解為一個 User ,只能對其目錄下的內容具有訪問和讀寫許可權,這樣就從根源上保護了其他應用程式,下圖說明了其隔離效果。

App Process Isolate

Zygote 程序

Zygote 的中文意思是受精卵,從這個意思裡也可以看出 Zygote 程序是用來分裂複製(fork)的,實際上所有的 App 程序都是通過對 Zygote 程序的 Fork 得來的。當 app_process 啟動 Zygote 時,Zygote 會在其啟動後,預載入必要的 Java Classes(相關列表檢視 預載入檔案) 和 Resources,並啟動 System Server ,並開啟 /dev/socket/zygote socket 去監聽啟動應用程式的請求,日後。在下面的程式碼中,顯示了 Zygote 程序如何啟動,和載入 System Server 的。

受精卵


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(); // 預載入資源
  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(); // 觸發 GC
  if (startSystemServer) { // 啟動 System Server.
      startSystemServer(abiList, socketName);
  }
  // ...
}

Fork Zygote 互動圖

那麼為何要做這種設計呢?每個應用程式的執行,都需要依託於相應的執行環境,而這個就是 Davlik (ART) 虛擬機器,但每次啟動的開銷較大,而通過對 Zygote 程序的 Fork,能夠提升不小的效率。並且在這個工程中,採用了 Copy-on-Write 的方式,極大程度上地複用了 Zygote 上面的資源。更多資訊也可以參考我這篇博文 詳解 Android 是如何啟動的

App 應用程序啟動

接下來的內容涉及到很多 Binder 通訊相關的東西,因此在閱讀本文前,建議查閱下 Binder 相關的文章,這裡有下列文章供查考。

ActivityManager 架構

在我們程式設計過程中,涉及到許多 Activity 跳轉的事情,在 Launcher 中點選 Icon 進行跳轉也是同樣的道理,呼叫 context.startActivity(intent) 方法。Launcher 出於一個執行緒,而啟動的 App 則執行在另一個程序中,在這其中勢必牽涉到跨程序 (IPC) 呼叫,這樣複雜的過程顯然需要一種中介者,或者一個系統來進行中轉和管理,而這個服務就是 ActivityManagerService

ActivityManagerService 作為一個守護程序執行在 Android Framework 中,如果讓開發者直接接觸這個類的話,就需要開發者自行處理 IPC 呼叫的問題,且這有不利於 Android 系統進行安全校驗等工作。因而 Android 系統實現了 ActivityManager,通過這個 ActivityManager 作為一個入口,變相地和 ActivityManagerService 打交道。這種模式在 Android 系統中極為常見,類似的還有 WifiManager, LocationManagerWindowsManager 等等。而這些 Manger 在背後呼叫的東西就是前面提及的 Binder 機制。下面以 ActivityManger 為例看看其背後的運作方式。

Binder 體系架構可以分為 Client 和 Server 兩端,為了更方便 Client 的呼叫,這次採用了 AIDL 的方式,具體參考連結 Android Binder 完全解析(三)AIDL實現原理分析 。這裡再用類比的方式來說明,方便大家理解。歷史上有不少垂簾聽政的故事,背後操作的人實際是通過控制傀儡來控制朝政,通過給傀儡皇帝傳遞命令,傀儡皇帝只是複述命令,起到傳遞的作用。更有甚者,不想去上朝的控權者,會通過手下的太監或者婢女,轉述給傀儡皇上。這種模式被我們稱為代理模式,ActivityManger 所使用的就是這種模式。

首先這裡要針對要執行的命令進行抽象,這樣掌權者、太監、皇上和朝政才能聽懂。IActivityManager 就是對這個進行的抽象,點選檢視 原始碼,這幾種就包括常見的 startActivity, showWaitingForDebugger, finishActivity 等等。ActivityManagerProxy 就相當於其他的太監或者婢女,Proxy 不需要懂具體的業務,只需要把指令傳遞過去就行。ActivityManagerService 就是具體的執行者,就是大臣們。ActivityManger 則就是具體的業務邏輯的外觀類(參加GOF的設計模式),也就是具體的掌權者們。它們的關係如下圖所示:

ActivityManager Service

原始碼分析從 Launcher 到 ActivityManager 啟動步驟

接下來分析下,在原始碼裡是具體操作的,也驗證我們前面的說法。

(1) 點選 Launcher 的圖示,會呼叫到 Activity 的 startActivity 方法。在繼續往下看過去,這個裡面會呼叫到 startActivityForResult 方法,在 startActivityForResult 方法中,疏通同歸,最後會呼叫 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 執行 execStartActivity 方法。引數裡面中的 contextThread 和 token 物件都是 IBinder 型別,而 Binder 可以在跨程序呼叫中依舊充當 Token 的角色,在多程序中由 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 中實際返回的就是 Proxy 物件,在實際中只起到代理的重要,並不進行邏輯處理。

先看看 ActivityManagerNative.getDefault() 中的實現。

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

IActivityManager 根據前文的描述即是對於可操作介面的抽象,Singleton 則是對單例物件的封裝。也就是說 gDefault 返回了實現 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, 這就是前文提及的 太監和婢女 角色,我們再看看 ActivityManagerProxy 內部是如何工作的。

(4) ActivityManagerProxy 的實現。從原始碼裡面可以看出,ActivityManagerProxy 將遠端 Binder 作為建構函式的引數,而在 startActivity 方法中,通過遠端 Binder 物件的 transact 方法,將引數寫入到 data 中,在遠端執行完畢後,結果寫入到 reply 裡。這裡實實在在地起到了 Proxy 的作用,只負責資料的傳輸。重點在下面這行程式碼:

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 的呼叫。

在上部分提及的 ActivityManagerProxy 中在建構函式裡傳入的 mRemote 遠端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。所有的系統服務都是 IBinder 物件,即他們必須支援遠端呼叫。而每個系統服務都會通過在 ServiceManager 註冊別名的方式,告知 ServiceManager 通過相應的別名即可訪問到我。而 activity 正是 ActivityManagerService 的別名。

從 ActivityManagerService 到 程序啟動

ActivityManagerService 在接受到相應的 Intent 請求後(Activity、Broadcast、Service、ContentProvider),會檢視是否需要進行新建程序的工作,這裡以 Activity 為例,其他元件的步驟與此原理相同,就不再贅述。

(1) ActivityManagerService 在啟動 Activity 之前,首先通過 resolveIntent 方法,來得到相應的 ResolveInfo,其後通過呼叫 startActivityLocked 往下啟動 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 方法,判斷是否需要新建程序。從程式碼中看出,這裡對 ProcessRecord 進行了判斷,ProcessRecord 就是響應的程序記錄,如果存在相應的程序,就啟動相應的 Activity, 否則將建立程序。mService.startProcessLocked 這個方法實現了開啟程序,下面再看看裡面的實現。

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 在內部呼叫了 Process.start 方法,並且指定了 android.app.ActivityThread 作為程序的入口,程序啟動後,將呼叫 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 方法中,實際呼叫的是 startViaZygote 方法,在這個方法裡通過 openZygoteSocketIfNeeded 開啟 Zygote 的 socket,並通過 zygoteSendArgsAndGetResult 進行互動。

根據前文提及的內容,zygote 開啟了一個 socket 監聽功能,監聽需要建立 Process 的請求,因而在這裡,我們去檢視 Zygote 的程式碼,看其是怎麼監聽請求的。

return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);

(5) ZygoteInit.runSelectLoop 方法,從原始碼的實現中可以看出,這裡是不斷地取 Socket 建立的連結(ZygoteConnection),然後呼叫 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 程序,這就是 應用程序 了,並返回相應的 process id,其具體實現是本地方法,這裡就不再深究了。如果得到相應的 Pid,接下來看看應用程序是如何初始化的。

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 方法,其中的重點就在 RuntimeInit.zygoteInit 方法 和 ZygoteInit.invokeStaticMain 方法。一個呼叫初始化了相應的程序,另一個在呼叫了程序的 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 方法裡實現了相應的 AndroidRuntime 初始化,並初始化 Binder Driver 相關的檔案,同時設定了 UncaughtHandler(應用程式崩潰)。在這個方法呼叫結束後,應用程序就具備與相應系統服務進行 IPC 通訊的能力。

(8) ZygoteInit.invokeStaticMain 通過反射呼叫了 ZygoteInit.MethodAndArgsCaller,而這個就是相應的 ActivityThread.java 中的 main 方法,其所執行的程序,就是大家耳熟能祥的主執行緒,也成為 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);
}

到此為止,整個應用程序啟動完畢。

get process

應用程序啟動總結

Android Process 啟動流程總結