理解 Android 程式啟動之全過程

z_paul發表於2021-09-09

一. 概述

Android系統將程式做得很友好的封裝,對於上層app開發者來說程式幾乎是透明的. 瞭解Android的朋友,一定知道Android四大元件,但對於程式可能會相對較陌生. 一個程式裡面可以跑多個app(通過share uid的方式), 一個app也可以跑在多個程式裡(通過配置Android:process屬性).

再進一步程式是如何建立的, 可能很多人不知道fork的存在. 在我的文章理解Android程式建立流程 集中一點詳細介紹了Process.start的過程是如何一步步建立程式.本文則是從另個角度來全域性性講解Android程式啟動全過程所涉及的根脈, 先來看看AMS.startProcessLocked方法.

二. 四大元件與程式

2.1 startProcessLocked

ActivityManagerService.java關於啟動程式有4個同名不同引數的過載方法, 為了便於說明,以下4個方法依次記為1(a),1(b)2(a)2(b) :

//方法 1(a)
final ProcessRecord startProcessLocked(
    String processName, ApplicationInfo info, boolean knownToBeDead,
    int intentFlags, String hostingType, ComponentName hostingName,
    boolean allowWhileBooting, boolean isolated, boolean keepIfLarge)

//方法 1(b)
final ProcessRecord startProcessLocked(
    String processName, ApplicationInfo info, boolean knownToBeDead,
    int intentFlags, String hostingType, ComponentName hostingName,
    boolean allowWhileBooting, boolean isolated, int isolatedUid,
    boolean keepIfLarge, String abiOverride, String entryPoint,
    String[] entryPointArgs, Runnable crashHandler)

//方法 2(a)
private final void startProcessLocked(
    ProcessRecord app, String hostingType, String hostingNameStr)

//方法 2(b)
private final void startProcessLocked(
    ProcessRecord app, String hostingType, String hostingNameStr,
    String abiOverride, String entryPoint, String[] entryPointArgs)

1(a) ==> 1(b): 方法1(a)將isolatedUid=0,其他引數賦值為null,再呼叫給1(b)

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

2(a) ==> 2(b): 方法2(a)將其他3個引數abiOverride,entryPoint, entryPointArgs賦值為null,再呼叫給2(b)

private final void startProcessLocked(ProcessRecord app,
        String hostingType, String hostingNameStr) {
    startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,
            null /* entryPoint */, null /* entryPointArgs */);
}

小結:

  • 1(a),1(b)的第一個引數為String型別的程式名processName,
  • 2(a), 2(b)的第一個引數為ProcessRecord型別程式記錄資訊ProcessRecord;
  • 1系列的方法最終呼叫到2系列的方法;

2.2 四大元件與程式

Activity, Service, ContentProvider, BroadcastReceiver這四大元件,在啟動的過程,當其所承載的程式不存在時需要先建立程式. 這個建立程式的過程是呼叫前面講到的startProcessLocked方法1(a) . 呼叫流程: 1(a) => 1(b) ==> 2(b). 下面再簡單說說這4大元件與程式建立是在何時需要建立的.

2.2.1 Activity

啟動Activity過程: 呼叫startActivity,該方法經過層層呼叫,最終會呼叫ActivityStackSupervisor.java中的startSpecificActivityLocked,當activity所屬程式還沒啟動的情況下,則需要建立相應的程式.

[-> ActivityStackSupervisor.java]

void startSpecificActivityLocked(...) {
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,
            r.info.applicationInfo.uid, true);
    if (app != null && app.thread != null) {
        ...  //程式已建立的case
        return
    }
    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
}

2.2.2 Service

啟動服務過程: 呼叫startService,該方法經過層層呼叫,最終會呼叫ActiveServices.java中的bringUpServiceLocked,當Service程式沒有啟動的情況(app==null), 則需要建立相應的程式. 更多關於Service, 見startService流程分析

[-> ActiveServices.java]

private final String bringUpServiceLocked(...){
    ...
    ProcessRecord app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
    if (app == null) {
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                "service", r.name, false, isolated, false)) == null) {
            ...
        }
    }
    ...
}

2.2.3 ContentProvider

ContentProvider處理過程: 呼叫ContentResolver.query該方法經過層層呼叫, 最終會呼叫到AMS.java中的getContentProviderImpl,當ContentProvider所對應程式不存在,則需要建立新程式. 更多關於ContentProvider,見理解ContentProvider原理(一)

[-> AMS.java]

private final ContentProviderHolder getContentProviderImpl(...) {
    ...
    ProcessRecord proc = getProcessRecordLocked(cpi.processName, cpr.appInfo.uid, false);
    if (proc != null && proc.thread != null) {
        ...  //程式已建立的case
    } else {
        proc = startProcessLocked(cpi.processName,
                    cpr.appInfo, false, 0, "content provider",
                    new ComponentName(cpi.applicationInfo.packageName,cpi.name),
                    false, false, false);
    }
    ...
}

2.2.4 Broadcast

廣播處理過程: 呼叫sendBroadcast,該方法經過層層呼叫, 最終會呼叫到BroadcastQueue.java中的processNextBroadcast,當BroadcastReceiver所對應的程式尚未啟動,則建立相應程式. 更多關於broadcast, 見Android Broadcast廣播機制分析.

[-> BroadcastQueue.java]

final void processNextBroadcast(boolean fromMsg) {
    ...
    ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
        info.activityInfo.applicationInfo.uid, false);
    if (app != null && app.thread != null) {
        ...  //程式已建立的case
        return
    }

    if ((r.curApp=mService.startProcessLocked(targetProcess,
            info.activityInfo.applicationInfo, true,
            r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
            "broadcast", r.curComponent,
            (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                    == null) {
        ...
    }
    ...
}

2.3 小節

Activity, Service, ContentProvider, BroadcastReceiver這四大元件在啟動時,當所承載的程式不存在時,都需要建立. 程式的建立過程交由系統程式system_server來完成的.

app_process_ipc

簡稱:

  • ATP: ApplicationThreadProxy
  • AT: ApplicationThread (繼承於ApplicationThreadNative)
  • AMP: ActivityManagerProxy
  • AMS: ActivityManagerService (繼承於ActivityManagerNative)

圖解:

  1. system_server程式中呼叫startProcessLocked方法,該方法最終通過socket方式,將需要建立新程式的訊息告知Zygote程式,並阻塞等待Socket返回新建立程式的pid;
  2. Zygote程式接收到system_server傳送過來的訊息, 則通過fork的方法,將zygote自身程式複製生成新的程式,並將ActivityThread相關的資源載入到新程式app process,這個程式可能是用於承載activity等元件;
  3. 建立完新程式後fork返回兩次, 在新程式app process向servicemanager查詢system_server程式中binder服務端AMS,獲取相對應的Client端,也就是AMP. 有了這一對binder c/s對, 那麼app process便可以通過binder向跨程式system_server傳送請求,即attachApplication()
  4. system_server程式接收到相應binder操作後,經過多次呼叫,利用ATP向app process傳送binder請求, 即bindApplication.

system_server擁有ATP/AMS, 每一個新建立的程式都會有一個相應的AT/AMS,從而可以跨程式 進行相互通訊. 這便是程式建立過程的完整生態鏈.

四大元件的程式建立時機:

元件 建立方法
Activity ASS.startSpecificActivityLocked()
Service ActiveServices.bringUpServiceLocked()
ContentProvider AMS.getContentProviderImpl()
Broadcast BroadcastQueue.processNextBroadcast()

三. 程式啟動全過程

前面剛已介紹四大元件的建立程式的過程是呼叫1(a) startProcessLocked方法,該方法會再呼叫1(b)方法. 接下來從該方法開始往下講述.

3.1 AMS.startProcessLocked

final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
        boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
        boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
        String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
    long startTime = SystemClock.elapsedRealtime();
    ProcessRecord app;
    if (!isolated) {
        //根據程式名和uid檢查相應的ProcessRecord
        app = getProcessRecordLocked(processName, info.uid, keepIfLarge);

        if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
            //如果當前處於後臺程式,檢查當前程式是否處於bad程式列表
            if (mBadProcesses.get(info.processName, info.uid) != null) {
                return null;
            }
        } else {
            //當使用者明確地啟動程式,則清空crash次數,以保證其不處於bad程式直到下次再彈出crash對話方塊。
            mProcessCrashTimes.remove(info.processName, info.uid);
            if (mBadProcesses.get(info.processName, info.uid) != null) {
                mBadProcesses.remove(info.processName, info.uid);
                if (app != null) {
                    app.bad = false;
                }
            }
        }
    } else {
        //對於孤立程式,無法再利用已存在的程式
        app = null;
    }

    //當存在ProcessRecord,且已分配pid(正在啟動或者已經啟動),
    // 且caller並不認為該程式已死亡或者沒有thread物件attached到該程式.則不應該清理該程式
    if (app != null && app.pid > 0) {
        if (!knownToBeDead || app.thread == null) {
            //如果這是程式中新package,則新增到列表
            app.addPackage(info.packageName, info.versionCode, mProcessStats);
            return app;
        }
        //當ProcessRecord已經被attached到先前的一個程式,則殺死並清理該程式
        killProcessGroup(app.info.uid, app.pid);
        handleAppDiedLocked(app, true, true);
    }

    String hostingNameStr = hostingName != null? hostingName.flattenToShortString() : null;
    if (app == null) {
        // 建立新的Process Record物件
        app = newProcessRecordLocked(info, processName, isolated, isolatedUid);
        if (app == null) {
            return null;
        }
        app.crashHandler = crashHandler;
    } else {
        //如果這是程式中新package,則新增到列表
        app.addPackage(info.packageName, info.versionCode, mProcessStats);
    }
    //當系統未準備完畢,則將當前程式加入到mProcessesOnHold
    if (!mProcessesReady && !isAllowedWhileBooting(info) && !allowWhileBooting) {
        if (!mProcessesOnHold.contains(app)) {
            mProcessesOnHold.add(app);
        }
        return app;
    }
    // 啟動程式【見小節3.2】
    startProcessLocked(app, hostingType, hostingNameStr, abiOverride, entryPoint, entryPointArgs);
    return (app.pid != 0) ? app : null;
}

主要功能:

  • 對於非isolated程式,則根據程式名和uid來查詢相應的ProcessRecord結構體. 如果當前程式處於後臺且當前程式處於mBadProcesses列表,則直接返回;否則清空crash次數,以保證其不處於bad程式直到下次再彈出crash對話方塊。
  • 當存在ProcessRecord,且已分配pid(正在啟動或者已經啟動)的情況下
    • 當caller並不認為該程式已死亡或者沒有thread物件attached到該程式.則不應該清理該程式,則直接返回;
    • 否則殺死並清理該程式;
  • 當ProcessRecord為空則新建一個,當建立失敗則直接返回;
  • 當系統未準備完畢,則將當前程式加入到mProcessesOnHold, 並直接返回;
  • 最後啟動新程式,其中引數含義:
    • hostingType可取值為”activity”,”service”,”broadcast”,”content provider”;
    • hostingNameStr資料型別為ComponentName,代表的是具體相對應的元件名.

另外, 程式的uid是在程式真正建立之前呼叫newProcessRecordLocked方法來獲取的uid, 這裡會考慮是否為isolated的情況.

3.2 AMS.startProcessLocked

private final void startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
    long startTime = SystemClock.elapsedRealtime();
    //當app的pid大於0且不是當前程式的pid,則從mPidsSelfLocked中移除該app.pid
    if (app.pid > 0 && app.pid != MY_PID) {
        synchronized (mPidsSelfLocked) {
            mPidsSelfLocked.remove(app.pid);
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        }
        app.setPid(0);
    }
    //從mProcessesOnHold移除該app
    mProcessesOnHold.remove(app);
    updateCpuStats(); //更新cpu統計資訊
    try {
        try {
            if (AppGlobals.getPackageManager().isPackageFrozen(app.info.packageName)) {
                //當前package已被凍結,則丟擲異常
                throw new RuntimeException("Package " + app.info.packageName + " is frozen!");
            }
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
        int uid = app.uid;
        int[] gids = null;
        int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
        if (!app.isolated) {
            int[] permGids = null;
            try {
                //通過Package Manager獲取gids
                final IPackageManager pm = AppGlobals.getPackageManager();
                permGids = pm.getPackageGids(app.info.packageName, app.userId);
                MountServiceInternal mountServiceInternal = LocalServices.getService(
                        MountServiceInternal.class);
                mountExternal = mountServiceInternal.getExternalStorageMountMode(uid,
                        app.info.packageName);
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            }

            //新增共享app和gids,用於app直接共享資源
            if (ArrayUtils.isEmpty(permGids)) {
                gids = new int[2];
            } else {
                gids = new int[permGids.length + 2];
                System.arraycopy(permGids, 0, gids, 2, permGids.length);
            }
            gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
            gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
        }

        //根據不同引數,設定相應的debugFlags
        ...

        app.gids = gids;
        app.requiredAbi = requiredAbi;
        app.instructionSet = instructionSet;

        boolean isActivityProcess = (entryPoint == null);
        if (entryPoint == null) entryPoint = "android.app.ActivityThread";
        //請求Zygote建立新程式[見3.3]
        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);

        ...
        if (app.persistent) {
            Watchdog.getInstance().processStarted(app.processName, startResult.pid);
        }
        //重置ProcessRecord的成員變數
        app.setPid(startResult.pid);
        app.usingWrapper = startResult.usingWrapper;
        app.removed = false;
        app.killed = false;
        app.killedByAm = false;

        //將新建立的程式加入到mPidsSelfLocked
        synchronized (mPidsSelfLocked) {
            this.mPidsSelfLocked.put(startResult.pid, app);
            if (isActivityProcess) {
                Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                msg.obj = app;
                //延遲傳送訊息PROC_START_TIMEOUT_MSG
                mHandler.sendMessageDelayed(msg, startResult.usingWrapper
                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
            }
        }
    } catch (RuntimeException e) {
        app.setPid(0); //程式建立失敗,則重置pid
    }
}
  • 根據不同引數,設定相應的debugFlags,比如在AndroidManifest.xml中設定androidd:debuggable為true,代表app執行在debug模式,則增加debugger標識以及開啟JNI check功能
  • 呼叫Process.start來建立新程式;
  • 重置ProcessRecord的成員變數, 一般情況下超時10s後傳送PROC_START_TIMEOUT_MSG的handler訊息;

關於Process.start()是通過socket通訊告知Zygote建立fork子程式,建立新程式後將ActivityThread類載入到新程式,並呼叫ActivityThread.main()方法。詳細過程見理解Android程式建立流程,接下來進入AT.main方法.

3.3 ActivityThread.main

[-> ActivityThread.java]

public static void main(String[] args) {
    //效能統計預設是關閉的
    SamplingProfilerIntegration.start();
    //將當前程式所在userId賦值給sCurrentUser
    Environment.initForCurrentUser();

    EventLogger.setReporter(new EventLoggingReporter());
    AndroidKeyStoreProvider.install();

    //確保可信任的CA證照存放在正確的位置
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    //建立主執行緒的Looper物件, 該Looper是不執行退出
    Looper.prepareMainLooper();

    //建立ActivityThread物件
    ActivityThread thread = new ActivityThread();

    //建立Binder通道 【見流程3.4】
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    // 當設定為true時,可開啟訊息佇列的debug log資訊
    if (false) {
        Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop(); //訊息迴圈執行
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
  • 建立主執行緒的Looper物件: 該Looper是不執行退出. 也就是說主執行緒的Looper是在程式建立完成時自動建立完成,如果子執行緒也需要建立handler通訊過程,那麼就需要手動建立Looper物件,並且每個執行緒只能建立一次.
  • 建立ActivityThread物件thread = new ActivityThread(): 該過程會初始化幾個很重要的變數:
    • mAppThread = new ApplicationThread()
    • mLooper = Looper.myLooper()
    • mH = new H(), H繼承於Handler;用於處理元件的生命週期.
  • attach過程是當前主執行緒向system_server程式通訊的過程, 將thread資訊告知AMS.接下來還會進一步說明該過程.
  • sMainThreadHandler通過getHandler(),獲取的物件便是mH,這就是主執行緒的handler物件.

之後主執行緒呼叫Looper.loop(),進入訊息迴圈狀態, 當沒有訊息時主執行緒進入休眠狀態, 一旦有訊息到來則喚醒主執行緒並執行相關操作.

3.4. ActivityThread.attach

[-> ActivityThread.java]

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
         //開啟虛擬機器的jit即時編譯功能
        ViewRootImpl.addFirstDrawHandler(new Runnable() {
            @Override
            public void run() {
                ensureJitEnabled();
            }
        });
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>", UserHandle.myUserId());

        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        //建立ActivityManagerProxy物件
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            //呼叫基於IActivityManager介面的Binder通道【見流程3.5】
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
        }

        //觀察是否快接近heap的上限
        BinderInternal.addGcWatcher(new Runnable() {
            @Override public void run() {
                if (!mSomeActivitiesChanged) {
                    return;
                }
                Runtime runtime = Runtime.getRuntime();
                long dalvikMax = runtime.maxMemory();
                long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
                if (dalvikUsed > ((3*dalvikMax)/4)) {
                    mSomeActivitiesChanged = false;
                    try {
                        //當已用記憶體超過最大記憶體的3/4,則請求釋放記憶體空間
                        mgr.releaseSomeActivities(mAppThread);
                    } catch (RemoteException e) {
                    }
                }
            }
        });
    } else {
        ...
    }
    //新增dropbox日誌到libcore
    DropBox.setReporter(new DropBoxReporter());

    //新增Config回撥介面
    ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            synchronized (mResourcesManager) {
                if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
                    if (mPendingConfiguration == null ||
                            mPendingConfiguration.isOtherSeqNewer(newConfig)) {
                        mPendingConfiguration = newConfig;
                        sendMessage(H.CONFIGURATION_CHANGED, newConfig);
                    }
                }
            }
        }
        @Override
        public void onLowMemory() {
        }
        @Override
        public void onTrimMemory(int level) {
        }
    });
}

對於非系統attach的處理流程:

  • 建立執行緒來開啟虛擬機器的jit即時編譯;
  • 通過binder, 呼叫到AMS.attachApplication, 其引數mAppThread的資料型別為ApplicationThread
  • 觀察是否快接近heap的上限,當已用記憶體超過最大記憶體的3/4,則請求釋放記憶體空間
  • 新增dropbox日誌到libcore
  • 新增Config回撥介面

3.5 AMP.attachApplication

[-> ActivityManagerProxy.java]

public void attachApplication(IApplicationThread app) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(app.asBinder());
    mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0); //【見流程3.6】
    reply.readException();
    data.recycle();
    reply.recycle();
}

此處 descriptor = “android.app.IActivityManager”

3.6 AMN.onTransact

[-> ActivityManagerNative.java]

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {
    ...
     case ATTACH_APPLICATION_TRANSACTION: {
        data.enforceInterface(IActivityManager.descriptor);
        //獲取ApplicationThread的binder代理類 ApplicationThreadProxy
        IApplicationThread app = ApplicationThreadNative.asInterface(
                data.readStrongBinder());
        if (app != null) {
            attachApplication(app); //此處是ActivityManagerService類中的方法 【見流程3.7】
        }
        reply.writeNoException();
        return true;
    }
    }
}

3.7 AMS.attachApplication

[-> ActivityManagerService.java]

public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid); // 【見流程3.8】
        Binder.restoreCallingIdentity(origId);
    }
}

此處的thread便是ApplicationThreadProxy物件,用於跟前面通過Process.start()所建立的程式中ApplicationThread物件進行通訊.

3.8 AMS.attachApplicationLocked

[-> ActivityManagerService.java]

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) {
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid); // 根據pid獲取ProcessRecord
        }
    } else {
        app = null;
    }
    if (app == null) {
        if (pid > 0 && pid != MY_PID) {
            //ProcessRecord為空,則殺掉該程式
            Process.killProcessQuiet(pid);
        } else {
            //退出新建程式的Looper
            thread.scheduleExit();
        }
        return false;
    }

    //還剛進入attach過程,此時thread應該為null,若不為null則表示該app附到上一個程式,則立刻清空
    if (app.thread != null) {
        handleAppDiedLocked(app, true, true);
    }

    final String processName = app.processName;
    try {
        //繫結死亡通知
        AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);
        thread.asBinder().linkToDeath(adr, 0);
        app.deathRecipient = adr;
    } catch (RemoteException e) {
        app.resetPackageList(mProcessStats);
        startProcessLocked(app, "link fail", processName); //重新啟動程式
        return false;
    }

    //重置程式資訊
    app.makeActive(thread, mProcessStats); //執行完該語句,則app.thread便不再為空
    app.curAdj = app.setAdj = -100;
    app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
    app.forcingToForeground = null;
    updateProcessForegroundLocked(app, false, false);
    app.hasShownUi = false;
    app.debugging = false;
    app.cached = false;
    app.killedByAm = false;
    mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); //移除程式啟動超時的訊息

    //系統處於ready狀態或者該app為FLAG_PERSISTENT程式,則為true
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

    //app程式存在正在啟動中的provider,則超時10s後傳送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG訊息
    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }

    try {
        ...
        ensurePackageDexOpt(app.instrumentationInfo != null
                ? app.instrumentationInfo.packageName
                : app.info.packageName);

        ApplicationInfo appInfo = app.instrumentationInfo != null
                ? app.instrumentationInfo : app.info;
        ...
        // 繫結應用 [見流程3.9]
        thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                isRestrictedBackupMode || !normalMode, app.persistent,
                new Configuration(mConfiguration), app.compat,
                getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked());
        //更新程式LRU佇列
        updateLruProcessLocked(app, false, null);
        app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
    } catch (Exception e) {
        app.resetPackageList(mProcessStats);
        app.unlinkDeathRecipient();
        //每當bind操作失敗,則重啟啟動程式, 此處有可能會導致程式無限重啟
        startProcessLocked(app, "bind fail", processName);
        return false;
    }

    mPersistentStartingProcesses.remove(app);
    mProcessesOnHold.remove(app);
    boolean badApp = false;
    boolean didSomething = false;

    //Activity: 檢查最頂層可見的Activity是否等待在該程式中執行
    if (normalMode) {
        try {
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;
            }
        } catch (Exception e) {
            badApp = true;
        }
    }

    //Service: 尋找所有需要在該程式中執行的服務
    if (!badApp) {
        try {
            didSomething |= mServices.attachApplicationLocked(app, processName);
        } catch (Exception e) {
            badApp = true;
        }
    }

    //Broadcast: 檢查是否在這個程式中有下一個廣播接收者
    if (!badApp && isPendingBroadcastProcessLocked(pid)) {
        try {
            didSomething |= sendPendingBroadcastsLocked(app);
        } catch (Exception e) {
            badApp = true;
        }
    }
    //檢查是否在這個程式中有下一個backup代理
    if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.uid) {
        ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
        try {
            thread.scheduleCreateBackupAgent(mBackupTarget.appInfo,
                    compatibilityInfoForPackageLocked(mBackupTarget.appInfo),
                    mBackupTarget.backupMode);
        } catch (Exception e) {
            badApp = true;
        }
    }
    if (badApp) { //殺掉bad應用
        app.kill("error during init", true);
        handleAppDiedLocked(app, false, true);
        return false;
    }
    if (!didSomething) {
        updateOomAdjLocked(); //更新adj的值
    }
    return true;
}
  1. 根據pid從mPidsSelfLocked中查詢到相應的ProcessRecord物件app;
  2. 當app==null,意味著本次建立的程式不存在, 則直接返回.
  3. 還剛進入attach過程,此時thread應該為null,若不為null則表示該app附到上一個程式,則呼叫handleAppDiedLocked清理.
  4. 繫結死亡通知,當程式pid死亡時會通過binder死亡回撥,來通知system_server程式死亡的訊息;
  5. 重置ProcessRecord程式資訊, 此時app.thread也賦予了新值,便不再為空.
  6. app程式存在正在啟動中的provider,則超時10s後傳送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG訊息
  7. 呼叫thread.bindApplication繫結應用程式, 後面再進一步說明
  8. 處理Provider, Activity, Service, Broadcast相應流程

下面,再來說說thread.bindApplication的過程.

3.9 ATP.bindApplication

[-> ApplicationThreadNative.java ::ApplicationThreadProxy]

class ApplicationThreadProxy implements IApplicationThread {
    ...
    public final void bindApplication(String packageName, ApplicationInfo info,
            List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo,
            Bundle testArgs, IInstrumentationWatcher testWatcher,
            IUiAutomationConnection uiAutomationConnection, int debugMode,
            boolean openGlTrace, boolean restrictedBackupMode, boolean persistent,
            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
            Bundle coreSettings) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        data.writeString(packageName);
        info.writeToParcel(data, 0);
        data.writeTypedList(providers);
        if (testName == null) {
            data.writeInt(0);
        } else {
            data.writeInt(1);
            testName.writeToParcel(data, 0);
        }
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        data.writeBundle(testArgs);
        data.writeStrongInterface(testWatcher);
        data.writeStrongInterface(uiAutomationConnection);
        data.writeInt(debugMode);
        data.writeInt(openGlTrace ? 1 : 0);
        data.writeInt(restrictedBackupMode ? 1 : 0);
        data.writeInt(persistent ? 1 : 0);
        config.writeToParcel(data, 0);
        compatInfo.writeToParcel(data, 0);
        data.writeMap(services);
        data.writeBundle(coreSettings);
        mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
                IBinder.FLAG_ONEWAY);
        data.recycle();
    }
    ...
}

ATP經過binder ipc傳遞到ATN的onTransact過程.

3.10 ATN.onTransact

[-> ApplicationThreadNative.java]

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    switch (code) {
    ...
    case BIND_APPLICATION_TRANSACTION:
    {
        data.enforceInterface(IApplicationThread.descriptor);
        String packageName = data.readString();
        ApplicationInfo info =
            ApplicationInfo.CREATOR.createFromParcel(data);
        List<ProviderInfo> providers =
            data.createTypedArrayList(ProviderInfo.CREATOR);
        ComponentName testName = (data.readInt() != 0)
            ? new ComponentName(data) : null;
        ProfilerInfo profilerInfo = data.readInt() != 0
                ? ProfilerInfo.CREATOR.createFromParcel(data) : null;
        Bundle testArgs = data.readBundle();
        IBinder binder = data.readStrongBinder();
        IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
        binder = data.readStrongBinder();
        IUiAutomationConnection uiAutomationConnection =
                IUiAutomationConnection.Stub.asInterface(binder);
        int testMode = data.readInt();
        boolean openGlTrace = data.readInt() != 0;
        boolean restrictedBackupMode = (data.readInt() != 0);
        boolean persistent = (data.readInt() != 0);
        Configuration config = Configuration.CREATOR.createFromParcel(data);
        CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
        HashMap<String, IBinder> services = data.readHashMap(null);
        Bundle coreSettings = data.readBundle();
        //[見流程3.11]
        bindApplication(packageName, info, providers, testName, profilerInfo, testArgs,
                testWatcher, uiAutomationConnection, testMode, openGlTrace,
                restrictedBackupMode, persistent, config, compatInfo, services, coreSettings);
        return true;
    }
    ...
}

3.11 AT.bindApplication

[-> ActivityThread.java ::ApplicationThread]

public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> providers, ComponentName instrumentationName,
        ProfilerInfo profilerInfo, Bundle instrumentationArgs,
        IInstrumentationWatcher instrumentationWatcher,
        IUiAutomationConnection instrumentationUiConnection, int debugMode,
        boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
        Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
        Bundle coreSettings) {

    if (services != null) {
        //將services快取起來, 減少binder檢索服務的次數
        ServiceManager.initServiceCache(services);
    }

    //傳送訊息H.SET_CORE_SETTINGS
    setCoreSettings(coreSettings);

    IPackageManager pm = getPackageManager();
    android.content.pm.PackageInfo pi = null;
    try {
        pi = pm.getPackageInfo(appInfo.packageName, 0, UserHandle.myUserId());
    } catch (RemoteException e) {
    }
    if (pi != null) {
        boolean sharedUserIdSet = (pi.sharedUserId != null);
        boolean processNameNotDefault = (pi.applicationInfo != null &&
         !appInfo.packageName.equals(pi.applicationInfo.processName));
        boolean sharable = (sharedUserIdSet || processNameNotDefault);

        if (!sharable) {
            VMRuntime.registerAppInfo(appInfo.packageName, appInfo.dataDir,
                                    appInfo.processName);
        }
    }

    //初始化AppBindData, 再傳送訊息H.BIND_APPLICATION
    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    data.instrumentationName = instrumentationName;
    data.instrumentationArgs = instrumentationArgs;
    data.instrumentationWatcher = instrumentationWatcher;
    data.instrumentationUiAutomationConnection = instrumentationUiConnection;
    data.debugMode = debugMode;
    data.enableOpenGlTrace = enableOpenGlTrace;
    data.restrictedBackupMode = isRestrictedBackupMode;
    data.persistent = persistent;
    data.config = config;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    sendMessage(H.BIND_APPLICATION, data);
}

其中setCoreSettings()過程就是呼叫sendMessage(H.SET_CORE_SETTINGS, coreSettings) 來向主執行緒傳送SET_CORE_SETTINGS訊息.bindApplication方法的主要功能是依次向主執行緒傳送訊息H.SET_CORE_SETTINGSH.BIND_APPLICATION. 接下來再來說說這兩個訊息的處理過程

3.12 H.SET_CORE_SETTINGS

[-> ActivityThread.java ::H]

當主執行緒收到H.SET_CORE_SETTINGS,則呼叫handleSetCoreSettings

private void handleSetCoreSettings(Bundle coreSettings) {
    synchronized (mResourcesManager) {
        mCoreSettings = coreSettings;
    }
    onCoreSettingsChange();
}

private void onCoreSettingsChange() {
    boolean debugViewAttributes = mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0;
    if (debugViewAttributes != View.mDebugViewAttributes) {
        View.mDebugViewAttributes = debugViewAttributes;

        // 由於發生改變, 請求所有的activities重啟啟動
        for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
            requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false);
        }
    }
}

3.13 H.BIND_APPLICATION

[-> ActivityThread.java ::H]

當主執行緒收到H.BIND_APPLICATION,則呼叫handleBindApplication

private void handleBindApplication(AppBindData data) {

    mBoundApplication = data;
    mConfiguration = new Configuration(data.config);
    mCompatConfiguration = new Configuration(data.config);

    ...

    //設定程式名, 也就是說程式名是在程式真正建立以後的BIND_APPLICATION過程中才取名
    Process.setArgV0(data.processName);
    android.ddm.DdmHandleAppName.setAppName(data.processName, UserHandle.myUserId());

    if (data.persistent) {
        //低記憶體裝置, persistent程式不採用硬體加速繪製,以節省記憶體使用量
        if (!ActivityManager.isHighEndGfx()) {
            HardwareRenderer.disable(false);
        }
    }

    //重置時區
    TimeZone.setDefault(null);
    Locale.setDefault(data.config.locale);

    //更新系統配置
    mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
    mCurDefaultDisplayDpi = data.config.densityDpi;
    applyCompatConfiguration(mCurDefaultDisplayDpi);

    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
    ...

    // 建立ContextImpl上下文
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
    if (!Process.isIsolated()) {
        final File cacheDir = appContext.getCacheDir();
        if (cacheDir != null) {
            System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
        }

        //用於儲存產生/編譯的圖形程式碼
        final File codeCacheDir = appContext.getCodeCacheDir();
        if (codeCacheDir != null) {
            setupGraphicsSupport(data.info, codeCacheDir);
        }
    }

    final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));
    DateFormat.set24HourTimePref(is24Hr);

    View.mDebugViewAttributes =  mCoreSettings.getInt(Settings.Global.DEBUG_VIEW_ATTRIBUTES, 0) != 0;
    ...

    //當處於除錯模式,則執行應用生成systrace資訊
    boolean appTracingAllowed = (data.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0;
    Trace.setAppTracingAllowed(appTracingAllowed);

    //初始化 預設的http代理
    IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
    if (b != null) {
        IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
        final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
        Proxy.setHttpProxySystemProperty(proxyInfo);
    }

    if (data.instrumentationName != null) {
        InstrumentationInfo ii = null;
        ii = appContext.getPackageManager().getInstrumentationInfo(data.instrumentationName, 0);

        mInstrumentationPackageName = ii.packageName;
        mInstrumentationAppDir = ii.sourceDir;
        mInstrumentationSplitAppDirs = ii.splitSourceDirs;
        mInstrumentationLibDir = ii.nativeLibraryDir;
        mInstrumentedAppDir = data.info.getAppDir();
        mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
        mInstrumentedLibDir = data.info.getLibDir();

        ApplicationInfo instrApp = new ApplicationInfo();
        instrApp.packageName = ii.packageName;
        instrApp.sourceDir = ii.sourceDir;
        instrApp.publicSourceDir = ii.publicSourceDir;
        instrApp.splitSourceDirs = ii.splitSourceDirs;
        instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
        instrApp.dataDir = ii.dataDir;
        instrApp.nativeLibraryDir = ii.nativeLibraryDir;
        LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                appContext.getClassLoader(), false, true, false);
        ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

        java.lang.ClassLoader cl = instrContext.getClassLoader();
        mInstrumentation = (Instrumentation)cl.loadClass(data.instrumentationName.getClassName()).newInstance();

        mInstrumentation.init(this, instrContext, appContext,
               new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
               data.instrumentationUiAutomationConnection);
        ...
    } else {
        mInstrumentation = new Instrumentation();
    }

    //FLAG_LARGE_HEAP則清除記憶體增長上限
    if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
        dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
    } else {
        dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();
    }

    try {
        // 通過反射,建立目標應用Application物件,即在AndroidManifest.xml檔案定義的應用名
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);
        mInitialApplication = app;

        if (!data.restrictedBackupMode) {
            List<ProviderInfo> providers = data.providers;
            if (providers != null) {
                installContentProviders(app, providers);
                mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
            }
        }

        mInstrumentation.onCreate(data.instrumentationArgs);
        //呼叫Application.onCreate()回撥方法.
        mInstrumentation.callApplicationOnCreate(app);

    } finally {
        StrictMode.setThreadPolicy(savedPolicy);
    }
}

小節: 到此程式啟動的全過程基本介紹完, 那接下來程式該往哪執行呢, 那就是要繼續看[見流程3.8] AMS.attachApplicationLocked.從[3.9 ~ 3.13] 只是介紹了bindApplication過程, 該方法之後便是元件啟動相關的內容,本文主要將程式相關內容, 元件的內容後續還會再進一步介紹.

四. 總結

本文首先介紹AMS的4個同名不同引數的方法startProcessLocked; 緊接著講述了四大元件與程式的關係, Activity, Service, ContentProvider, BroadcastReceiver這四大元件,在啟動的過程,當其所承載的程式不存在時需要先建立程式. 再然後進入重點以startProcessLocked以引線一路講解整個過程所遇到的核心方法. 在整個過程中有新建立的程式與system_server程式之間的互動過程 是通過binder進行通訊的, 這裡有兩條binder通道分別為AMP/AMN 和 ATP/ATN.

start_process

上圖便是一次完整的程式建立過程,app的任何元件需要有一個承載其執行的容器,那就是程式, 那麼程式的建立過程都是由系統程式system_server通過socket向zygote程式來請求fork()新程式, 當建立出來的app process與system_server程式之間的通訊便是通過binder IPC機制.

相關文章