Android 探索Activity和Window丈探,View之間的關(guān)系

1录择、Activity,Window碗降,View是什么隘竭?

在開始之前,我們先來回憶一下我們對(duì)Activity讼渊,Window动看,View的印象;

ActivityAndroid四大組件之一精偿,也是我們最常見的頁(yè)面的宿主弧圆,通過setContentView將xml布局赋兵,解析并展示到頁(yè)面上笔咽;

Window:窗口,這是一個(gè)抽象類霹期,真正的實(shí)現(xiàn)類在PhoneWindow里叶组,用來管理View的展示以及事件的分發(fā);

ViewAndroid的視圖历造,是各種炫酷控件的最終父類甩十,維護(hù)了繪制流程以及事件的分發(fā)和處理船庇;

下面通過一張圖來了解它們的對(duì)應(yīng)關(guān)系:

image

紙上得來終覺淺,絕知此事要躬行侣监!接下來讓我們通過源碼深入看看底層實(shí)現(xiàn)吧鸭轮;

2、Activity和Window橄霉,View的關(guān)系窃爷;

(1)從上面的一張關(guān)系圖了解到ActivityWindow是包含的關(guān)系,而Window的實(shí)現(xiàn)是在PhoneWindow里面姓蜂,那么ActivityWindow的關(guān)系可以理解為ActivityPhoneWindow的關(guān)系按厘;到這里就有一個(gè)疑問了,為什么ActivityPhoneWindow是包含的關(guān)系钱慢?而不是平等的逮京,或者對(duì)稱的關(guān)系呢?

(2)要想理清它們的關(guān)系束莫,目前并沒有什么好的頭緒懒棉,但是我們可以先從Activity的來源來進(jìn)行分析,試著從Activity的來源中能否找到它們的對(duì)應(yīng)關(guān)系览绿;說到Activity的來源漓藕,我們就不得不來分析一下Activity的啟動(dòng)流程,看看Activity究竟是何方神圣挟裂!

(3)Activity的啟動(dòng)流程涉及到很多系統(tǒng)服務(wù)享钞,要完整的分析,會(huì)花上很大的篇章诀蓉,等看完Activity的啟動(dòng)流程之后栗竖,估計(jì)我們都忘了看這篇博客的目的的;為了簡(jiǎn)化流程渠啤,這里會(huì)從Activity的創(chuàng)建的方法開始講起狐肢;

Activity的創(chuàng)建是在ActivityThreadhandleLaunchActivity()方法里面,我們就從這個(gè)方法進(jìn)行分析沥曹;
在開始之前份名,我們先來看幾個(gè)問題:

  • 1、在哪里創(chuàng)建Window妓美,創(chuàng)建的Window用來干嘛的僵腺;
  • 2、Activity和Window的關(guān)系壶栋;
  • 3辰如、Activity和View的關(guān)系;

源碼分析:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        ...
       
        // Initialize before creating the activity
        // 初始化WindowManagerService
        WindowManagerGlobal.initialize();
        
        //step1:
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            ...
            // step2:
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
            ...
        } else {
            ...
        }
    }

這里面主要分為兩步操作贵试,第一步是調(diào)用performLaunchActivity方法琉兜,第二步是調(diào)用handleResumeActivity方法凯正,先來看一下performLaunchActivity方法;

2.1. performLaunchActivity方法解析:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

        ...
        // Step1:
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            // Step2:
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            ...
        } catch (Exception e) {
            ...
        }

        try {
            // Step3:
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
          ...
            if (activity != null) {
                ...
                // Step4:
                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, window, r.configCallback);

                ...

                activity.mCalled = false;
                // Step5:
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ...
                }
            }
            r.paused = true;

            mActivities.put(r.token, r);

        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
           ...
        }

        return activity;
    }
    

Step1:

通過調(diào)用createBaseContextForActivity創(chuàng)建Activity的上下文Context豌蟋,而Context是一個(gè)抽象類廊散,具體的實(shí)現(xiàn)是在ContextImpl里面;

Step2:

通過調(diào)用mInstrumentationnewActivity來創(chuàng)建Activity的實(shí)例梧疲,里面是通過反射的方式進(jìn)行創(chuàng)建的奸汇;Instrumentation這個(gè)類底層實(shí)現(xiàn)是代理模式,用戶代理Activity的各種生命周期的操作往声;

image

newActivity方法里通過工廠模式來創(chuàng)建Activity的實(shí)例擂找;

image

最終通過ClassLoader來創(chuàng)建Activity的實(shí)例;

Step3:

通過調(diào)用LoadedApkmakeApplication方法來創(chuàng)建全局的上下文Application

Step4:

調(diào)用Activityattach來進(jìn)行初始化浩销,將之前創(chuàng)建的上下文傳進(jìn)去贯涎;

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
        // 將Context賦值給Activity
        attachBaseContext(context);
        
        // 將fragment添加到host
        mFragments.attachHost(null /*parent*/);

        // 創(chuàng)建PhoneWindo的實(shí)例
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        ...
        
        // 給Window設(shè)置管理器,通過系統(tǒng)服務(wù)獲取的管理器
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        ...
    }

這里面做的主要操作就是創(chuàng)建了WindowWindowManager的實(shí)例慢洋,Window的實(shí)現(xiàn)是在PhoneWindow里面塘雳,而WindowManager的實(shí)現(xiàn)是在WindowManagerImpl里面;

@Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

Step5:

當(dāng)Window創(chuàng)建完了之后普筹,就會(huì)觸發(fā)ActivityonCreate方法败明,通過代理類mInstrumentationcallActivityOnCreate方法,最終調(diào)用到ActivityonCreate方法太防;

image
image

看一下總結(jié)流程圖:

image

2.2. Activity中的onCreate方法的調(diào)用:

Step1:

ActivityonCreate方法調(diào)用了setContentView來進(jìn)行布局的加載妻顶;

image

Step2:

走了父類AppCompatActivitysetContentView方法,這里調(diào)用了getDelegate獲取實(shí)例AppCompatDelegate蜒车,AppCompatDelegate是一個(gè)抽象類讳嘱,具體實(shí)現(xiàn)是在AppCompatDelegateImpl里面;

image

Step3:

AppCompatDelegateImpl里的setContentView方法酿愧,調(diào)用了ensureSubDecor方法來創(chuàng)建DecorView沥潭,繼續(xù)跟蹤源碼往下看;

image

Step4:

這里調(diào)用了createSubDecor方法嬉挡,而這里會(huì)走到WindowsetContentView方法钝鸽;

image
image
image

到這里似乎有一些眉目了,Activity的setContentView會(huì)通過WindowsetContentView來設(shè)置布局庞钢,那么可以理解為Window管理著Activity對(duì)于View的一些相關(guān)操作拔恰;那到底是不是這樣呢,繼續(xù)跟蹤分析焊夸;

Step5:

Window的實(shí)現(xiàn)是在PhoneWindow里面仁连,來看一下PhoneWindow里的setContentView的邏輯;

image
image

這里調(diào)用了installDecor方法阱穗;

image

最終調(diào)用了generateDecor來創(chuàng)建DecorView饭冬;

image

看一下流程圖:

image

小結(jié):到這里,performLaunchActivity的方法就分析完了揪阶,這里的源碼看到創(chuàng)建了Window昌抠,并且通過PhoneWindowsetContentView來創(chuàng)建DecorView的操作;這里可以理解為Window管理著Activity關(guān)于View的一些操作鲁僚;

這里并沒有發(fā)現(xiàn)ActivityDecorView的關(guān)聯(lián)炊苫,接下來看一下handleResumeActivity方法,進(jìn)一步跟蹤看看是否有關(guān)聯(lián)冰沙;

2.3. handleResumeActivity方法解析:

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        // 通過token獲取記錄當(dāng)前Activity的信息類
        ActivityClientRecord r = mActivities.get(token);
        

        // TODO Push resumeArgs into the activity for consideration
        // Stpe1:
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            ...
            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            // 判斷是否要添加window
            // Stpe2:
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManager.getService().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                // 將創(chuàng)建Window和decorView賦值給信息記錄類
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                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) {
                    if (!a.mWindowAdded) {
                        a.mWindowAdded = true;
                        // 通過manager將decorView添加到頁(yè)面去侨艾;
                        // 具體實(shí)現(xiàn)是在
                        // Stpe3:
                        wm.addView(decor, l);
                    } else {
                        ...
                    }
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
            // 判斷window已經(jīng)被添加了,就不展示這個(gè)Window了拓挥;
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }
        ...
    }

Step1:

調(diào)用了performResumeActivity方法來觸發(fā)ActivityonResume方法唠梨;

主要是調(diào)用了ActivityperformResume方法;

@VisibleForTesting
    public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,
            String reason) {
        final ActivityClientRecord r = mActivities.get(token);
        ...
        try {
            ...
            r.activity.performResume(r.startsNotResumed, reason);
        } catch (Exception e) {
           ...
        }
        return r;
    }

來看一下performResume方法侥啤,

final void performResume(boolean followedByPause, String reason) {
        // 觸發(fā)Activity的onStart方法当叭;
        performRestart(true /* start */, reason);

        mFragments.execPendingActions();

        mLastNonConfigurationInstances = null;

        mCalled = false;
        // mResumed is set by the instrumentation
        // 通過代理類Instrumentation來回調(diào)Activity的onReusme方法;
        mInstrumentation.callActivityOnResume(this);
       
        // 回調(diào)fragment的onResume方法盖灸;
        mFragments.dispatchResume();
        mFragments.execPendingActions();

        onPostResume();
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onPostResume()");
        }
    }

這里面主要分為三步:
第一步:通過performRestart回調(diào)ActivityonStart方法蚁鳖;

image

第二步:通過代理類Instrumentation來回調(diào)ActivityonReusme方法;

image

第三步:回調(diào)fragmentonResume方法赁炎;

image

Step2:

通過系統(tǒng)服務(wù)來判斷當(dāng)前窗口如果還沒有被添加到窗口管理器中醉箕,就添加該窗口,將頁(yè)面設(shè)置為可見狀態(tài)徙垫;

Step3:

DecorView添加到WindowManager里去琅攘,ViewManager是一個(gè)抽象類,由前面的分析得知松邪,WindowManager的實(shí)現(xiàn)是在WindowManagerImpl里面坞琴,來看一下WindowManagerImpladdView方法;

image

最終走的是WindowManagerGlobaladdView方法逗抑;

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ...
       
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ...
            // 創(chuàng)建頂層的View視圖管理類
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                將當(dāng)前的DecorView設(shè)置給ViewRootImpl
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
        }
    }

在這里創(chuàng)建了頂層的View視圖管理類ViewRootImpl剧辐,并將DecorView設(shè)置給ViewRootImpl

來看看ViewRootImplsetView方法做了啥邮府?

/**
     * We have one child
     */
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                // 觸發(fā)View樹的布局與繪制
                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();
               
            }
        }
    }

這里最終會(huì)調(diào)用requestLayout方法觸發(fā)View樹的繪制荧关;

第一步:調(diào)用了scheduleTraversals方法;

image

第二步:調(diào)用了doTraversal方法褂傀;

image

image

第三步:調(diào)用了performTraversals方法忍啤;

image

performTraversals最終調(diào)用了performMeasureperformLayoutperformDraw三個(gè)大步驟完成對(duì)View樹的繪制同波;

這個(gè)流程在之前的一篇博客里面已經(jīng)分析過了鳄梅,感興趣的可以去看看;

到這里handleResumeActivity方法差不多就分析完了未檩,在這里我們理清了ActivityView之間的關(guān)系戴尸,是通過Window的管理器WindowManger來觸發(fā)View的繪制的,也就是說ActivityView的繪制流程都是交由WindowWindowManger來管理的冤狡;

看一下流程圖:

image

讓我們來回憶一下開頭提到的幾個(gè)問題孙蒙;

  • 1、在哪里創(chuàng)建Window悲雳,創(chuàng)建的Window用來干嘛的挎峦?
    Activityattach方法里面創(chuàng)建了Window的實(shí)現(xiàn)類PhoneWindow,用于管理View的創(chuàng)建以及和ViewRootIml進(jìn)行一些操作合瓢;
  • 2坦胶、ActivityWindow的關(guān)系?
    Window相當(dāng)于Activity的代理類,用于管理View的創(chuàng)建歪玲,以及后續(xù)View樹繪制的一些操作迁央;
  • 3、ActivityView的關(guān)系?
    Activity不直接操作View滥崩,通過代理類Window來管理View的創(chuàng)建以及繪制流程岖圈;

關(guān)于我

兄dei,如果我的文章對(duì)你有幫助的話钙皮,請(qǐng)幫我點(diǎn)個(gè)贊吧?蜂科,也可以關(guān)注一下我的Github博客;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市短条,隨后出現(xiàn)的幾起案子导匣,更是在濱河造成了極大的恐慌,老刑警劉巖茸时,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贡定,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡可都,警方通過查閱死者的電腦和手機(jī)缓待,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渠牲,“玉大人旋炒,你說我怎么就攤上這事∏╄荆” “怎么了瘫镇?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我铣除,道長(zhǎng)谚咬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任通孽,我火速辦了婚禮序宦,結(jié)果婚禮上睁壁,老公的妹妹穿的比我還像新娘背苦。我一直安慰自己,他們只是感情好潘明,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布行剂。 她就那樣靜靜地躺著,像睡著了一般钳降。 火紅的嫁衣襯著肌膚如雪厚宰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天遂填,我揣著相機(jī)與錄音铲觉,去河邊找鬼。 笑死吓坚,一個(gè)胖子當(dāng)著我的面吹牛撵幽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播礁击,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼盐杂,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了哆窿?” 一聲冷哼從身側(cè)響起链烈,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挚躯,沒想到半個(gè)月后强衡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡码荔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年漩勤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片目胡。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锯七,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出誉己,到底是詐尸還是另有隱情眉尸,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站噪猾,受9級(jí)特大地震影響霉祸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袱蜡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一丝蹭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坪蚁,春花似錦奔穿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至嘴脾,卻和暖如春男摧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背译打。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工耗拓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奏司。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓乔询,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親结澄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子哥谷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容