摘要:在結合下面簡要的分析,分鐘內你就能搞明白的啟動流程。關于的啟動,我在驚天秘密從開始,揭露線程通訊的詭計和主線程的陰謀一文中有提到過。從上圖可以看到,方法中主要做的事情有初始化主線程的主。并使主線程進入等待接收消息的無限循環狀態。
背景介紹
從事開發到了一定階段,想要提高就必須搞明白系統的一些工作原理。為什么?因為只有明白了這些,你才能針對平臺的特性寫出優質的代碼。當遇到棘手的問題時,你才能更快速的結合系統原理去尋找最優解決方案。底層基礎決定上層建筑。這個原理在開發中同樣適用。我是提倡 回歸基礎 的。高級的功能總是由最基本的元件構成,就好比為數不多的元素構成了我們難以想象的豐富的物質世界一樣。只有掌握了最根本的內容,才能促使你爆發出難以想象的創造力來!
重視基礎,回歸基礎。回到最初,去探尋靈感。 愿與君共勉??!
一張圖明白Activity的啟動流程
本篇主要講的是從一個App啟動,到Activity執行onCreate()的流程。后面關于Activity的生命周期相信大家基本都耳熟能詳了。
上圖中我把涉及到的類名方法命均列出來了,你可以看著流程,打開源碼跟著過一遍。相信在過完一遍之后,在今后的開發中你會更加自信!
上圖乍一看可能感覺有些眼花繚亂,但請不要懼怕。其實根本就沒什么東西,你只需要從藍色箭頭開始看下去,會發現一下就看完了。在結合下面簡要的分析,3分鐘內你就能搞明白Activity的啟動流程。
關于Activity的啟動,我在【驚天秘密!從Thread開始,揭露Android線程通訊的詭計和主線程的陰謀】http://www.jianshu.com/p/8862bd2b6a29 一文中有提到過。這篇文章主要講的是Thread線程到底是個什么東西,以及Android中的消息機制。感興趣可以點鏈接看一看。
一切從main()方法開始Android中,一個應用程序的開始可以說就是從ActivityThread.java中的main()方法開始的。都是學過Java的人,想必也都知道Java的程序入口就是main()方法。從這點而言,我們可以把它想成是一個Java程序(注意,不是說Android是個Java程序哦)去理解。
從上圖可以看到,main()方法中主要做的事情有:
初始化主線程的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團隊在設計的過程中是實踐了最小驚異原則的,就是把相關的東西盡量放在一起。那么既然是個代理類,它究竟代理了誰?代碼里看看嘍。
下面這個代碼稍微有點繞啊!老哥,穩住!
先看ActivityManagerProxy的構造函數:
public ActivityManagerProxy(IBinder remote) { mRemote = remote; }
這個構造函數非常的簡單。首先它需要一個IBinder參數,然后賦值給mRemote變量。這個mRemote顯然是ActivityManagerNative的成員變量。但對它的操作是由ActivityManagerProxy來代理間接進行的。這樣設計的好處是保護了mRemote,并且能夠在操作mRemote前執行一些別的事務,并且我們是以IActivityManager的身份來進行這些操作的!這就非常巧妙了。
那么這個構造函數是在那調用的呢?
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作為參數!老夫被繞暈了。但是不怕,咱們繼續往找!
getDefault()獲取到的靜態常量gDefault
private static final SingletongDefault = new Singleton () { 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的目的就是為了通過這個IBinder和ActivityManager進行通訊,進而ActivityManager會調度發送H.BIND_APPLICATION即初始化Application的Message消息。如果之前沒接觸過Binder機制的話,只需知道這個目的就行了。我后面會寫一篇專門介紹Android中Binder機制的文章。當然,你也可以參考一下羅大的系列文章,寫的很詳細,非常的很贊![【Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
】http://m.blog.csdn.net/articl...。
再來看看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又是個啥?在ActivityThread的成員變量中,你能夠發現:
final ApplicationThread mAppThread = new ApplicationThread();
ApplicationThread是作為ActivityThread中的一個常量出現的。這表明系統不希望這個變量中途被修改,可見這個變量具有特定而十分重要的作用。
我們看看他是啥。
private class ApplicationThread extends ApplicationThreadNative{ ... }
ApplicationThread是ActivityThread中的一個內部類,為什么沒有多帶帶出來寫在別的地方呢?我覺得這也是對最小驚異原則的實踐。因為ApplicationThread是專門真對這里使用的對象。
它繼承自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, Listproviders, 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){ ... 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。這個過程本篇先不提,下次再說。主要看流程!
直接的,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()走了! ... } }
從上面的代碼中可以看出...好吧,什么都看不出來!
再走一個方法。
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()結束的。這了流程也不算長,關鍵是結合上面的圖來看。重點環節我都用不同的顏色標記出來了。
看到這里的同學獎勵自己一包辣條吧!
參考鏈接[【Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
】http://m.blog.csdn.net/articl...
【Instrumentation API】https://developer.android.com/reference/android/app/Instrumentation.html
【Activity啟動流程(下)】http://www.jianshu.com/p/3cf90c633bb1
【Android學習——ActivityManager與Proxy模式的運用】http://www.cnblogs.com/bastard/archive/2012/05/25/2517522.html
【ActivityManager API】https://developer.android.com/reference/android/app/ActivityManager.html
感謝你的閱讀。如果你覺得對你有用的話,記得給CoorChice點個贊,添加下關注哦,謝謝!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67210.html
摘要:本文從定義,作用,技術架構,安裝和使用等全方位帶你看懂。如圖中左邊紅框中和右邊的紅框中都唯一表示為同一個鏡像。最后,于開發者而言提供了一種開發環境的管理辦法,與測試人員而言保證了環境的同步,于運維人員提供了可移植的標準化部署流程。 作者丨唐文廣:騰訊工程師,負責無線研發部地圖測試。 導語:Docker,近兩年才流行起來的超輕量級虛擬機,它可以讓你輕松完成持續集成、自動交付、自動部署...
摘要:本文從定義,作用,技術架構,安裝和使用等全方位帶你看懂。最后,于開發者而言提供了一種開發環境的管理辦法,與測試人員而言保證了環境的同步,于運維人員提供了可移植的標準化部署流程。顯示上圖內容就表明安裝完成。 作者丨唐文廣:騰訊工程師,負責無線研發部地圖測試。 導語:Docker,近兩年才流行起來的超輕量級虛擬機,它可以讓你輕松完成持續集成、自動交付、自動部署,并且實現開發環境、測試環...
摘要:注意類別指示此的圖標應放入系統的應用啟動器。如果元素未使用指定圖標,則系統將使用元素中的圖標使用方法對象是對象的包裝器。主要應用于以下場景通知應用小部件定時任務使用注意事項適用于啟動的。 showImg(https://segmentfault.com/img/remote/1460000019975019?w=157&h=54); 極力推薦文章:歡迎收藏Android 干貨分享 s...
閱讀 2571·2021-11-22 09:34
閱讀 932·2021-11-19 11:34
閱讀 2801·2021-10-14 09:42
閱讀 1472·2021-09-22 15:27
閱讀 2385·2021-09-07 09:59
閱讀 1731·2021-08-27 13:13
閱讀 3432·2019-08-30 11:21
閱讀 771·2019-08-29 18:35