Android源碼之Activity從啟動到顯示到界面

前言:

在上兩篇文章中對setContentView()是如何將我們的內容視圖添加到系統提供的根視圖中以及PhoneWindow的創建等做了粗略的分析,且這個時候內容視圖還完全處于內存中 我們是還看不見的,本篇趁熱打鐵繼續跟進 將要了解Activity是如何將DecorView添加到Window中 以及是如何將DecorView繪制到屏幕上的

分析要點:

老規矩 先來張Activity啟動流程圖:


都知道系統源碼中有一個全局的Handler 不管是Activity的啟動還是Service的啟動或綁定 亦或廣播的注冊 都會由它發送消息處理,而這個Handler對消息的處理就在ActivityThread類中 本文也將從ActivityThread中著手去分析Activity啟動后生命周期是如何執行的。

源碼閱讀:

因對于startActivity()和通過Launch啟動App的啟動流程尚且不熟悉 所以這里直接跳到Activity已經開始啟動的流程著手分析 首先來到ActivityThread#handleLaunchActivity() 所有Activity的啟動都會調用這里 同時也會只撿要緊的函數看

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;
        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }
        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        // WindowManagerGlobal初始化工作 這個WindowManagerGlobal是非常重要的一個類
        WindowManagerGlobal.initialize();
        
        //Activity就是在這里創建的
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;

            //Activity的onResume() 以及內容視圖的繪制就是從這個函數開始的
            handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed);
        }
}

先看Activity是如何創建的

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
        //這里通過反射的方式創建Activity
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);

                //這里調用了activity#attach()進行PhoneWindow的創建以及一些Cllback監聽的初始化和非常重要的WindowManagerImpl初始化等操作
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                
                //Activity的onCreate()這里被調用 區別就是參數不同
                if (r.isPersistable()) {    
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                r.activity = activity;
                r.stopped = true;
                
                //Activity的onStart()在這里被調用
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                .................................
        return activity;
    }

mInstrumentation.callActivityOnCreate()Activtiy#onCreate()和Activity#onStart()

//這個Instrumentation類似于AppCompatActivity中的AppCompatDelegate一樣是一個代理類
public class Instrumentation {

public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        //onCreate()
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }
    //Activity#onStart()
public void callActivityOnStart(Activity activity) {
        activity.onStart();
    }
} 
 .................分割線................
 
// 跟進activity.performCreate(icicle)
 
class Activity{
    //一個參數
    final void performCreate(Bundle icicle) {
        restoreHasCurrentPermissionRequest(icicle);
        //執行OnonCreate()
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
    //2個參數
    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        restoreHasCurrentPermissionRequest(icicle);
        //執行OnonCreate()
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
    
     final void performStart() {
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
        mFragments.noteStateNotSaved();
        mCalled = false;
        mFragments.execPendingActions();

        //通過調用Instrumentation類的callActivityOnStart執行OnStart()
        mInstrumentation.callActivityOnStart(this);
        mFragments.dispatchStart();
        mFragments.reportLoaderStart();
        ........
    }
}

Activity創建好了 并且也執行到onStart() 再返回去看Activity的onResume()以及內容視圖的繪制過程

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // Activity#onResume()在這里執行 且此時內容還未繪制 也驗證了onResume()后視圖可見 是一個錯誤的觀念 至少需要等一會
        ActivityClientRecord r = performResumeActivity(token, clearHide);
        
        /*****************重點將從這里開始**************************/
        
       if (r.window == null && !a.mFinished && willBeVisible) {
                //獲取Activity中創建的Window對象
                r.window = r.activity.getWindow();
                
                //獲取Window中的DecorView
                View decor = r.window.getDecorView();
                
                //INVISIBLE雖然不可見 但是卻能占據window空間
                decor.setVisibility(View.INVISIBLE);
                
                //這里獲取WindowManager的實現類WindowManagerImpl這是一個很重要的類
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;

                    //這一步執行將DecorView添加到Window窗口中和針對DecorView的測量 布局 繪制操作
                    wm.addView(decor, l);
                }
            }
            
            .................... 
  }

先看Activity的onResume()是如何執行的

public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        ............
                //onResume()在這里被執行
                r.activity.performResume();
        ............
        
        return r;
    }

DecorView是如何添加到Window中的? 且WindowManagerImpl是在Activity#attach()時初始化的 就要從WindowManagerImpl類開始分析 上面也是調用的 WindowManagerImpl的addView()

public final class WindowManagerImpl implements WindowManager {

//WindowManagerGlobal
 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
 
 @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
}   

 /*******************WindowManagerGlobal************************/
public final class WindowManagerGlobal {
    //這里聲明一些集合 來緩存我們加載過的view 在下次使用的時候 見會提高程序的執行速度

    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
            
  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        
        if (parentWindow != null) { 
    parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } 
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            
            mRoots.get(index).doDie();
           
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            //這個view就是DecorView
            mViews.add(view);
            //ViewRootImpl 繪制好了View
            mRoots.add(root);
            //Window#Params
            mParams.add(wparams);
        }
        // do this last because it fires off messages to start doing things
        try {
           /*****************從這里開始 就開啟正式的繪制流程*******/
           
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
}

繼續深入 ViewRootImpl.setView()

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
             ...........................................
                //執行繪制操作
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();

                    //這里使用IPC WMS將完成窗口的添加過程 這塊我也不太下熟悉就跳過了
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
           //這句代碼也很重要 將ViewRootImpl設置為DecorView的 ViewParent                
           view.assignParent(this);

}                

requestLayout()繪制流程

@Override
public void requestLayout() {
     if (!mHandlingLayoutInLayoutRequest) {
          checkThread();
          mLayoutRequested = true;
          scheduleTraversals();
     }
 }
 
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

            //Choreographer類中有一個Handler通過發送消息執行TraversalRunnable的run()方法
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
} 

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    } 
    
 void doTraversal() {
            .........      
            performTraversals();
            ..........
    }
//到這函數 針對DecorView的測量 布局 繪制 都將完成    
private void performTraversals() {
    .............
    if (!mStopped || mReportNextDraw) {
      //確定測量模式和測量規格
       boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
       (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
       if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
       || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
       int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
       int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                    
      //將調用DecorView的Measure()
      performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
     //將調用DecorView的Layout()
     performLayout(lp, desiredWindowWidth, desiredWindowHeight);
     
     //將調用DecorView的Draw()
     performDraw();
}                             

測量 布局 繪制完成 再通過WMS完成窗口的添加過程 到此DecorView才真正顯示到我們的屏幕上了


最后再多看一眼view.assignParent(this);將ViewRootImpl設置為DecorView的 ViewParent 這樣的目的是什么

class View{

    void assignParent(ViewParent parent) {
        if (mParent == null) {
            mParent = parent;
        } else if (parent == null) {
            mParent = null;
    }  
    //當我們調用View的 requestLayout()
    @CallSuper
    public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();

        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }

        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;

        if (mParent != null && !mParent.isLayoutRequested()) {
            //將會一直獲取到父容器的 mParent.requestLayout();
            mParent.requestLayout();
        }
    }          
}

到這里就很明白了平時我們在調用View的requestLayout()進行重新測量 布局 繪制時 會一直走到 ViewRootImpl的requestLayout()函數將DecorView重新測量 布局 繪制 由此看出在不是很必要的情況下不能直接調用該函數來刷新視圖 因為這個過程是很耗CUP的。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容