【拒絕一問就懵】之Activity的啓動流程



背景介紹

從事開發到了必定階段,想要提升就必須搞明白系統的一些工做原理。爲何?由於只有明白了這些,你才能針對平臺的特性寫出優質的代碼。當遇到棘手的問題時,你才能更快速的結合系統原理去尋找最優解決方案。底層基礎決定上層建築。這個原理在開發中一樣適用。我是提倡 迴歸基礎 的。高級的功能老是由最基本的元件構成,就比如爲數很少的元素構成了咱們不可思議的豐富的物質世界同樣。只有掌握了最根本的內容,才能促使你爆發出不可思議的創造力來!html

重視基礎,迴歸基礎。回到最初,去探尋靈感。 願與君共勉✌️!java

一張圖明白Activity的啓動流程



本篇主要講的是從一個App啓動,到Activity執行onCreate()的流程。後面關於Activity的生命週期相信你們基本都耳熟能詳了。

上圖中我把涉及到的類名方法命均列出來了,你能夠看着流程,打開源碼跟着過一遍。相信在過完一遍以後,在從此的開發中你會更加自信!android

上圖乍一看可能感受有些眼花繚亂,但請不要害怕。其實根本就沒什麼東西,你只須要從藍色箭頭開始看下去,會發現一下就看完了。在結合下面簡要的分析,3分鐘內你就能搞明白Activity的啓動流程。bash

關於Activity的啓動,我在《【拒絕一問就懵】之從Thread講到Handle》https://juejin.im/post/5cdc09aa6fb9a031f80dff23 一文中有提到過。這篇文章主要講的是Thread線程究竟是個什麼東西,以及Android中的消息機制。感興趣能夠點連接看一看。
app

一切從main()方法開始

Android中,一個應用程序的開始能夠說就是從ActivityThread.java中的main()方法開始的。都是學過Java的人,想必也都知道Java的程序入口就是main()方法。從這點而言,咱們能夠把它想成是一個Java程序(注意,不是說Android是個Java程序哦)去理解。框架

從上圖能夠看到,main()方法中主要作的事情有:ide

  1. 初始化主線程的Looper、主Handler。並使主線程進入等待接收Message消息的無限循環狀態。關於Android的Handler機制,能夠參考一下我上面提到的文章:
    《【拒絕一問就懵】之從Thread講到Handle》https://juejin.im/post/5cdc09aa6fb9a031f80dff23
    下面是main()方法中比較關鍵的代碼:
public static void main(String[] args){
    ...
    Looper.prepareMainLooper(); 
    //初始化Looper
    ...
    ActivityThread thread = new ActivityThread();
    //實例化一個ActivityThread
    thread.attach(false);
    //這個方法最後就是爲了發送出建立Application的消息
    ... 
    Looper.loop();
    //主線程進入無限循環狀態,等待接收消息
}

複製代碼

2.調用attach()方法,主要就是爲了發送出初始化Application的消息。這個流程說長不長,說短不短。下文會再捋一捋。函數

建立Application的消息是如何發送的呢?

上面提到過,ActivityThread的attach()方法最終的目的是發送出一條建立Application的消息——H.BIND_APPLICATION,到主線程的主Handler中。那咱們來看看attach()方法幹了啥。
attach()關鍵代碼:oop

public void attach(boolean system){
    ...
    final IActivityManager mgr = ActivityManagerNative.getDefault();  
    //得到IActivityManager實例,下面會看看它是個啥
    try {
        mgr.attachApplication(mAppThread);
         //看見沒?關鍵啊。mAppThread這個參數下面也會說一下
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    ...
}

複製代碼

莫慌莫慌,下面看看上面出現的兩個對象是個啥。post

IActivityManager mgr是個啥?

從上圖也能夠看到,IActivityManager是一個接口,當咱們調用ActivityManagerNative.getDefault()得到的實際是一個代理類的實例——ActivityManagerProxy,這個東西實現了IActivityManager接口。打開源碼你會發現,ActivityManagerProxy是ActivityManagerNative的一個內部類。能夠看出,Android團隊在設計的過程當中是實踐了最小驚異原則的,就是把相關的東西儘可能放在一塊兒。那麼既然是個代理類,它究竟代理了誰?代碼裏看看嘍。
下面這個代碼稍微有點繞啊!老哥,穩住!

  1. 先看ActivityManagerProxy的構造函數:
public ActivityManagerProxy(IBinder remote) {
        mRemote = remote;
}
複製代碼

這個構造函數很是的簡單。首先它須要一個IBinder參數,而後賦值給mRemote變量。這個mRemote顯然是ActivityManagerProxy的成員變量,對它的操做是由ActivityManagerProxy來代理間接進行的。這樣設計的好處是保護了mRemote,而且可以在操做mRemote前執行一些別的事務,而且咱們是以IActivityManager的身份來進行這些操做的!這就很是巧妙了。

  1. 那麼這個構造函數是在那調用的呢?
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);
    //這個地方調用了構造函數
}
複製代碼

上面這個方法是ActivityManagerNative中的一個靜態方法,它會調用到ActivityManagerProxy的構造方法。然而,這個靜態方法也須要一個IBinder做爲參數!老夫被繞暈了。可是不怕,我們繼續往找!

  1. getDefault()獲取到的靜態常量gDefault
private static final Singleton<IActivityManager> gDefault = 
  new Singleton<IActivityManager>() {
    protected IActivityManager create() {
       IBinder b = ServiceManager.getService("activity");
       //重點啊!IBinder實例就是在這裏得到的。
        ...
        IActivityManager am = asInterface(b);
        //調用了上面的方法。
        ...
        return am;
    }
};
複製代碼

這是ActivityManagerNative的靜態常量,它是一個單例。在其中終於得到了前面一直在用的IBinder實例。

IBinder b = ServiceManager.getService("activity");
複製代碼

試着在上圖中找到對應位置。

這裏是經過ServiceManager獲取到IBinder實例的。若是你之前瞭解AIDL通信流程的話。這可能比較好理解一點,這只是經過另外一種方式獲取IBinder實例罷了。獲取IBinder的目的就是爲了經過這個IBinderActivityManager進行通信,進而ActivityManager會調度發送H.BIND_APPLICATION即初始化Application的Message消息。若是以前沒接觸過Binder機制的話,只需知道這個目的就好了。我後面會寫一篇專門介紹Android中Binder機制的文章。固然,你也能夠參考一下羅大的系列文章,寫的很詳細,很是的很贊!【Android系統進程間通訊Binder機制在應用程序框架層的Java接口源代碼分析
】http://m.blog.csdn.net/article/details?id=6642463

  1. 再來看看attachApplication(mAppThread)方法。
public void attachApplication(IApplicationThread app){
  ...
  mRemote.transact(ATTACH_APPLICATION_TRANSACTION, data, reply, 0);  
  ...
}
複製代碼

這個方法我在上圖中也體現出來了。

這個方法中上面這一句是關鍵。調用了IBinder實例的tansact()方法,而且把參數app(這個參數稍後就會提到)放到了data中,最終傳遞給ActivityManager。

如今,咱們已經基本知道了IActivityManager是個什麼東東了。其實最重要的就是它的一個實現類ActivityManagerProxy,它主要代理了內核中與ActivityManager通信的Binder實例。下面再看看ApplicationThread mAppThread

ApplicationThread mAppThread又是個啥?

  1. 在ActivityThread的成員變量中,你可以發現:
final ApplicationThread mAppThread = new ApplicationThread();
複製代碼

ApplicationThread是做爲ActivityThread中的一個常量出現的。這代表系統不但願這個變量中途被修改,可見這個變量具備特定而十分重要的做用。

  1. 咱們看看他是啥。
private class ApplicationThread extends ApplicationThreadNative{
    ...
}
複製代碼

ApplicationThread是ActivityThread中的一個內部類,爲何沒有單獨出來寫在別的地方呢?我以爲這也是對最小驚異原則的實踐。由於ApplicationThread是專門真對這裏使用的對象。

  1. 它繼承自ApplicationThreadNative,咱們再看看它是個啥。
public abstract class ApplicationThreadNative extends Binder implements IApplicationThread{
    ...
    //無參構造函數
    public ApplicationThreadNative() {
        //這是Binder的
        attachInterface(this, descriptor);
    }
    ...
}
複製代碼

那麼很明顯,ApplicationThread最終也是一個Binder!同時,因爲實現了IApplicationThread接口,因此它也是一個IApplicationThread。以上這系對應關係你均可以在上圖中找到。

咱們在ActivityThread中看到的ApplicationThread使用的構造函數是無參的,因此看上面無參構造函數都幹了啥!

Binder的attachInterface(IInterface owner, String descriptor)方法沒什麼特別的,就是賦值了。

public void attachInterface(IInterface owner, String descriptor) {
    mOwner = owner;
    mDescriptor = descriptor;
}
複製代碼

4.那麼IApplicationThread又是啥?老鐵,走着!咱們繼續挖。

public interface IApplicationThread extends IInterface {
    ...
    String descriptor = "android.app.IApplicationThread"; 
    //留意下這個參數
    ...
}
複製代碼

好吧,這在上圖中沒有,挖的有點什麼了。可是學習嘛,咱就看看嘍。

IApplicationThread是繼承了IInterface的一個接口,咱們須要關注一下里面的descriptor參數。後面會用它,它是一個標識,查詢的時候很重要。

好,咱們終於知道attach()方法中出現的兩個對象是啥了。ApplicationThread做爲IApplicationThread的一個實例,承擔了最後發送Activity生命週期、及其它一些消息的任務。也就是說,前面繞了一大圈,最後仍是回到這個地方來發送消息。我擦!

也許你會想,既然在ActivityThread中咱們已經建立出了ApllicationThread的了,爲何還要繞這麼彎路?,固然是爲了讓系統根據狀況來控制這個過程嘍,否則爲何要把ApplicationThread傳到ActivityManager中呢?

ActivityManagerService調度發送初始化消息

通過上面的展轉,ApplicationThread終於到了ActivityManagerService中了。請在上圖中找到對應位置!

從上圖中能夠看到,ActivityManagerService中有一這樣的方法:

private final boolean attachApplicationLocked(IApplicationThread thread , int pid) {
    ...
    thread.bindApplication();
    //注意啦!
    ...
}
複製代碼

ApplicationThread以IApplicationThread的身份到了ActivityManagerService中,通過一系列的操做,最終被調用了本身的bindApplication()方法,發出初始化Applicationd的消息。

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<String, IBinder> services, Bundle coreSettings){
    
    ...
    sendMessage(H.BIND_APPLICATION, data);
}
複製代碼

嚇屎老紙!這麼多參數。這明明很違反參數儘可能要少的原則嘛!因此說,有的時候,開發過程當中仍是很難避免一些參數堆積的狀況的。也不能一律而論。

可是,這個地方,咱們只要知道最後發了一條H.BIND_APPLICATION消息,接着程序開始了。

收到初始化消息以後的世界

上面咱們已經找到初始化Applicaitond的消息是在哪發送的了。如今,須要看一看收到消息後都發生了些什麼。

如今上圖的H下面找到第一個消息:H.BIND_APPLICATION。一旦接收到這個消息就開始建立Application了。這個過程是在handleBindApplication()中完成的。看看這個方法。在上圖中能夠看到對應的方法。

private void handleBindApplication(AppBindData data) {
    ...
    mInstrumentation = (Instrumentation)
        cl.loadClass(data.instrumentationName.getClassName())
        .newInstance();
    //經過反射初始化一個Instrumentation儀表。後面會介紹。
    ...
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    //經過LoadedApp命令建立Application實例
    mInitialApplication = app;
    ...
    mInstrumentation.callApplicationOnCreate(app);
    //讓儀器調用Application的onCreate()方法
    ...
}
複製代碼

handleBindApplication()是一個很長的方法,可是我爲各位看官精選出了上面這幾句代碼。對於本篇的主題來講,他們是相當重要的。上面短短的代碼中出現了幾個新對象。下面我會一一道來。

Instrumentation儀表,什麼鬼?

1.這個叫Instrumentation儀表的東西十分詭異,姑且翻譯爲儀器吧。字面上看不出任何它是幹什麼的線索。可是,咱們能夠打開文檔看看嘍。

Instrumentation會在應用程序的任何代碼運行以前被實例化,它可以容許你監視應用程序和系統的全部交互。

大概就這個意思啦。

2.可是,從上面的代碼咱們能夠看出,Instrumentation確實是在Application初始化以前就被建立了。那麼它是如何實現監視應用程序和系統交互的呢?

打開這個類你能夠發現,最終Apllication的建立,Activity的建立,以及生命週期都會通過這個對象去執行。簡單點說,就是把這些操做包裝了一層。經過操做Instrumentation進而實現上述的功能。

3.那麼這樣作究竟有什麼好處呢?仔細想一想。Instrumentation做爲抽象,當咱們約定好須要實現的功能以後,咱們只須要給Instrumentation儀表添加這些抽象功能,而後調用就好。剩下的,無論怎麼實現這些功能,都交給Instrumentation儀器的實現對象就好。啊!這是多態的運用。啊!這是依賴抽象,不依賴具體的實踐。啊!這是上層提出需求,底層定義接口,即依賴倒置原則的踐行。呵!抽象不過如此。

從代碼中能夠看到,這裏實例化Instrumentation的方法是反射!而反射的ClassName是來自於從ActivityManagerService中傳過來的Binder的。套路太深!就是爲了隱藏具體的實現對象。可是這樣耦合性會很低。

4.好了,不瞎扯了。既然在說Instrumentation,那就看看最後調的callApplicationOnCreate()方法。

public void callApplicationOnCreate(Application app) {
    app.onCreate();
}
複製代碼

你沒看錯,它啥也沒幹。只是調用了一下Application的onCreate()方法。這就是爲何它可以起到監控的做用。

在上圖中你可以看到Instrumentation,以及它的交互過程。

LoadedApk就是data.info哦!

關於它是怎麼來的本篇就不說了,之後可能會介紹下。本篇就看流程就好。因此直接進去看它的makeApplication()幹了啥,就把Application給建立了。

public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
    ...
    String appClass = mApplicationInfo.className;
    //Application的類名。明顯是要用反射了。
    ...
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread
        , this);
    //留意下Context
    app = mActivityThread.mInstrumentation
        .newApplication( cl, appClass, appContext);
    //經過儀表建立Application
    ...
}
複製代碼

在這個方法中,咱們須要知道的就是,在取得Application的實際類名以後,最終的建立工做仍是交由Instrumentation去完成,就像前面所說的同樣。

值得留意的是,就像上圖所標註的同樣,當須要第二次獲取Application時,一樣只須要調用這個方法就好。「真是方便!」

如今把目光移回Instrumentation

看看newApplication()中是如何完成Application的建立的。

static public Application newApplication(Class<?> clazz , Context context) throws InstantiationException , IllegalAccessException , ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        //反射建立,簡單粗暴
        app.attach(context);
        //關注下這裏,Application被建立後第一個調用的方法。
        //目的是爲了綁定Context。
        return app;
    }
複製代碼

個人天,繞了這麼多,這Application可算是建立出來了。快給本身一個小紅花吧!

LaunchActivity

當Application初始化完成後,系統會更具Manifests中的配置的啓動Activity發送一個Intent去啓動相應的Activity。這個過程本篇先不提,下次再說。主要看流程!

  1. 直接的,H就收到了一條LAUNCH_ACTIVITY的消息。而後開始初始化Activity之旅。收到消息後,真正處理是在ActivityThread中的handleLaunchActivity()中進行的。是否是火燒眉毛的想要知道發生了啥?快在上圖中找到對應的步驟吧!
private void handleLaunchActivity(ActivityClientRecord r , Intent customIntent , String reason) {
    ...
    Activity a = performLaunchActivity(r, customIntent);
    //媽蛋!又封裝到另外一個方法中建立了。
    ...
    if (a != null) {
        ...
        handleResumeActivity(r.token
        , false
        , r.isForward
        ,!r.activity.mFinished && !r.startsNotResumed
        , r.lastProcessedSeq, reason);
        //Activity建立成功就往onResume()走了!
        ...
    }
}
複製代碼

從上面的代碼中能夠看出...好吧,什麼都看不出來!

  1. 再走一個方法。
private Activity performLaunchActivity(ActivityClientRecord r , Intent customIntent) {
    ...
    activity = mInstrumentation.newActivity(
         cl, component.getClassName(), r.intent);
    //經過儀表來建立Activity
    ...
     Application app = r.packageInfo.makeApplication(false
     , mInstrumentation);
     //前面說過,是在獲取Application
    ...
    activity.attach(appContext
        , this
        , getInstrumentation()
        , r.token
        ,.ident
        , app
        , r.intent
        , r.activityInfo
        , title
        , r.parent
        , r.embeddedID
        , r.lastNonConfigurationInstances
        , config
        ,r.referrer
        , r.voiceInteractor
        , window);
    //方法怪出現!
    ...
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(
          activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    //根據是否可持久化選擇onCreate()方法。
    ...
}
複製代碼

這個方法內容較多,咱們一個個看。

activity = mInstrumentation.newActivity(
         cl, component.getClassName(), r.intent);
複製代碼

正如前面所說,Activity、Application的建立及生命週期都被承包給Instrumentation儀表了。因此由它來負責。看看Instrumentation幹了啥。

public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException , IllegalAccessException, ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
        //真的沒幹啥。反射實例化Activity而已
    }
複製代碼

就是反射出一個Activity而已。

if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(
          activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
複製代碼

根據是否可持久化選擇Activity的onCreate()方法。一樣是經過Instrumentation儀表來執行onCreate()的。它兩分別對應的onCreate()方法爲:

onCreate(icicle, persistentState);
//可得到持久化數據
複製代碼

onCreate(icicle);
//平時重寫的最多的。
複製代碼

中間兩個方法留意一下就好,就不在解釋的,感興趣的點源碼看看。

到此,Activity就跑起來了!怎麼樣?是否是並不複雜。

總結

本篇就到此結束了。本篇主要流程是從Application建立開始,到第一個Activity onCreate()結束的。這了流程也不算長,關鍵是結合上面的圖來看。重點環節我都用不一樣的顏色標記出來了。

看到這裏的同窗獎勵本身一包辣條吧!

參考連接

  1. 【Android系統進程間通訊Binder機制在應用程序框架層的Java接口源代碼分析
    】http://m.blog.csdn.net/article/details?id=6642463

  2. 【Instrumentation API】https://developer.android.com/reference/android/app/Instrumentation.html

  3. 【Activity啓動流程(下)】http://www.jianshu.com/p/3cf90c633bb1

  4. 【Android學習——ActivityManager與Proxy模式的運用】http://www.cnblogs.com/bastard/archive/2012/05/25/2517522.html

  5. 【ActivityManager API】https://developer.android.com/reference/android/app/ActivityManager.html

感謝你的閱讀。若是你以爲對你有用的話,記得給CoorChice點個贊,添加下關注哦,謝謝!

相關文章
相關標籤/搜索