從源碼看Application的啓動流程

開篇廢話

開篇廢話就真的是廢話,強烈建議直接跳過。
一直看重用戶體驗的我,很想做好一款超級好用的產品,但我只是一個默默開發的開發,一切都要跟產品大佬的奇葩需求走(這裏也體諒一下產品吧,產品也是跟用戶走的,還要考慮收益等等問題)。好多好多年前就開始用CSDN了,可能是早期用戶的原因吧,博客質量不怎麼樣,但是總排名在前萬名,可以看到數字,1w+就不顯示數字了。一開始的CSDN是很醜的,沒有支持MarkDown,排版不好看,書寫也不方便,後來發現了簡書,簡書是超級好看,虐爆CSDN,所以後來的博客就都發表在簡書上面了。但是簡書畢竟太雜了,打開首頁滿屏雞湯,還是CSDN專業一點,SEO也超好。一不小心回來CSDN看了一下,發現也默認使用MarkDown了啊,排版也超好看啊,甚至比簡書好看多了,簡書那個黑色的代碼框超難看,還不能修改,還是CSDN的灰色好看,於是我又回來了!以後努力寫更多有用又好看的博客吧!儘管CSDN的博客頁各種廣告,留言排版各種難看,個人中心通知各種亂七八糟。

大致流程

詳細流程

查看源碼方法

1、直接下載源碼,之前寫了篇下載源碼的文章,不需要全部下載,只下載你需要的部分就行了,這樣就不會幾個G幾個G的了,幾百M而已,文章:超級簡單的Android源碼下載
2、直接在AndroidStudio中雙擊shift鍵搜索文件就行了。需要寫註釋的話,直接在源碼上面寫,它會彈框問你允不允許修改源碼的。推薦這種方法,可以看到基本上你想看的源碼了,那些看不到的可以結合線上看,鏈接 :http://androidxref.com

源碼分析(基於API28)

一個應用的入口就是main方法了吧,這裏直接從ActivityThreadmain方法開始看吧。

public static void main(String[] args) {
    Looper.prepareMainLooper();
    // ...
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
	// ...
    Looper.loop();
}

main方法重點就是這幾行,Looper.prepareMainLooper()Looper.loop()可以看出這些代碼就是執行在應用進程了,而且就是主線程。loop之前創建了ActivityThread對象,並調用了attach方法,跟進看一下。

private void attach(boolean system, long startSeq) {
    // ...
    final IActivityManager mgr = ActivityManager.getService();
    mgr.attachApplication(mAppThread, startSeq);
	// ...
}

ActivityManager.getService()拿到的是IActivityManager,它是ActivityManagerService的代理對象,也就是在AMS進程中了。值得注意的是這裏傳入了個mAppThread,對應的類是ApplicationThread,ActivityThread的一個內部類,後面AMS會回調此類。繼續看attachApplication,這部分代碼在ActivityManagerService裏面。大概意思就是通知AMS獲取和生成當前應用進程的相關信息,並進行綁定,以便管理。
附1:IActivityManager.aidl源碼
附2:IApplicationThread.aidl源碼

@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
	// ...
	// thread 是前面傳過來的ApplicationThread
    attachApplicationLocked(thread, callingPid, callingUid, startSeq);
    // ...
}

private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
	// ...
	// 調用應用自身的進程bindApplication
    thread.bindApplication(processName, appInfo, providers,
            app.instr.mClass,
            profilerInfo, app.instr.mArguments,
            app.instr.mWatcher,
            app.instr.mUiAutomationConnection, testMode,
            mBinderTransactionTrackingEnabled, enableTrackAllocation,
            isRestrictedBackupMode || !normalMode, app.persistent,
            new Configuration(getGlobalConfiguration()), app.compat,
            getCommonServicesLocked(app.isolated),
            mCoreSettingsObserver.getCoreSettingsLocked(),
            buildSerial, isAutofillCompatEnabled);
	// ...
	// 下面的流程是啓動Activity的了,此文先不做分析
    if (mStackSupervisor.attachApplicationLocked(app)) {...}
	// ...
}

thread仍然是前面傳過來的ApplicationThread,調用bindApplication方法通知應用進程啓動Application。我們回到ApplicationThreadbindApplication方法看看,回到了應用自身的進程了。
疑問:bindApplication應該是異步執行的,所以bindApplication和attachApplicationLocked應該是同步執行的。

public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> providers, ComponentName instrumentationName,
        ProfilerInfo profilerInfo, Bundle instrumentationArgs,
        IInstrumentationWatcher instrumentationWatcher,
        IUiAutomationConnection instrumentationUiConnection, int debugMode,
        boolean enableBinderTracking, boolean trackAllocation,
        boolean isRestrictedBackupMode, boolean persistent, Configuration config,
        CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
        String buildSerial, boolean autofillCompatibilityEnabled) {
	// ...
    AppBindData data = new AppBindData();
    // ...
    sendMessage(H.BIND_APPLICATION, data);
}

bindApplication是AMS進程遠程調用,有oneway修飾,應該是執行在子線程的,這裏通過Handler轉到主線程執行,發送了H.BIND_APPLICATION消息,繼續進入handleMessage看看。

public void handleMessage(Message msg) {
    switch (msg.what) {
        case BIND_APPLICATION:
            AppBindData data = (AppBindData)msg.obj;
            handleBindApplication(data);
            // ...
            break;
        // ...
    }
}

private void handleBindApplication(AppBindData data) {
	// ...
    mBoundApplication = data;
    // ...
    // data.info就是一個LoadedApk,雖然我也不知道是幹嘛用的
    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
    // ...
    Application app;
    app = data.info.makeApplication(data.restrictedBackupMode, null);
	// ...
}

public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
	// ...
    Application app = null;
	// 在manifest裏面配置的Application類,沒配就是默認的android.app.Application
    String appClass = mApplicationInfo.className;
    if (forceDefaultAppClass || (appClass == null)) {
        appClass = "android.app.Application";
    }
	// ...
    try {
        java.lang.ClassLoader cl = getClassLoader();
        // ...
        // 這個就是ApplicationContext
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        // 這裏創建了Application對象
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);
    } catch (Exception e) {...}
    // ...
    if (instrumentation != null) {
        try {
        	// 調用Application的onCreate方法
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {...}
    }
    // ...
}

從這裏可以看出,Application對象是用反射創建出來的。而且是在應用進程的主線程上創建的。先繼續進入InstrumentationnewApplication方法看看,再回來看instrumentation.callApplicationOnCreate(app);

public Application newApplication(ClassLoader cl, String className, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = getFactory(context.getPackageName())
            .instantiateApplication(cl, className);
    app.attach(context);
    return app;
}

final void attach(Context context) {
    attachBaseContext(context);
    // ...
}

這裏調用了attachBaseContext方法,這個方法也是常用的,比如:MultiDex.install(base)就是寫在這個方法裏面。
接下來我們回到剛纔那個instrumentation.callApplicationOnCreate(app);,instrumentation是ActivityThread的一個變量,對應的類是Instrumentation,直接進入這個類的callApplicationOnCreate方法。

public void callApplicationOnCreate(Application app) {
    app.onCreate();
}

到這裏就是我們熟悉的Application.onCreate()了,對於一個Android應用的入口就是這裏了,跑完onCreate()就會啓動Activity,或者Service等。
順便說一個初學者大多不知道的點,Application和應用不是一一對應的,如果一個應用有多個進程,那麼每個進程都會有一個Application,進程啓動的時候都會先創建Application,先跑onCreate()方法。所以初始化一些東西的時候,最好判斷一下當前是不是主進程,避免重複進行一些不必要的初始化。還有就是在應用沒起來的時候啓動Service或者收到廣播,都會先創建Application,再做其它的事情。

寫在最後

以前看源碼總是亂七八糟的,跳來跳去,還沒跟到最後就跟丟了,就算勉強看完整個流程了,過兩天又忘記了,再次看依然要花很大的力氣,一直都在心裏罵谷歌的工程師寫的都是什麼破代碼,後來發現用時序圖把源碼流程畫出來之後清晰多了,一目瞭然,需要的時候看一下圖就清楚了,瞭解某一個點的細節直接從指定位置開始看源碼就行。時序圖,以後你就是我女朋友了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章