Activity、View、Window的理解一篇文章就夠了

weixin_34239169發表於2017-04-09

要了解這三者之間的關係,我們帶著問題通過分析原始碼一步一步來揭開它們的神祕面紗!
文章有點長,首先要理解Activity、View、Window,我提出了一些問題,這篇文章可以解答如下問題:
1、為什麼要設計Activity、View、Window?
2、Activity工作過程是什麼樣的?(理解Activity)
3、Window是什麼?它的職能是什麼?
4、View跟Window有什麼聯絡?
5、Activity、View、Window三者如何關聯?

1、為什麼要設計Activity、View、Window?

用一句話來聯絡他們之間的關係:

Activity就像工匠,Window就像是窗戶,View就像是窗花,LayoutInflater像剪刀,Xml配置像窗花圖紙。
Android根據他們不同的職能讓他們各斯其活,同時也相互配合展示給我們靈活、精緻的介面。

一張圖理清所有層級關係:


1677180-10136019b1f4c254.png

好了,接下來一步一步的分析,首先從大家最熟悉的Activity開始:

我們的工匠大神Activity

一個應用程式裡所有的介面展示都來自於Activity,那Activity是如何工作的呢?
Activity工作過程:
要了解Activity工作過程,首先從啟動開始,下面沒有貼原始碼,因為本文章主題是三者之間的關係,而Activity東西太多了,就簡單的講一下。
啟動:
從startActivity開始,它會呼叫到Instrumentation,然後Instrumentation通過Binder向AMS(ActivityManagerService)發請求,通過PIC啟動Activity。而這個AIDL操作的方法定義在ApplicationThread中(裡面包括了Activity所有的生命週期方法的呼叫)。然後通過Handle回到主執行緒啟動activity。
因為中間流程太多,詳細寫出來容易造成“見其樹木,而不見其森林”的局面。

啟動Activity所執行的操作:
1、從ActivityClientRecord中獲取待啟動的Activity元件資訊
2、通過Instrumentation的newActivity方法使用類載入器建立Activity物件
3、通過LoadedApk的mackApplication方法來嘗試建立Application物件(如果Application已經建立,則不會重複建立)
4、建立ContextImpl物件,並通過Activity的attach方法來完成一些重要資料的初始化(包括讓Activity跟Window關聯)
5、呼叫Activity的onCreate方法

Activity其他生命週期的呼叫都是通過Binder向AMS發請求,然後執行的PIC操作,最後從ApplicationThread對生命週期呼叫。
下面是重點:Activity、View、Window三者的關係。

美麗的窗花View

View如何跟Activity關聯起來的?
其實View並不是直接跟Activity關聯起來的,而是通過Window這個中間人。如前面所說,View只是窗花,Window才是直接關聯到Activity上的。那麼:
View如何跟Window關聯起來呢?

下面先了解一下Window,就可以理解這個問題了

靈活的窗戶Window

Window如何跟Activity關聯?
每一個Activity都包含了唯一一個PhoneWindow,這個就是Activity根Window(之所以是說根Window是因為在它上面可以增加更多其他的Window,例如:彈出框(dialog))

那麼,PhoneWindow如何跟Activity關聯起來的呢?
來個最簡單的,setContentView其實就讓View與Window關聯,Window跟Activity關聯起來了。

那setContentView不是View跟Activity關聯嗎?
真相見Activity原始碼:


1677180-e15ad70d9e73cea3.png

明顯是將layout設定到Window上了,那這個 getWindow() 返回的Window是誰呢? 是不是前面提及PhoneWindow?


1677180-7a06828f1d80589b.png

1677180-fc547dc6cd1c02c9.png

真的是PhoneWindow,在 attach 的時候執行了PhoneWindow的初始化。
提到了 activity 的 attach 方法,該方法是在執行Activity啟動時在ActivityThread裡面的performLaunchActivity呼叫的。performLaunchActivity裡面做了很多Activity啟動過程具體的操作,例如:主題、記錄Activity棧、執行Activity onCreate 方法等。

這麼說來setContentView其實就是將View設定到Window上,Activity展示的其實是PhoneWindow上的內容。那麼其實 setContentView 實際上是呼叫的 getWindow().setContentView。

PhoneWindow是個什麼東西?它作為Activity跟View的中間人,它做了哪些工作?


1677180-c80f0dff1b4c4a47.png

首先 PhoneWindow 本身就是一個 Window。

從setContentView來分析:


1677180-08aa98c1b6008ba3.png

這裡的 mContentParent 其實是一個 ViewGroup。這麼看來就簡單了。PhoneWindow裡面包含了一個ViewGroup,setContentView其實就是將layout設定到了這個ViewGroup上了。

我們再看看這張圖:


1677180-10136019b1f4c254.png

DecorView是啥?
它直接跟PhoneWindow關聯起來的,有了mContentParent,為啥還需要DecorView?
如圖所見,DecorView它不僅包含了我們自己的佈局,它還包含了titleBar。為啥需要?結構上的需要,更好的管理佈局。

Window作為中間人,已經關聯了Activity跟View了,那麼如果處理Activity跟View之間的關係呢?
是時候揭開Window這個神祕面紗了:
之前提的PhoneWindow是繼承於Window的,它是連線Activity跟View之間的橋樑。所有對View的一些操作都需要藉助這個橋樑。

為了更好的理解Window,我們先從Dialog入手。在Activity中展示一個對話方塊的流程是怎樣的?
為啥從Dialog入手,因為它裡面包含了Window,而且可以直接操作Window裡面的View,這樣就能瞭解Window是如何控制View的,以及自定義Window怎麼展示到Activity上(因為了解Dialog,就知道怎麼讓自定義的Window與Activity關聯了)

我們省去所有的Dialog build的方法。直接從AlertDialog.show()方法開始:


1677180-8a9faee10ae46e7f.png

可見show()方法裡面執行了create()方法,繼續看create()方法做了什麼事:
1677180-5e1d78aeb584abf0.png

new 了一個 AlertDialog,那麼跟蹤它的構造方法看看:
1677180-24747dfa90f8f650.png

AlertDialog構造方法裡面最後執行的是這個構造方法,這裡找到了亮點:
1、mWindow是啥?是不是PhoneWindow?
首先AlertDialog是繼承Dialog的,mWindow就是Dialog裡面初始化的物件,看看是不是PhoneWindow,如果是,那麼就可以猜到通過在PhoneWindow新增View就可以在Activity上展示了,因為經過上面分析Activity是跟PhoneWindow有關聯的。帶著問題繼續分析原始碼:


1677180-7ffbee48e8919941.png

果然如此,太開心了。

2、AlertController是啥?


1677180-7fdfe0cc119052a5.png

從這兩個屬性就知道了,設定Title、Message的。用過Dialog的人都知道他們是啥,就不多說了。

既然這樣關聯起來了,那麼Window怎麼對View操控的呢?


1677180-3025b5e1d7c7b770.png

WindowManager是主角。
在Dialog build之後就將View設定進來了,繼續追蹤Dialog show()這個方法,這個方法會將View與Window相互聯絡:


1677180-7735ef1788d24963.png

1677180-afecf317d3c12889.png

因為電腦螢幕大小受限,就截了兩個圖。

mWindowManager.addView,看到這個方法,可以猜想到WindowManager是個啥玩意了。


1677180-8e8ab3ba7ef7c49e.png

從原始碼註釋可看出,WindowManager是對Window進行操作的。它可以做哪些操作?
1677180-491cdd01e4731824.png

好了,真相揭曉了。Window對View的操作是通過WindowManager來處理的。WindowManager提供在Window上新增View、移除View、更新View的操作。
然而可見 WindowManager 其實只是一個介面,真正的實現類是WindowManagerImpl
以addView為例,裡面有點繞,直接忽略中間過程,最後執行addView的是通過ViewRootImpl完成Window的新增工作的,它執行了View的requestLayout方法,在requestLayout方法裡會通過WindowSession完成Window的新增過程WindowSession是IWindowSession型別的,它是一個Binder物件,因此Window的新增工作其實是一次IPC呼叫。好了,大致流程就是如此,這樣就更新介面了。

總結:
1、為什麼要設計Activity、View、Window?
Activity就像工匠,Window就像是窗戶,View就像是窗花,LayoutInflater像剪刀,Xml配置像窗花圖紙。
Android根據他們不同的職能讓他們各斯其活,同時也相互配合展示給我們靈活、精緻的介面。為啥這樣設計?因為這樣的結構更好管理。就像為啥需要使用MVP、MVVM、各種設計模式一樣。

2、Activity工作過程是什麼樣的?
以Activity啟動過程為例,Activity啟動時是通過Binder向AMS(ActivityManagerService)發請求,通過PIC啟動Activity的。

3、Window是什麼?它的職能是什麼?
Activity要管理View需要通過Window來間接管理的。Window通過addView()、removeView()、updateViewLayout()這三個方法來管理View的。

4、View跟Window有什麼聯絡?
View需要通過Window來展示在Activity上。

5、Activity、View、Window三者如何關聯?
Activity包含了一個PhoneWindow,而PhoneWindow就是繼承於Window的,Activity通過setContentView將View設定到了PhoneWindow上,而View通過WindowManager的addView()、removeView()、updateViewLayout()對View進行管理。Window的新增過程以及Activity的啟動流程都是一次IPC的過程。Activity的啟動需要通過AMS完成;Window的新增過程需要通過WindowSession完成。

你竟然看完了,非常感謝你的支援,希望這篇文章對你有幫助~

相關文章