1. 程式人生 > >3分鐘看懂Activity啟動流程

3分鐘看懂Activity啟動流程

Android

背景介紹

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

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

一張圖明白Activity的啟動流程

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

上圖中我把涉及到的類名方法命均列出來了,你可以看著流程,開啟原始碼跟著過一遍。相信在過完一遍之後,在今後的開發中你會更加自信!

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

一切從main()方法開始

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

從上圖可以看到,main()方法中主要做的事情有:
1. 初始化主執行緒的Looper、主Handler。並使主執行緒進入等待接收Message訊息的無限迴圈狀態。關於Android的Handler機制,可以參考一下我上面提到的文章:
【驚天祕密!從Thread開始,揭露Android執行緒通訊的詭計和主執行緒的陰謀】http://www.jianshu.com/p/8862bd2b6a29
下面是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()關鍵程式碼:

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

莫慌莫慌,下面看看上面出現的兩個物件是個啥。

IActivityManager mgr是個啥?

從上圖也可以看到,IActivityManager是一個介面,當我們呼叫ActivityManagerNative.getDefault()獲得的實際是一個代理類的例項——ActivityManagerProxy,這個東西實現了IActivityManager介面。開啟原始碼你會發現,ActivityManagerProxy是ActivityManagerNative的一個內部類。可以看出,Android團隊在設計的過程中是實踐了最小驚異原則的,就是把相關的東西儘量放在一起。那麼既然是個代理類,它究竟代理了誰?程式碼裡看看嘍。
下面這個程式碼稍微有點繞啊!老哥,穩住!
1. 先看ActivityManagerProxy的建構函式:

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

這個建構函式非常的簡單。首先它需要一個IBinder引數,然後賦值給mRemote變數。這個mRemote顯然是ActivityManagerNative的成員變數。但對它的操作是由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作為引數!老夫被繞暈了。但是不怕,咱們繼續往找!
3. 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中的一個常量出現的。這表明系統不希望這個變數中途被修改,可見這個變數具有特定而十分重要的作用。
2. 我們看看他是啥。

private class ApplicationThread extends ApplicationThreadNative{
    ...
}

ApplicationThread是ActivityThread中的一個內部類,為什麼沒有單獨出來寫在別的地方呢?我覺得這也是對最小驚異原則的實踐。因為ApplicationThread是專門真對這裡使用的物件。
3. 它繼承自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()走了!
        ...
    }
}

從上面的程式碼中可以看出…好吧,什麼都看不出來!
2. 再走一個方法。

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()方法。
    ...
}

這個方法內容較多,我們一個個看。
1.

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而已。

    2.
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()結束的。這了流程也不算長,關鍵是結合上面的圖來看。重點環節我都用不同的顏色標記出來了。

看到這裡的同學獎勵自己一包辣條吧!

參考連結

感謝你的閱讀。如果你覺得對你有用的話,記得給CoorChice點個贊,新增下關注哦,謝謝!