Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中Activity啟動過程探究

Android中Activity啟動過程探究

編輯:關於Android編程

android.app.ActivityThread.performLaunchActivity()   以下是部分源碼,我做了一些省略。   復制代碼  1 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {  2       3     ...  4     Activity activity = null;  5     try {  6         java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  7         activity = mInstrumentation.newActivity(  8                 cl, component.getClassName(), r.intent);  9         StrictMode.incrementExpectedActivityCount(activity.getClass()); 10         r.intent.setExtrasClassLoader(cl); 11         if (r.state != null) { 12             r.state.setClassLoader(cl); 13         } 14     } catch (Exception e) { 15         if (!mInstrumentation.onException(activity, e)) { 16             throw new RuntimeException( 17                 "Unable to instantiate activity " + component 18                 + ": " + e.toString(), e); 19         } 20     } 21  22     try { 23         ... 24         if (activity != null) { 25             ... 26              27             activity.attach(appContext, this, getInstrumentation(), r.token, 28                     r.ident, app, r.intent, r.activityInfo, title, r.parent, 29                     r.embeddedID, r.lastNonConfigurationInstances, config); 30             ... 31             activity.mCalled = false; 32             mInstrumentation.callActivityOnCreate(activity, r.state); 33             ... 34         } 35         ... 36     } 37     ... 38 } 復制代碼 重點關注紅色加粗的部分:   7, 8行:通過Activity的類名構建一個Activity對象。   27行:調用了Activity.attach()方法。   32行:通過Instrumentation對象執行Activity的onCreate()方法,Activity的生命周期方法都是由Instrumentation對象來調用的。   其中attach()方法裡面也做了很重要的事情。       android.app.Activity.attach()   復制代碼  1 final void attach(Context context, ActivityThread aThread,  2             Instrumentation instr, IBinder token, int ident,  3             Application application, Intent intent, ActivityInfo info,  4             CharSequence title, Activity parent, String id,  5             NonConfigurationInstances lastNonConfigurationInstances,  6             Configuration config) {  7     attachBaseContext(context);  8   9     mFragments.attachActivity(this, mContainer, null); 10      11     mWindow = PolicyManager.makeNewWindow(this); 12      13     ... //將各種參數賦給Activity的成員變量 14  15     mWindow.setWindowManager( 16             (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), 17             mToken, mComponent.flattenToString(), 18             (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); 19     if (mParent != null) { 20         mWindow.setContainer(mParent.getWindow()); 21     } 22     mWindowManager = mWindow.getWindowManager(); 23     mCurrentConfig = config; 24 } 復制代碼 首先給Activity.mWindow成員變量賦值,然後給mWindow變量設置WindowManager,然後給Activity.mWindowManager賦值。   mWindow是一個Window類型的變量,但實際上它是一個PhoneWindow對象,與Activity的內容顯示相關。       上面的attach()方法調用完成後,就自然而然的調用了Activity的onCreate()方法了。   按一般的情況,Activity中的onCreate()方法調用了setContentView()方法,而setContentView()方法並不是由Activity實現的,以下是android.app.Activity中的一段代碼:   1 public void setContentView(View view, ViewGroup.LayoutParams params) { 2     getWindow().setContentView(view, params); 3     initActionBar(); 4 } 而getWindow()返回的是一個android.app.Window對象,這個對象就是剛剛在attach()中賦值的mWindow成員變量。   android.app.Window是一個抽象類,其中setContentView()方法並沒有具體的實現,而這個方法的真正實現是com.android.internal.policy.impl.PhoneWindow類。使用類圖表示:           以下是PhoneWindow中的setContentView()的代碼。   復制代碼  1 @Override  2 public void setContentView(View view, ViewGroup.LayoutParams params) {  3     if (mContentParent == null) {  4         installDecor();  5     } else {  6         mContentParent.removeAllViews();  7     }  8     mContentParent.addView(view, params);  9     final Callback cb = getCallback(); 10     if (cb != null && !isDestroyed()) { 11         cb.onContentChanged(); 12     } 13 } 復制代碼 PhoneWindow類中有兩個和視圖相關的成員變量,一個是DecorView mDecor,另一個是ViewGroup mContentParent。   來看官方文檔的描述:       再回到PhoneWindow.setContentView(View, ViewGroup.LayoutParams)中的installDecor()方法做了什麼。   復制代碼  1 private void installDecor() {  2     if (mDecor == null) {  3         mDecor = generateDecor();  4         ...  5     }  6     if (mContentParent == null) {  7         mContentParent = generateLayout(mDecor);  8         ...  9     } 10 } 復制代碼 mDecor是通過一個generateDecor()方法來獲取的。而generateDecor()就是new了一個DecorView而已。   1 protected DecorView generateDecor() { 2     return new DecorView(getContext(), -1); 3 } DecorView是PhoneWindow類中定義的一個內部類,它繼承了FrameLayout,用來作為整個PhoneWindow的根視圖。   再來看generateLayout()做了什麼事情。   復制代碼  1 protected ViewGroup generateLayout(DecorView decor) {  2   3     //...以上省去,大致上是與樣式,主題,版本相關的對視圖的設置。  4       5     //以下開始填充decor  6       7     // Inflate the window decor.  8     int layoutResource;    //這個是用來inflate的id  9      10     ...    //這裡省去,內容是根據Window的各種特性(feature)選擇一個合適的視圖id賦給layoutResource 11  12     mDecor.startChanging(); 13  14     View in = mLayoutInflater.inflate(layoutResource, null); 15     decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 16  17     ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);    //注意這個地方 18     if (contentParent == null) { 19         throw new RuntimeException("Window couldn't find content container view"); 20     } 21      22     ... //省去,內容是設置背景,設置ActionBar的一些屬性。 23  24     mDecor.finishChanging(); 25  26     return contentParent; 27 } 復制代碼 可以看出,這個方法(generateLayout())做了如下幾件比較關鍵的事情:   1.根據各種FLAG值設置了LayoutParam參數,以上代碼省略了這部分。   2.根據各種FEATURE值,選擇了一個合適的類似於R.layout.something這樣的布局id賦值給layoutResource。   3.將layoutResource填充為一個View對象,加入到DecorView中。   4.【不能確定,猜測】layoutResource所指示的布局文件中有一個id為ID_ANDROID_CONTENT的ViewGroup對象,上述代碼中第17行,源碼“非常自信”的findViewById獲取到這個ViewGroup。   5.最後將contentParent返回給PhoneWindow的成員變量mContentParent。       這樣,我們就知道了成員變量mDecor、mContentParent的來歷。在學習的過程中,我還同時了解到,mDecor管理著一個ActionBar。   綜合以上的探究,加上自己的一些思考和猜測。對PhoneWindow做一下小小的總結:   1.一個Activity對應著一個PhoneWindow對象,是一對一的關系,如果從Activity A啟動到Activity B,那麼Activity B會創建一個自己的PhoneWindow對象。   2.PhoneWindow管理著整個屏幕的內容,不包括屏幕最頂部的系統狀態條。所以,PhoneWindow或者Window是與應用的一個頁面所相關聯。   3.PhoneWindow同時管理著ActionBar和下面的內容主題,setContentView()方法是用來設置內容主體的,而setTitle()等其他方法就是操作ActionBar的,Window中定義的requestFeature()等方法,有很多與ActionBar屬性相關的設置。另外這些方法都是公有方法,顯然是為了給客戶端程序員調用的,也進一步佐證了這些操作的意義與作用。   4.PhoneWindow自己並不是一個視圖(View),它的成員變量mDecor才是整個界面的視圖,mDecor是在generateLayout()的時候被填充出來的,而actionBar和contentParent兩個視圖都是通過findViewById()直接從mDecor中獲取出來的。       以上講了這麼多,其實只是把installDector()方法給執行完了。接下來是mContentParent.removeAllViews()。這個好理解,如果setContentView()被調用兩次,第二次肯定要把裡面的內容都給清空移除了。移除後就是添加,mContentParent.addView(view, params)。       這個方法是ViewGroup中的一個方法,貼上源代碼:       首先調用的是requestLayout(),這裡的這個方法呢實際上沒有起到作用的。   ViewGourp.requestLayout()實際上是調用的View.requestLayout()方法,而View.requestLayout()方法中,除去做了一些View本身的操作外,實際上調用的是View中的mParent成員變量的requestLayout()方法,而mParent這個成員變量是一個ViewParent類型的對象,ViewParent是一個接口,View中的mParent對象是通過View.assignParent(ViewParent)方法來賦值的,而assignParent()方法是由ViewRootImpl.setView()方法調用的……暫時就不考慮它了,要明確的一點,就是requestLayout()並沒有起到具體的作用。   接下來觀察addViewInner()方法,這個方法其實就是將child加入到自己的一個View數組中保存起來,然後在把這個child的parent標記為自己。   到此為止,setContentView()方法基本就執行完畢了,這個時候界面還沒有顯示出任何東西來,而僅僅是將mDecor->mContentParent->(customer layout)一個這樣的樹狀結構給搭建好了而已。       假設setContentView()方法是onCreate()方法中唯一一個方法調用的話,那麼onCreate()方法也執行完了,調用棧繼續回退,就回到了android.app.ActivityThread.handleLaunchActivity()中,以上的所以就是剛剛執行完了android.app.ActivityThread.performLaunchActivity()。   接下來執行第二個關鍵性的方法handleResumeActivity()。       android.app.ActivityThread.handleResumeActivity()   貼上省略後的代碼:   復制代碼  1 final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,  2         boolean reallyResume) {  3     ...  4   5     ActivityClientRecord r = performResumeActivity(token, clearHide);  6   7     if (r != null) {  8         final Activity a = r.activity;  9         ... 10         if (r.window == null && !a.mFinished && willBeVisible) { 11             r.window = r.activity.getWindow(); 12             View decor = r.window.getDecorView(); 13             decor.setVisibility(View.INVISIBLE); 14             ViewManager wm = a.getWindowManager(); 15             WindowManager.LayoutParams l = r.window.getAttributes(); 16             a.mDecor = decor; 17             l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 18             l.softInputMode |= forwardBit; 19             if (a.mVisibleFromClient) { 20                 a.mWindowAdded = true; 21                 wm.addView(decor, l); 22             } 23             ... 24         } 25     ... 26     } 27 } 復制代碼 注意紅色加粗的部分:   首先是performResumeActivity()方法,這個方法內就是通過Instrumentation調用Activity的onResume()方法。   下面的wm.addView()方法非常關鍵,wm是上面a.getWindowManager()獲取到的,a是Activity,getWindowManager()就是返回它的mWindowManger對象,而這個對象是WindowManagerImpl,它的內部方法大部分是代理的WindowManagerGlobal,這個在上面的內容中已經提到了。   然而,這個WindowManger的addView()是干了什麼呢?   復制代碼  1 public void addView(View view, ViewGroup.LayoutParams params,  2         Display display, Window parentWindow) {  3     ...  4     ViewRootImpl root;  5     View panelParentView = null;  6   7     ...  8         root = new ViewRootImpl(view.getContext(), display);  9  10         view.setLayoutParams(wparams); 11  12         mViews.add(view); 13         mRoots.add(root); 14         mParams.add(wparams); 15     } 16  17     // do this last because it fires off messages to start doing things 18     try { 19         root.setView(view, wparams, panelParentView); 20     } catch (RuntimeException e) { 21         ... 22         throw e; 23     } 24 } 復制代碼 從上面的代碼可以看出,addView方法中,new了一個ViewRootImpl對象,然後調用ViewRootImpl.setView()方法。       android.view.ViewRootImpl.setView()   復制代碼  1 /**  2  * We have one child  3  */  4 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {  5     synchronized (this) {  6         if (mView == null) {  7             mView = view;  8             ...  9  10             // Schedule the first layout -before- adding to the window 11             // manager, to make sure we do the relayout before receiving 12             // any other events from the system. 13             requestLayout(); 14              15             ... 16  17             view.assignParent(this); 18             ... 19         } 20     } 21 } 復制代碼 省略後的代碼如上所示,首先將傳進來的參數view賦值給mView,這裡有一點要明確ViewRootImpl其實並不是一個View的子類……因此我認為,mView將是這個對象所認識的root節點,也是整個Activity的root的節點。   接下來調用了requestLayout()方法,這個方法是有效的!   最後將view的父親注冊為自己。終於終於,mDecor知道了自己父親是誰,或者說,整個Activity設置了一個根節點,在此之前,我們setContentView()將自己的layout布局add到PhoneWindow.mContentParent的時候,mDecor都不知道自己的parent是哪個,現在整個view的樹形結構中有了根節點,也就是ViewRootImpl,那麼requestLayout()就有效了,就可以進行後面的measure,layout,draw三步操作了。       android.view.ViewRootImpl.requestLayout()       該方法首先檢查了是否在主線程,然後就執行了scheduleTraversals()方法。看這個方法的名字,就知道是執行一次遍歷,遍歷的對象就是根節點開始的view tree。       android.view.ViewRootImpl.scheduleTraversals()       注意標記出來的Runnable對象,這個Runnable的run()方法中,調用了doTraversal()方法。而doTraversal()方法又調用了performTraversals()方法,這個方法非常長,依次調用了performMeasure(),performLayout(),performDraw()三個方法,終於開始了控件層的測量,布局,繪制三個步驟。對於這些的探究就留到下一篇博客中好了,這篇已經寫的夠長了。       小結:   花了兩天時間在grepcode上看源代碼,感覺還是有點收獲,學習到了一些以前從未了解的東西,最大的感觸就是,只要源碼給的全,慢慢看還是可以琢磨出來的。另外,關於這些內容與實際應用之間的聯系,還有待進一步的探究和經驗的積累。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved