WindowManagerImpl是WindowManagerGlobal的代理類,
WindowManagerGlobal是單例模式,所以在一個(gè)App里面只會(huì)有一個(gè)
而IWindowSession一個(gè)App只有一個(gè),但是每個(gè)ViewRootImpl都持有對(duì)IWindowSession的引用,所以ViewRootImpl可以和WMS喊話,但是WMS怎么和ViewRootImpl喊話呢?是通過(guò)ViewRootImpl::W這個(gè)內(nèi)部類實(shí)現(xiàn)的
ActivityThread.main:
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
。。。
Looper.loop();
ActivityThread.attach();
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);//把ApplitionThread binder 傳過(guò)去
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
//回調(diào)
handleBindApplication(AppBindData data)
handleBindApplication(AppBindData data) :
//data.info = LoadApk;
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
mInstrumentation = new Instrumentation();
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
data.info.makeApplication:
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
Application app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
instrumentation.callApplicationOnCreate(app);
DecorView 根部局:
DecorView是FrameLayout的子類,它可以被認(rèn)為是Android視圖樹(shù)的根節(jié)點(diǎn)視圖。DecorView作為頂級(jí)View,一般情況下它內(nèi)部包含一個(gè)豎直方向的LinearLayout,在這個(gè)LinearLayout里面有上下三個(gè)部分,上面是個(gè)ViewStub,延遲加載的視圖(應(yīng)該是設(shè)置ActionBar,根據(jù)Theme設(shè)置),中間的是標(biāo)題欄(根據(jù)Theme設(shè)置,有的布局沒(méi)有),下面的是內(nèi)容欄。
ViewRoot:
ViewRoot對(duì)應(yīng)ViewRootImpl類,它是連接WindowManagerService和DecorView的紐帶,View的三大流程(測(cè)量(measure),布局(layout),繪制(draw))均通過(guò)ViewRoot來(lái)完成。
ViewRoot并不屬于View樹(shù)的一份子。從源碼實(shí)現(xiàn)上來(lái)看,它既非View的子類,也非View的父類,但是,它實(shí)現(xiàn)了ViewParent接口,這讓它可以作為View的名義上的父視圖。RootView繼承了Handler類,可以接收事件并分發(fā),Android的所有觸屏事件、按鍵事件、界面刷新等事件都是通過(guò)ViewRoot進(jìn)行分發(fā)的。
下面結(jié)構(gòu)圖可以清晰的揭示四者之間的關(guān)系:

handleLaunchActivity
handleLaunchActivity:調(diào)用了Activity的attach方法
Activity啟動(dòng)attach時(shí)候發(fā)生的事情:
1.創(chuàng)建PhoneWindow:mWindow = new PhoneWindow
2.設(shè)置window的Callback為Activity mWindow.setCallback(this);//設(shè)置回調(diào),向Activity分發(fā)點(diǎn)擊或狀態(tài)改變等事件
3.window設(shè)置windowmanager
windowmanager是接口,繼承自ViewManager,ViewManager接口就3方法:添加\更新\移除view,windowmanager實(shí)現(xiàn)類WindowManagerImpl
handleResumeActivity:
- WindowManagerImpl#addView方法add decor view
- 1中又WindowManagerGlobal#addView方法
- 2中實(shí)例化了ViewRootImpl類,接著用ViewRootImpl#setView方法,并把DecorView作為參數(shù)傳遞進(jìn)去,在這個(gè)方法內(nèi)部,會(huì)通過(guò)跨進(jìn)程的方式向WMS(WindowManagerService)發(fā)起一個(gè)調(diào)用,從而將DecorView最終添加到Window上。
- 最后通過(guò)WMS調(diào)用ViewRootImpl#performTraverals方法開(kāi)始View的測(cè)量、布局、繪制流程,這三大流程在下文會(huì)詳細(xì)講述,謝謝大家的閱讀。
WindowManagerImpl是WindowManagerGlobal的代理類,方法都是調(diào)用的WindowManagerGlobal的方法。
WindowManagerGlobal是單例模式,所以在一個(gè)App里面只會(huì)有一個(gè)WindowManagerGlobal實(shí)例。在WindowManagerGlobal里面維護(hù)了三個(gè)集合:
//前三個(gè)是addview時(shí)加
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>();
//removeview時(shí)加
private final ArraySet<View> mDyingViews = new ArraySet<View>();
參考上下
Activity只看到Window類,Window只有一個(gè)實(shí)現(xiàn)類PhoneWindow,Window里有WindowManager和DecorView,DecorView是FrameLayout的子類,是最底層的布局,DecorView里面有一個(gè)mContentParent,根據(jù)用戶選擇的不同標(biāo)志如不同Theme這個(gè)有不同的布局(上面標(biāo)題下面內(nèi)容),但是里面一定有一個(gè)R.id.content的ViewGroup,而且我們?cè)趚ml文件中聲明的布局文件,會(huì)被添加進(jìn)去DecorView是PhoneWindow的內(nèi)部類,繼承自FrameLayout,是最底層的界面
RPC方面:WindowManagerGlobal、ViewRootImpl、W
WindowManagerGlobal是和WindowManagerService(即WMS)通信的,但建立連接后將IWindowSession交給ViewRootImpl具體的交互都交給ViewRootImpl了。同時(shí)ViewRootImpl在setview()方法時(shí)通過(guò)IWindowSession將W對(duì)象傳給WMS,這樣,雙方都有了對(duì)方的接口,WMS中的Session注冊(cè)到WindowManagerGlobal的成員WindowSession中,ViewRootImpl::W注冊(cè)到WindowState中的成員mClient中。
- IWindowSession為了App改變View結(jié)構(gòu)時(shí)請(qǐng)求WMS為其更新布局。
- ViewRootImpl::W代表了App端的一個(gè)添加到WMS中的View,它可以理解為是ViewRootImpl中暴露給WMS的接口,這樣WMS可以通過(guò)這個(gè)接口和App端通信。每一個(gè)像這樣通過(guò)WindowManager接口中addView()添加的窗口都有一個(gè)對(duì)應(yīng)的ViewRootImpl,也有一個(gè)相應(yīng)的ViewRootImpl::W。
ViewRootImpl負(fù)責(zé)管理視圖樹(shù)和與WMS交互,與WMS交互是通過(guò)WindowSession。而且ViewRootImpl也負(fù)責(zé)UI界面的布局與渲染,負(fù)責(zé)把一些事件分發(fā)至Activity,以便Activity可以截獲事件。大多數(shù)情況下,它管理Activity頂層視圖DecorView,它相當(dāng)于MVC模型中的Controller。
詳細(xì):
DecorView:
DecorView是FrameLayout的子類,它可以被認(rèn)為是Android視圖樹(shù)的根節(jié)點(diǎn)視圖。DecorView作為頂級(jí)View,一般情況下它內(nèi)部包含一個(gè)豎直方向的LinearLayout,在這個(gè)LinearLayout里面有上下三個(gè)部分,上面是個(gè)ViewStub,延遲加載的視圖(應(yīng)該是設(shè)置ActionBar,根據(jù)Theme設(shè)置),中間的是標(biāo)題欄(根據(jù)Theme設(shè)置,有的布局沒(méi)有),下面的是內(nèi)容欄。具體情況和Android版本及主體有關(guān),以其中一個(gè)布局為例,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<ViewStub android:id="@+id/action_mode_bar_stub"
android:inflatedId="@+id/action_mode_bar"
android:layout="@layout/action_mode_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarTheme" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
在Activity中通過(guò)setContentView所設(shè)置的布局文件其實(shí)就是被加到內(nèi)容欄之中的,成為其唯一子View,就是上面的id為content的FrameLayout中,在代碼中可以通過(guò)content來(lái)得到對(duì)應(yīng)加載的布局。
ViewGroup content = (ViewGroup)findViewById(android.R.id.content);
ViewGroup rootView = (ViewGroup) content.getChildAt(0);
Activity啟動(dòng)attach時(shí)候發(fā)生的事情:
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) {
..................................................................
mWindow = new PhoneWindow(this, window);//創(chuàng)建一個(gè)Window對(duì)象
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);//設(shè)置回調(diào),向Activity分發(fā)點(diǎn)擊或狀態(tài)改變等事件
mWindow.setOnWindowDismissedCallback(this);
.................................................................
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//給Window設(shè)置WindowManager對(duì)象
....................................................................
}
結(jié)語(yǔ)
上面寫(xiě)了這么多,你可能看了前面忘了后面,下面,凱子哥給你總結(jié)一下,這篇文章到底講了什么東西:
每個(gè)Activity,都至少有一個(gè)Window,這個(gè)Window實(shí)際類型為PhoneWindow,當(dāng)Activity中有子窗口,比如Dialog的時(shí)候,就會(huì)出現(xiàn)多個(gè)Window。Activity的Window是我們控制的,狀態(tài)欄和導(dǎo)航欄的Window由系統(tǒng)控制。
在DecorView的里面,一定有一個(gè)id為content的FraneLayout的布局容器,咱們自己定義的xml布局都放在這里面。
Activity的Window里面有一個(gè)DecorView,它使繼承自FrameLayout的一個(gè)自定義控件,作為整個(gè)View層的容器,及View樹(shù)的根節(jié)點(diǎn)。
Window是虛擬的概念,DecorView才是看得見(jiàn),摸得著的東西,Activity.setContentView()實(shí)際調(diào)用的是PhoneWindow.setContentView(),在這里面實(shí)現(xiàn)了DecorView的初始化和id為content的FraneLayout的布局容器的初始化,并且會(huì)根據(jù)主題等配置,選擇不同的xml文件。而且在Activity.setContentView()之后,Window的一些特征位將被鎖定。
Activity.findViewById()實(shí)際上調(diào)用的是DecorView的findviewById(),這個(gè)方法在View中定義,但是是final的,實(shí)際起作用的是在ViewGroup中被重寫(xiě)的findViewTraversal()方法。
Activity的mWindow成員變量是在attach()的時(shí)候被初始化的,attach()是Activity被通過(guò)反射手段實(shí)例化之后調(diào)用的第一個(gè)方法,在這之后生命周期方法才會(huì)依次調(diào)用
在onResume()剛執(zhí)行之后,界面還是不可見(jiàn)的,只有執(zhí)行完Activity.makeVisible(),DecorView才對(duì)用戶可見(jiàn)
ViewManager這個(gè)接口里面就三個(gè)接口,添加、移除和更新,實(shí)現(xiàn)這個(gè)接口的有WindowManager和ViewGroup,但是他們兩個(gè)面向的對(duì)象是不一樣的,WindowManager實(shí)現(xiàn)的是對(duì)Window的操作,而ViewGroup則是對(duì)View的增、刪、更新操作。
WindowManagerImpl是WindowManager的實(shí)現(xiàn)類,但是他就是一個(gè)代理類,代理的是WindowManagerGlobal,WindowManagerGlobal一個(gè)App里面就有一個(gè),因?yàn)樗菃卫?,它里面管理了App中所有打開(kāi)的DecorView,ContentView和PhoneWindow的布局參數(shù)WindowManager.LayoutParams,而且WindowManagerGlobal這個(gè)類是和WMS通信用的,是通過(guò)IWindowSession對(duì)象完成這個(gè)工作的,而IWindowSession一個(gè)App只有一個(gè),但是每個(gè)ViewRootImpl都持有對(duì)IWindowSession的引用,所以ViewRootImpl可以和WMS喊話,但是WMS怎么和ViewRootImpl喊話呢?是通過(guò)ViewRootImpl::W這個(gè)內(nèi)部類實(shí)現(xiàn)的,而且源碼中很多地方采用了這種將接口隱藏為內(nèi)部類的方式,這樣可以實(shí)現(xiàn)六大設(shè)計(jì)原則之一——接口最小原則,這樣ViewRootImpl和WMS就互相持有對(duì)方的代理,就可以互相交流了
ViewRootImpl這個(gè)類每個(gè)Activity都有一個(gè),它負(fù)責(zé)和WMS通信,同時(shí)相應(yīng)WMS的指揮,還負(fù)責(zé)View界面的測(cè)量、布局和繪制工作,所以當(dāng)你調(diào)用View.invalidate()和View.requestLayout()的時(shí)候,都會(huì)把事件傳遞到ViewRootImpl,然后ViewRootImpl計(jì)算出需要重繪的區(qū)域,告訴WMS,WMS再通知其他服務(wù)完成繪制和動(dòng)畫(huà)等效果,當(dāng)然,這是后話,咱們以后再說(shuō)。
Window分為三種,子窗口,應(yīng)用窗口和系統(tǒng)窗口,子窗口必須依附于一個(gè)上下文,就是Activity,因?yàn)樗枰狝ctivity的appToken,子窗口和Activity的WindowManager是一個(gè)的,都是根據(jù)appToken獲取的,描述一個(gè)Window屬于哪種類型,是根據(jù)LayoutParam.type決定的,不同類型有不同的取值范圍,系統(tǒng)類的的Window需要特殊權(quán)限,當(dāng)然Toast比較特殊,不需要權(quán)限
PopupWindow使用的時(shí)候,如果想觸發(fā)按鍵和觸摸事件,需要添加一個(gè)背景,代碼中會(huì)根據(jù)是否設(shè)置背景進(jìn)行不同的邏輯判斷
Dialog在Activity不可見(jiàn)的時(shí)候,要主動(dòng)dismiss掉,否則會(huì)因?yàn)閍ppToken為空crash
Toast屬于系統(tǒng)窗口,由系統(tǒng)服務(wù)NotificationManagerService統(tǒng)一調(diào)度,NotificationManagerService中維持著一個(gè)集合ArrayList<ToastRecord>,最多存放50個(gè)Toast,但是NotificationManagerService只負(fù)責(zé)管理Toast,具體的現(xiàn)實(shí)工作由Toast::TN來(lái)實(shí)現(xiàn)
作者:裸奔的凱子哥
鏈接:http://www.itdecent.cn/p/65295b2cb047
來(lái)源:簡(jiǎn)書(shū)
簡(jiǎn)書(shū)著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。