title: '深入理解android2-WMS,控件-圖床版'
date: 2020-03-08 16:22:42
tags:
typora-root-url: ./深入理解android2-WMS-控件
typora-copy-images-to: ./深入理解android2-WMS-控件
WMS
WMS主要負(fù)責(zé)兩個(gè)功能, 一是負(fù)責(zé)窗口的管理,如窗口的增加刪除,層級(jí).二是負(fù)責(zé)全局事件的派發(fā).如觸摸點(diǎn)擊事件.
先簡單介紹幾個(gè)重要的類
IWindowSession. 進(jìn)程唯一的.是一個(gè)匿名binder.通過他向WMS請(qǐng)求窗口操作
surface 繪畫時(shí),canvas會(huì)把內(nèi)容繪制到surface里.surface是有surfaceFlinger提供給客戶端的.
WindowManager.LayoutParams 集成自ViewGroup.LayoutParams.用來指明client端的窗口的一些屬性.最重要的是type. 根據(jù)這屬性來對(duì)多個(gè)窗口進(jìn)程ZOrder的排序.
windowToken.向WMS添加的窗口令牌.每個(gè)窗口都要有一個(gè)令牌.
IWindow. 是client提供給WMS的.繼承自binder.WMS通過IWindow對(duì)象來主動(dòng)發(fā)起client端的事件.
窗口的本周就是進(jìn)行繪制所使用的surface,客戶端向WMS添加窗口的過程,就是WMS為客戶端分配surface的過程.
ui框架層就是使用surface上繪制ui元素.及響應(yīng)輸入事件
WMS負(fù)責(zé)surface的分配.窗口的層級(jí)順序
surfaceFlinger負(fù)責(zé)將多個(gè)Surface混合并輸出.
WMS 啟動(dòng)
WMS有SystemServer 進(jìn)程啟動(dòng).他和AMS其實(shí)是運(yùn)行于一個(gè)進(jìn)程中的.只是分別有各自的線程.
SystemServer.run
- 顯示創(chuàng)建兩個(gè)HandlerThread.和對(duì)應(yīng)的handler.一個(gè)叫ui,一個(gè)叫windowManager.這兩個(gè)HandlerThread就會(huì)在run中循環(huán)接受發(fā)過來的消息.
- 調(diào)用WMS的main.傳入兩個(gè)handler.返回一個(gè)WMS對(duì)象,加入ServiceManager管理
- 初始化顯示信息,主要是顯示持續(xù)相關(guān).完成后WMS會(huì)要求AMS進(jìn)行配置更新
- 通知WMS初始化完成,調(diào)用windowManagerPolicy的systemReady
//創(chuàng)建兩個(gè) HandlerThread
HandlerThread uiHandlerThread = new HandlerThread("UI");
uiHandlerThread.start();
Handler uiHandler = new Handler(uiHandlerThread.getLooper());
HandlerThread wmHandlerThread = new HandlerThread("WindowManager");
wmHandlerThread.start();
Handler wmHandler = new Handler(wmHandlerThread.getLooper());
//調(diào)用WMS.main 返回一個(gè)WMS對(duì)象,加入ServiceManager管理
wm = WindowManagerService.main(context, power, display, inputManager,
uiHandler, wmHandler,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
!firstBoot, onlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
//初始化顯示信息
wm.displayReady();
//通知WMS初始化完成.
wm.systemReady();
WMS.main
上邊傳入了兩個(gè)handler.這里就使用windowManager的handler來創(chuàng)建WMS.也就是在一個(gè)handerThread線程中創(chuàng)建
public static WindowManagerService main(final Context context,
final PowerManagerService pm, final DisplayManagerService dm,
final InputManagerService im,
final Handler uiHandler, final Handler wmHandler,
final boolean haveInputMethods, final boolean showBootMsgs,
final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
//在名為WindowManager的handlerThread創(chuàng)建WMS,注意,傳入WMS了uiHandler.這是另一個(gè)handlerThread
wmHandler.runWithScissors(new Runnable() {
@Override
public void run() {
holder[0] = new WindowManagerService(context, pm, dm, im,
uiHandler, haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
}
WMS構(gòu)造函數(shù)主要有下
1. 初始化了很多東西.包括動(dòng)畫相關(guān)屏幕相關(guān)
2. 保存輸入事件的服務(wù), WMS要管理輸入事件
mInputManager = inputManager;
3. 對(duì)ui線程進(jìn)行初始.這里創(chuàng)建的WindowManagerPolicy
initPolicy(uiHandler);
WMS的重要成員
InputManagerService mInputManager
用來管理每個(gè)窗口的事件輸入.也就是把輸入事件轉(zhuǎn)發(fā)到正確的窗口
Choreographer mChoreographer
能獲取顯示系統(tǒng)的同步信號(hào).用來驅(qū)動(dòng)動(dòng)畫的渲染
WindowAnimator mAnimator
所有窗口動(dòng)畫的總管,在mChoreographer的驅(qū)動(dòng)下渲染所有動(dòng)畫
WindowManagerPolicy mPolicy
只有PhoneWindowManager一個(gè)實(shí)現(xiàn).定義了很多窗口相關(guān)的策略.是最重要的成員,比如負(fù)責(zé)窗口的zorder順序.
zorder就是各個(gè)窗口在z軸的值.越大越在屏幕上層.窗口就是根據(jù)zorder值一層一層堆在一起.
SparseArray mDisplayContents
可以繪制的屏幕列表.默認(rèn)是只有1個(gè).
HashMap<IApplicationToken, WindowToken> mTokenMap
管理所以窗口的顯示令牌token,每個(gè)窗口都要屬于一個(gè)token.這里的IBinder 是
ArrayList< AppWindowToken> mAppTokens
表示所有Activity的token. AppWindowToken是WindowToken的子類,這個(gè)list的順序和AMS中對(duì)mHistory列表中activity的順序是一樣的 .反應(yīng)了系統(tǒng)中activity的疊加順序.也就是說.所有窗口都有WindowToken.而Activity對(duì)應(yīng)的窗口則多了AppWindowToken.
HashMap<IWIndow, WindowState> mWindowMap
每個(gè)窗口都對(duì)應(yīng)一個(gè)WindowState.存儲(chǔ)改窗口的狀態(tài)信息(這就和AMS中對(duì)每個(gè)activity抽象成ActivityRecord一樣)
這里的iBinder 是IWIndow類.
HashSet< Session > mSessions
Session 是WMS提供給客戶端來與WMS進(jìn)行交互的,這是匿名binder.為了減輕WMS的負(fù)擔(dān).客戶端通過IWindowManager.openSession 拿到他的代理.然后通過代理與WMS交互.每個(gè)進(jìn)程唯一.
WMS窗口管理結(jié)構(gòu)
添加窗口
客戶端通過IWindowSession.add 來添加窗口. iWindowSession 是同aidl形成的.最終到了WMS.addWindow.
獲取要顯示的屏幕
找到了這個(gè)窗口對(duì)應(yīng)的windowToken,可以為空,為空就新建一個(gè)并保存.然后是對(duì)窗口類型的各種判斷.
為APP的窗口創(chuàng)建WindowState對(duì)象.維護(hù)窗口的所有信息.
WindowManagerPolicy出現(xiàn),調(diào)整了layoutparams的參數(shù).
保存這個(gè)新窗口的token令牌和生成的WindowState.并調(diào)用WindowState的attach
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
1.獲取要顯示的屏幕
final DisplayContent displayContent = getDisplayContentLocked(displayId);
//attrs 是 LayoutParams, attrs.type表示窗口的類型.attrs.toekn表示窗口屬于哪個(gè)對(duì)象.
//2.這里找到了這個(gè)窗口對(duì)應(yīng)的windowToken
WindowToken token = mTokenMap.get(attrs.token);
if (token == null) {
token = new WindowToken(this, attrs.token, -1, false);
}elseif (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
AppWindowToken atoken = token.appWindowToken;
}
//3.為APP的窗口創(chuàng)建WindowState
win = new WindowState(this, session, client, token,
attachedWindow, seq, attrs, viewVisibility, displayContent);
4.WindowManagerPolicy出現(xiàn),調(diào)整了layoutparams的參數(shù).
mPolicy.adjustWindowParamsLw(win.mAttrs);
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
res = mPolicy.prepareAddWindowLw(win, attrs);
5. 保存這個(gè)新窗口的token令牌和生成的WindowState.并調(diào)用WindowState的attach
if (addToken) {
mTokenMap.put(attrs.token, token);
}
win.attach();
mWindowMap.put(client.asBinder(), win);
}
這里總的來說就是確立了客戶窗口的WindowToken.WindowState.和DisplayContent. 并都保存了起來.同時(shí)根據(jù)layoutparams.type進(jìn)行了些窗口等級(jí)的判斷.
理解windowToken
WindowToken將同一個(gè)應(yīng)用組件的窗口安排在一起.一個(gè)應(yīng)用組件可以是Activity,InputMethod.
WindowToken使應(yīng)用組件在變更窗口時(shí)必須與自己的WindowToken匹配.
這里主要是為了處理窗口的層級(jí)關(guān)系而設(shè)立的.
只要是一個(gè)binder對(duì)象.都可以作為token向wms聲明.wms會(huì)把這個(gè)binder對(duì)應(yīng)起一個(gè)WindowToken.其實(shí)就是把客戶端的binder和wms里的一個(gè)WindowToken對(duì)象進(jìn)行了綁定.
因?yàn)锳ctivity比較復(fù)雜,因此WMS為Activity實(shí)現(xiàn)了WindowToken的子類 appwindowtoken.同時(shí)在AMS啟動(dòng)Activity的ActivityStack.startActivityLocked里聲明token.
mService.mWindowManager.addAppToken(addPos, r.userId, r.appToken,
r.task.taskId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
r 是要啟動(dòng)Activity的ActivityRecord r.apptoken就是這個(gè)binder
然后在activityStack.realStartActivityLocked里在發(fā)給用戶進(jìn)程,然后用戶在通過這個(gè)binder和WMS交互時(shí)帶過來.
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
new Configuration(mService.mConfiguration),
r.compat, r.icicle, results, newIntents, !andResume,
mService.isNextTransitionForward(), profileFile, profileFd,
profileAutoStop);
r.appToken 就是這個(gè)binder
activity則在 activityStack 線程的handleResumeActivity 里把Activity 對(duì)應(yīng)的窗口,加入到wMS中
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
ActivityClientRecord r = performResumeActivity(token, clearHide);
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;
wm.addView(decor, l);
}
}
取消token 也是在AMS中 ,也就是說, AMS負(fù)責(zé)avtivity的token向WMS的添加和刪除.
當(dāng)然.Activity的 r.appToken 是 IApplicationToken.Stub ,他里邊有一系列的窗口相關(guān)的通知回調(diào).
這里總結(jié)下. AMS在創(chuàng)建Activity的ActivityRecord時(shí),創(chuàng)建了他的appToken,有把a(bǔ)ppToken傳送給WMS.WMS對(duì)應(yīng)匹配為APPWindowToken,最后還把這個(gè)appToken發(fā)送給activity.因此AMS就通過ActivityRecord就可有直接操作WMS對(duì)該窗口的繪制.如圖.
理解WindowState
每個(gè)window在WMS里都抽象成了WindowState.他包含一個(gè)窗口的所有屬性.WindowState在客戶端對(duì)應(yīng)的則是iWidow.stub類.iWidow.stub有很多窗口通知的回調(diào).
WindowState被保存在mWindowMap里.這是整個(gè)系統(tǒng)所有窗口的一個(gè)全集.
HashMap<IBinder, WindowToken> mTokenMap .這里是 IApplicationToken(客戶端)和WindowToken的映射
HashMap<IBinder, WindowState> mWindowMap 這里是IWidow(客戶端)和WindowState的映射,并且WMS通過這個(gè)IWindow 來回調(diào)客戶端的方法.
上圖可以看出.每個(gè)activity 只有一個(gè)ActivityRecord.也只有一個(gè)AppToken,也就只有一個(gè)WindowToken.而一個(gè)acitvity可能有多個(gè)窗口.每個(gè)窗口對(duì)應(yīng)一個(gè)WindowState.
WindowToken用來和AMS交換. 而WindowState對(duì)應(yīng)的iWindow則是WMS來與客戶端交互的.
窗口顯示次序
窗口顯示次序就是窗口在Z軸的排了.因?yàn)榇翱谑钳B加在一起的.因此就需要知道哪些顯示在上邊,哪些在下邊.這個(gè)由WindowState構(gòu)造時(shí)確定
窗口類型是子窗口
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
} else {
窗口類型是普通窗口
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
}
可見.分配規(guī)則是由WindowManagerPolicy mPolicy來決定的.產(chǎn)生 mBaseLayer和mSubLayer. mBaseLayer決定該窗口和他的子窗口在所有窗口的顯示位置. mSubLayer決定子窗口在同級(jí)的兄弟窗口的顯示位置.值越高.顯示約靠上.
WindowState 產(chǎn)生了他自己這個(gè)窗口的layer值后.在添加窗口的時(shí)候就會(huì)把所有窗口按layer排序插入mWindows列表中,在通過 adjustWallpaperWindowsLocked();進(jìn)行層級(jí)調(diào)整.
窗口的布局
當(dāng)客戶端通過IWindowsession.add后,客戶端還沒有獲得Surface.只有在執(zhí)行IWindowsession.relayout后.客戶端才獲得了一塊Surface. IWindowsession.relayout根據(jù)客戶端提供的參數(shù),為客戶端提供surface.具體實(shí)現(xiàn)是WMS.relayoutWindow
WMS.relayoutWindow
- 方法參數(shù)的介紹
- 所在mWindowMap.防止多線程競爭
- 找到需要進(jìn)行relayout 的窗口對(duì)應(yīng)的windowState
- 遍歷所有DispalyContent的所有窗口,計(jì)算布局尺寸,并把尺寸設(shè)置給Surface
- 把布局結(jié)果返回給用戶端.
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
參數(shù)里的outSurface最后就是傳遞給客戶端的surface.他會(huì)和客戶端的surface進(jìn)行綁定.然后客戶端就可以繪制了.
synchronized(mWindowMap) {
//獲得要relayout的窗口
WindowState win = windowForClientLocked(session, client, false);
//下邊是對(duì)win各種屬性的更新.
...
//遍歷所有DispalyContent的所有窗口,計(jì)算布局尺寸,并把尺寸設(shè)置給Surface
performLayoutAndPlaceSurfacesLocked();
//把布局結(jié)果返回給用戶端
outFrame.set(win.mCompatFrame);
outContentInsets.set(win.mContentInsets);
outVisibleInsets.set(win.mVisibleInsets);
}
}
總的來說就是根據(jù)用戶傳入的參數(shù),更新WindowState.然后遍歷所有窗口布局.在設(shè)置合適的Surface尺寸,在返回給用戶端
performLayoutAndPlaceSurfacesLocked 會(huì)循環(huán)調(diào)用6次.里邊的邏輯大概如下
- 清除已退出或已隱藏的僵尸窗口的surface
- 又調(diào)用其他方法進(jìn)行布局
- 如果需要.再次重復(fù)布局操作.
performLayoutAndPlaceSurfacesLockedLoop{
1清除已退出或已隱藏的僵尸窗口的surface
for (int i=0; i<mForceRemoves.size(); i++) {
WindowState ws = mForceRemoves.get(i);
removeWindowInnerLocked(ws.mSession, ws);
}
performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
//如果需要.再次重復(fù)布局
if (needsLayout()) {
if (++mLayoutRepeatCount < 6) {
requestTraversalLocked();
}
}
}
這里主要下,因?yàn)橹凹恿随i.requestTraversalLocked他又會(huì)重復(fù)執(zhí)行performLayoutAndPlaceSurfacesLocked();因此會(huì)重復(fù)循環(huán)執(zhí)行布局.
布局這部分就記個(gè)原理吧
布局完成后.客戶端的尺寸和surface都得到了.就可以繪制 了.WMS會(huì)通知客戶端布局發(fā)送變化
總結(jié),WMS 負(fù)責(zé)管理所有的窗口.包括系統(tǒng)窗口和APP窗口,而窗口必須有一個(gè)WindowToken所為標(biāo)識(shí)符.同時(shí)WMS為每個(gè)窗口創(chuàng)建一個(gè)WindowState類,這是窗口在服務(wù)端的抽象.WindowState則綁定了一個(gè)客戶端的IWindow類,WMS通過這個(gè)IWindow 向APP發(fā)送消息.
AMS在啟動(dòng)Activity的時(shí)候.把ActivityRecord.token 通過wms.addtoken 注冊(cè)到WMS.又把這個(gè)token發(fā)送到APP端.因此三方可以通過這個(gè)token正確找到對(duì)應(yīng)的數(shù)據(jù).
WMS負(fù)責(zé)給所以窗口按ZOrder排序,確定窗口的尺寸,提供繪畫用的surface.
Activity的窗口是先wms.addtoken 建立windowToken關(guān)系 . wms.addWindow. 添加串口, WMS.relayout獲取surface. 完成 .
一個(gè)windowToken對(duì)應(yīng)一個(gè)Activity. 但是可能對(duì)應(yīng)多個(gè)windowSatate.也就是對(duì)應(yīng)多個(gè)窗口.
安卓控件系統(tǒng)
基礎(chǔ)類介紹
ViewRoot
是view樹的根實(shí)現(xiàn)類是viewRootImpl.但是他不是view.他是用來和WMS進(jìn)行交流的管理者.viewRootImpl內(nèi)部有個(gè)IWindowSession,是WMS提供的匿名binder,同時(shí)還有個(gè)iWindow子類,用來讓W(xué)MS給viewr發(fā)消息. view通過ViewRoot向WMS發(fā)消息.WMS在通過IWIndow 向APP發(fā)消息. 每個(gè)View樹只有一個(gè)ViewRoot,每個(gè)Activity也只有一個(gè)ViewRoot. UI繪制,事件傳遞.都是通過ViewRoot.
Window
.實(shí)現(xiàn)類是PhoneWindow . Activity和View的溝通就是通過Window.Activity實(shí)現(xiàn)window的各種回調(diào).一個(gè)Activity也對(duì)應(yīng)一個(gè)PhoneWindow.也對(duì)應(yīng)一個(gè)View樹.
Docerview 就是View樹的根.這是一個(gè)View. 他由PhoneWindow管理. 下文的WindowManager也由phoneWindow管理.
他還管理window的屬性 WindowManager.layoutparams.
windowManager
他是一個(gè)代理類.他集成自ViewManager.他的實(shí)現(xiàn)是WindowManagerImpl.這是每個(gè)Activity都有一個(gè).但是他只是把工作委托給了 WindowManagerGlobal來實(shí)現(xiàn). 他負(fù)責(zé)添加刪除窗口,更新窗口.并控制窗口的補(bǔ)件屬性WindowManager.Layoutparams.
WindowManagerGlobal
是進(jìn)程唯一的.負(fù)責(zé)這個(gè)進(jìn)程的窗口管理.他里邊有三個(gè)集合.保存這個(gè)進(jìn)程所有窗口的數(shù)據(jù).這里的每個(gè)數(shù)據(jù)根據(jù)index得到的是同一個(gè)Activity屬性.所有的WindowManager的操作都轉(zhuǎn)到他這里來.
private final ArrayList<View> mViews 每個(gè)view是個(gè)跟節(jié)點(diǎn)
private final ArrayList<ViewRootImpl> mRoots view對(duì)應(yīng)的viewRoot
private final ArrayList<WindowManager.LayoutParams> mParams 窗口的layoutparams屬性.每個(gè)窗口一個(gè)
對(duì)于一個(gè)acitivity對(duì)象永遠(yuǎn)對(duì)應(yīng)一個(gè)PhoneWindow,一個(gè)WindowManagerImpl,一個(gè)WMS端的APPWindowToken,一個(gè)AMS里的ActivityRecord(但是如果一個(gè)activity在棧里有多個(gè)對(duì)象,就有多個(gè)ActivityRecord和AppWindowToken),acitvity 的默認(rèn)窗口的view樹是DocerView.
一個(gè)窗口 對(duì)應(yīng)一個(gè)ViewRoot,一個(gè)View樹.一個(gè)WindowManager.LayoutParams,一IWindow(WMS回調(diào)app).一個(gè)WSM端的WindowSatate.
但是一個(gè)Activity可以有多個(gè)窗口,因此對(duì)應(yīng)WMS里可能有多個(gè)WindowSatate.這些WindowState都對(duì)應(yīng)一個(gè)AppWindowToken.
一個(gè)Activity可能被加載多次.因此在AMS中可能有多個(gè)ActivityRecord對(duì)應(yīng)這個(gè)activit的多個(gè)對(duì)象.
但是一個(gè)進(jìn)程則對(duì)應(yīng)一個(gè)WindowManagerGlobal.一個(gè)ActivityThread(主線程).一個(gè)ApplicationThread(AMS調(diào)用app).一個(gè)iWindowSession(viewroot向WMS發(fā)消息)
這里的區(qū)別就是 app與AMS 的交互是以進(jìn)程之間進(jìn)行通信.而App與WMS的交互.則是以窗口作為通信基礎(chǔ).
窗口添加過程
當(dāng)Activity由AMS啟動(dòng)時(shí).ActivityThread 通過handleResumeActivity執(zhí)行resume相關(guān)的操作.這個(gè)函數(shù)首先是執(zhí)行activity.resume, 此時(shí)activity 對(duì)應(yīng)的view樹已經(jīng)建立完成(oncreate中建立,PhoneWindow也創(chuàng)建了).需要把a(bǔ)ctivity的窗口添加到WMS中去管理.
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
//執(zhí)行onresume
ActivityClientRecord r = performResumeActivity(token, clearHide);
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;
//添加daoWMS中
wm.addView(decor, l);
}
}
這里的wm是WindowManager.是每個(gè)activity一個(gè).他內(nèi)部會(huì)調(diào)用WindowManagerGlobal.addView
WindowManagerGlobal.addView
這里會(huì)為窗口創(chuàng)建ViewRootImpl. 并把view.ViewRootImpl.WindowMa.LayoutParams都保存在WindowManagerGlobal中, 并通過ViewRootImpl向WMS添加窗口
如果這個(gè)窗口是子窗口(wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW)
就把子窗口的token設(shè)為父窗口的token否則就是所屬activity的token.
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
root = new ViewRootImpl(view.getContext(), display);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
}
}
在來個(gè)圖
在這里我們看到.我們通過mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 拿到的并不是遠(yuǎn)程的WMS.而是本地的WindowManagerImpl. 他又把請(qǐng)求轉(zhuǎn)發(fā)給WindowManagerGlobal ,而WindowManagerGlobal作為進(jìn)程單實(shí)例.又是吧請(qǐng)求轉(zhuǎn)給對(duì)應(yīng)窗口的ViewRootImpl.ViewRootImpl通過WMS的IWindowSession 把數(shù)據(jù)發(fā)給WMS.
理解ViewRootImpl
ViewRootImpl用來溝通View和WMS.并接受WMS的消息.這是雙向的binder通信.作為整個(gè)空間樹的根部,控件的測量,布局,繪制,輸入時(shí)間的派發(fā)都由ViewRootImpl來觸發(fā).
ViewRootImpl由WindowManagerGlobal創(chuàng)建,是在activityThread.handleResumeActivity時(shí),先執(zhí)行activity.resume.在調(diào)用wm.addView. 就會(huì)執(zhí)行WindowManagerGlobal.addView里.創(chuàng)建ViewRootImpl,此時(shí)是在ui線程中.
ViewRootImpl里的mView屬性.host屬性,就是view樹
ViewRootImpl 創(chuàng)建
- 得到WMS里的IWindowSession代理.
- 保存display.表示一塊屏幕.之后view繪制在這上.
- 保存當(dāng)前線程,也就是ui線程
- 保存當(dāng)前窗口尺寸.
- W類是WMS回調(diào)APP的binder
- attachInfo表示view樹所依附的窗口的信息,會(huì)傳遞給每個(gè)view
- 主線程中創(chuàng)建Choreographer,用于接收重繪信號(hào).
- 用于向主線程發(fā)消息的mHandler
- 用于繪制的surface. 會(huì)和WMS返回的surface綁定.
public ViewRootImpl(Context context, Display display) {
得到WMS里的IWindowSession代理.
mWindowSession = WindowManagerGlobal.getWindowSession();
保存繪制的屏幕
mDisplay = display;
保存ui線程
mThread = Thread.currentThread();
mDirty = new Rect();
//窗口尺寸
mTempRect = new Rect();
//WMS回調(diào)APP的binder
mWindow = new W(this);
//表示當(dāng)前view樹所依附的窗口的信息,會(huì)給每個(gè)view
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,context);
//主線程中創(chuàng)建Choreographer,用于接收重繪信號(hào).
mChoreographer = Choreographer.getInstance();
}
///依附于主線程.發(fā)消息到主線程執(zhí)行.
final ViewRootHandler mHandler = new ViewRootHandler();
public final Surface mSurface = new Surface();
ViewRootImpl.setView 注冊(cè)窗口
- 保存view樹
- 添加到WMS前的重繪操作,這次的結(jié)果可能會(huì)被WMS的回調(diào)進(jìn)行修改.但是重繪必不可少
- 向WMS注冊(cè)窗口.
- ui線程注冊(cè)完成輸入事件的注冊(cè).
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//保存控件樹
mView = view;
//添加到WMS前的重繪操作
requestLayout();
inputChannel是一個(gè)接受輸入事件的管道.用來接收輸入事件.
mInputChannel = new InputChannel();
//向WMS注冊(cè)窗口
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
//這里是輸入事件的接收者,傳入的looper.myLooper是ui線程的looper.因此輸入事件在ui線程觸發(fā).這里了解吧
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
}
ViewRootImpl.performTraversals 重繪
添加窗口時(shí)通過requestLayout();向ui線程發(fā)送消息.最后回調(diào)到ViewRootImpl.performTraversals.他是整個(gè)ui控件樹,measure.layout.draw的集合.
這里分為五個(gè)階段.
預(yù)測量階段.進(jìn)行第一次測量,獲得view.getMeasuredWitdh/Height,此時(shí)是控件樹期望的尺寸.會(huì)執(zhí)行View的onMeasure
布局階段,根據(jù)預(yù)測量的結(jié)果,通過IWindowSession.relayout向WMS請(qǐng)求調(diào)整窗口的尺寸這會(huì)使WMS對(duì)窗口重新布局,并把結(jié)果返回給ViewRootImpl.
最終測量階段, 預(yù)測量的結(jié)果是view樹期望的結(jié)果.WMS可能會(huì)進(jìn)行調(diào)整,在這里WMS已經(jīng)把結(jié)果通知了ViewRootImpl.因此這里會(huì)窗口實(shí)際尺寸performTraversals進(jìn)行布局.view及子類的onMeasure會(huì)被回調(diào)
布局階段. 測量完成后獲得空間的尺寸,布局要確定控件的位置,View及子類的onLayout會(huì)被回調(diào).
繪制階段,使用WMS提供的surface.進(jìn)行繪制,View及子類的onDraw會(huì)被回調(diào).
通常我們看到的都是 先measure,在layout在draw. 這里看到.其實(shí)measure先得到期望值,在和WMS溝通.WMS在調(diào)整后,返回確定值,在根據(jù)確定值進(jìn)行mesure.
預(yù)測量
private void performTraversals() {
窗口的最新尺寸
Rect frame = mWinFrame;
if (mFirst) {//第一次遍歷.窗口剛添加到WMS.還沒有有效的窗口尺寸.
//使用屏幕的尺寸.
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
//通過view書跟節(jié)點(diǎn).執(zhí)行 attach 每個(gè)view都會(huì)回調(diào)onAttachedToWindow();
host.dispatchAttachedToWindow(mAttachInfo, 0);
}else {
//不是第一次.并且窗口尺寸和Viewrootimpl不一樣.表現(xiàn)需要重新測量.
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
}
//處理view.post(runnable) 也就是借著ViewRootimpl的hanlder發(fā)送給ui線程執(zhí)行.
這個(gè)RunQue是進(jìn)程唯一的.
getRunQueue().executeActions(mAttachInfo.mHandler);
//進(jìn)行預(yù)測量.
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
}
measureHierarchy里會(huì)通過三次協(xié)商.執(zhí)行performMeasure 來確認(rèn)合適的尺寸.
performMeasure 會(huì)調(diào)用view 的measure 優(yōu)會(huì)調(diào)用onMeasure. 我們可重寫onMeasure來實(shí)現(xiàn)測量.而measure 方法是final的.onMeasure 的結(jié)果通過setMeasuredDimension方法保存.
對(duì)于view. onMeasure.比較容易. 對(duì)于ViewGroup.則還要遍歷調(diào)用他所以子view的measure. 并且需要考慮padding和子view 的margin. padding是控件外內(nèi)邊距. margin 是控件外邊距.
ViewGroup需要先測量完子view.在根據(jù)子view的測量值得到自己的寬高.舉例,如果只有一個(gè)子view.那么ViewGroup的寬= 子view的寬+子view的margin+viewg的padding. 至少是這個(gè)值.
繼續(xù)回到performTraversals
...
boolean windowShouldResize //決定是否窗口要改變尺寸.至此.預(yù)測量完成
這里就是提前測量了一下.得到控件樹希望的尺寸大小,
布局窗口和最終測量
通過relayoutWindow來布局窗口. ViewRootImpl 通過IWindowSession 來通知WMS進(jìn)行窗口布局.
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending){
int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,mPendingMergedConfiguration, mSurface);
}
這里主要下. 調(diào)用WMS后.WMS會(huì)調(diào)整窗口的尺寸. 同時(shí)會(huì)生成surface返回給ViewRootImpl. 因此后續(xù)的繪畫就有了畫布了.可以看到最后的參數(shù)是mSurface.這是本地的surface. 這里會(huì)和wms的進(jìn)行綁定.
接下來繼續(xù)performTraversals,綁定WMS返回的surface.然后更新尺寸.
最后進(jìn)行最終測量. 上邊過程太亂了. 了解下就行.還是看常見的控件繪制流程.
viewRootImpl.performTraversals
繪制由viewRootImpl.performTraversals觸發(fā), 抽取出來后,就是這樣
private void performTraversals(){
performConfigurationChange() //會(huì)觸發(fā)onConfigurationChange事件
//這時(shí)候傳入的是屏幕的尺寸
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//用測量完的高度.在進(jìn)行補(bǔ)件 host是view樹的跟布局
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
performLayout(lp, mWidth, mHeight);
//繪制流程. 此時(shí)WMS返回的surface已和ViewRootImpl的surface綁定了起來.可以進(jìn)行繪制了.
performDraw();
}
viewRootImpl.performMeasure
就是直接調(diào)用view樹的根的measure方法. 傳入到View
View.measure
該方法是final .意味著無法重寫.這里又會(huì)調(diào)用onMeasure.
因此.對(duì)于view.在onMeasure中調(diào)整好高度,通過setMeasuredDimension設(shè)置好自己的測量寬高就可以了.
對(duì)應(yīng)ViewGroup.則在onMeasure中,先要遍歷子view.調(diào)用他們的measure(注意一定是調(diào)用子類的measure,measure又會(huì)調(diào)用onMeasure), 子view寬高都知道后,在根據(jù)子view的寬高來設(shè)置自己.也就是ViewGroup的寬高受子view影響.
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
這里的widthMeasureSpec由兩部分組成,模式mode和值size.都由父view提供
模式有三種
unspecified 父view不限制ziview大小.多用于系統(tǒng)內(nèi)部
exactly 精確值,子view的大小就是widthMeasureSpec的size,對(duì)應(yīng)子view的match_parent 和確定值這兩種
at_most 父view提供可用大小,既widthMeasureSpec的size,子view不超過這個(gè)值就可以.對(duì)應(yīng)子view的wrap_content
可以看到view的measure又調(diào)用了onMeasure, 如果是view 則可以直接重新onMeasure來設(shè)定大小.而對(duì)于ViewGroup, 則需要重寫onMeasure來先遍歷子view.設(shè)定大小.然后再設(shè)定viewGroup的大小. ViewGroup并沒有重寫onMeasure.因?yàn)槊總€(gè)ViewGroup要實(shí)現(xiàn)的效果不同,需要自己完成.但ViewGroup提供了幾個(gè)方法供ViewGroup的繼承類來遍歷子view.
view的寬高由自己的layoutParams和父view提供的 widthMeasureSpec|heightMeasureSpec共同決定.
View 自己的寬高,是保存在LayoutParams中對(duì),以寬舉例 LayoutParams.width 有三種情況,精確值(就是指定大小),MATCH_PARENT. WRAP_CONTENT,模式則有fuview提供.有 unspecified,exactly,at_most三種.
匹配如下.
?其實(shí)這個(gè)很好理解. 如果子view自己指定了寬高.就用他的值就可以.如果子view是match_parent.那就使用父view提供的寬高. 如果子view是wrap_content,那就不能超過父view的值.
看下ViewGroup為子view繪制而提供的方法,可以看到.ViewGroup會(huì)減去padding和margin,來提供子view的寬高.
viewgroup.measureChildWithMargins
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
Framelayout.onMeasure
- 遍歷子view.通過ViewGroup提供的measureChildWithMargins拿到子view的寬高.并用最大值作為framelayout的寬高
- 最大值在加上framelayout的padding
- 通過setMeasureDemision 來設(shè)置framelayout的寬高
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
//遍歷子view.通過ViewGroup提供的measureChildWithMargins拿到子view的寬高.
并用最大值作為framelayout的寬高
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
}
}
//最大值在加上framelayout的padding
// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
通過setMeasureDemision 來設(shè)置framelayout的寬高
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}
ViewRootImpl.performLayout
上步measure過程未完成后,整個(gè)view書的 測量寬高都得到了.也就是view.getMeasuredWidth()和getMeasuredHeight()
performLayout中會(huì)調(diào)用mView.layout. 這樣就把事件從ViewRootImpl傳遞到了view.而layout中又會(huì)調(diào)用onLayout.ViewGroup需要重寫onLayout為子view進(jìn)行布局,遍歷調(diào)用子view的layout.因此就完成整個(gè)view樹的laylut過程.
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
//host就是view書的根view.對(duì)與activity就是docerView
//這里傳入的參數(shù)就是measure 過程測量出的view的寬高.作為左上右下的左邊傳給layout
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
view.layout
- 通過setframe設(shè)定view本身的布局位置,這里都是以fuview左上角作為標(biāo)點(diǎn).
- 調(diào)用onLayout. 整個(gè)方法在view中是空的.這是給ViewGroup用來布局子view的.
- onLayout 和具體布局有關(guān).viewGroup也沒有實(shí)現(xiàn),而是具體的ViewGroup的集成類自己實(shí)現(xiàn).ViewGroup的onLayout是抽象方法.必須要子類實(shí)現(xiàn)
public void layout(int l, int t, int r, int b) {
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
onLayout(changed, l, t, r, b);
}
LinearLayout.onLayout
豎向的實(shí)現(xiàn), 豎向的就行把view從上到下一次排開
void layoutVertical(int left, int top, int right, int bottom) {
final int paddingLeft = mPaddingLeft;
int childTop;
int childLeft;
// ViewGroup的寬度
final int width = right - left;
int childRight = width - mPaddingRight;
final int count = getVirtualChildCount();
childTop = mPaddingTop;
//因?yàn)槭菑纳系较屡砰_,計(jì)算每個(gè)view的top值,top值就是view.getMeasureHeigh+view. topMargin+view.bottomPargin +LinearLayout的paddingTop
所以這里的子view的top值是不斷變大.因此.view就依次從上到下擺開了.
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child.getVisibility() != GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();
childLeft = paddingLeft + lp.leftMargin;
childTop += lp.topMargin;
設(shè)置ziview的四個(gè)角的坐標(biāo).調(diào)用子view的layout.重復(fù)layout過程.
setChildFrame(child, childLeft, childTop + getLocationOffset(child),
childWidth, childHeight);
childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
}
}
}
這里注意區(qū)分.measure過程是先得到子view的測量值,在設(shè)定父ViewGroup的值.而layout過程則是先傳入父view的左上右下值,來計(jì)算子view的左上右下的位置值.這里應(yīng)該具有普遍性.但不知道是否絕對(duì).
ViewRootImpl.performDraw
performDraw 中的調(diào)用draw.又調(diào)用mView.draw.然后就進(jìn)入view樹的繪制了.
view的draw 又會(huì)調(diào)用onDraw ,viewGroup又調(diào)用dispatchDraw()把draw分發(fā)到子view里 繪制的畫布就是canvas. 這是從surface.lockCanvas中獲得的一個(gè)區(qū)域.
而在ViewGroup.dispatchDraw中.重要的一點(diǎn)是getChildDrawingOrder 表示子view的繪制順序.默認(rèn)是與ziview的添加順序一樣.我們也可以改變他.最后繪制的會(huì)顯示在最上邊,而這也影響view的事件傳遞順序.
view.draw. 就是一層一層的畫內(nèi)容.先畫北京,在onDraw.在畫裝飾什么的.
控件樹繪制講解
canvas.translate(100,300)通過平移坐標(biāo)系.使之后的內(nèi)容可以直接在新坐標(biāo)系中繪制.
這就是ViewGroup在向子view傳遞canvas的時(shí)候.方便多了. 會(huì)之前先對(duì)其ziview的左上角.那么子view就可以直接從自己坐標(biāo)軸的(0,0)開始繪制, 繪制完成后ViewGroup在還原原有坐標(biāo)系.
canvas.save. canvas.restore 用來保存還原坐標(biāo)系.
view.invalidate.
當(dāng)某個(gè)view發(fā)送變化需要重繪時(shí),通過view.invalidate向上通知到ViewRootImpl.從這個(gè)view到ViewRootImpl的節(jié)點(diǎn)都標(biāo)記為藏區(qū)域.dirty area. ViewRootimpl再次從上到下重繪時(shí),只繪制這些臟區(qū)域.效率高.
輸入事件派發(fā)
觸摸模式
本來安卓兼容使用鍵盤,也支持,觸摸.二者的輸入事件派發(fā)不一樣.使用鍵盤時(shí)會(huì)有個(gè)控件處于獲得焦點(diǎn)狀態(tài).處于觸摸模式則由用戶決定. 因此控件分為兩類.任何情況下都能獲得焦點(diǎn).如輸入文本框.只有在鍵盤操作時(shí)才能獲得焦點(diǎn).如菜單,按鈕.
安卓里有觸摸模式.當(dāng)發(fā)送任意觸摸時(shí)進(jìn)入觸摸模式.當(dāng)發(fā)送方向鍵和鍵盤或者執(zhí)行View.requestRocusFromTouch時(shí),退出觸摸模式.
View.requestFocus
獲取焦點(diǎn). view.request.
先檢查是否能獲取焦點(diǎn),
然后設(shè)置獲取簡單的標(biāo)記,
向上傳遞到ViewRootimpl.保證只能有一個(gè)控件獲取焦點(diǎn).
通知焦點(diǎn)變化的監(jiān)聽者.
更新view的drawable狀態(tài),
void handleFocusGainInternal(@FocusRealDirection int direction, Rect previouslyFocusedRect) {
if ((mPrivateFlags & PFLAG_FOCUSED) == 0) {
mPrivateFlags |= PFLAG_FOCUSED;
View oldFocus = (mAttachInfo != null) ? getRootView().findFocus() : null;
if (mParent != null) {
mParent.requestChildFocus(this, this);
updateFocusedInCluster(oldFocus, direction);
}
if (mAttachInfo != null) {
mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, this);
}
onFocusChanged(true, direction, previouslyFocusedRect);
refreshDrawableState();
}
}
requestChildFocus會(huì)把焦點(diǎn)事件層層上報(bào)取消原來有焦點(diǎn)的控件.最后的效果就是從viewrootimpl中.到最終有焦點(diǎn)的view.構(gòu)成一條 mFoucued 標(biāo)識(shí)的鏈條.來個(gè)圖就明白了.每個(gè)view的mFocused總是指向他的直接下級(jí).
獲取focus的傳遞是從底層view到頂層的ViewRootImpl.而取消focus測試從頂層的ViewRootimpl到底層原來那個(gè)獲得焦點(diǎn)的view.
ViewGroup.requestFocus
而如果是ViewGroup請(qǐng)求獲取焦點(diǎn),會(huì)根據(jù)FLAG_MASK_FOCUSABILITY特性來做不同方式,分別有先讓自己獲取焦點(diǎn),或者安卓view的索引遞增或者遞減來匹配view.
事件派發(fā)
ViewRootImpl 中的.WindowInputEventReceiver接受輸入事件.他會(huì)把事件包裝成一個(gè)QueuedInputEvent.然后追加到一個(gè)單鏈表的末尾.接著重頭到尾的處理輸入事件,并通過deliverInputEvent完成分發(fā).這里會(huì)把單鏈表所有事件都處理完.
deliverInput中又會(huì)把觸摸事件執(zhí)行到通過 ViewPreImeInputStage.processKeyEvent. 轉(zhuǎn)入mView.dispatchPointerEvent(event).這里又進(jìn)入 dispatchTouchEvent
MotionEvent是觸摸事件的封裝.getAction可以拿到動(dòng)作的類型和觸控點(diǎn)索引號(hào).
getX(),getY().拿到動(dòng)作的位置信息.通過getPointID拿到觸控點(diǎn)的id. 動(dòng)作以down 開頭.跟多個(gè)move.最后是up.
view.dispatchTouchEvent
,當(dāng)事件返回true.表示事件被消費(fèi)掉了.
public boolean dispatchTouchEvent(MotionEvent event) {
//安全模式.不允許窗口被遮擋.
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//回調(diào)listener的ontouch
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//回調(diào)onTouchEvent
if (!result && onTouchEvent(event)) {
result = true;
}
}
}
ViewGroup.dispatchTouchEvent
因?yàn)橛卸帱c(diǎn)觸控.ViewGroup可能把motionevent拆成多個(gè)事件,分發(fā)給多個(gè)ziview.因此他內(nèi)部的mFirstTouchTarget是單向鏈表.存儲(chǔ)所有要處理事件的子view.
public boolean dispatchTouchEvent(MotionEvent ev) {
//同樣.對(duì)窗口遮擋進(jìn)行檢查.
if (onFilterTouchEventForSecurity(ev)) {
down是事件開始.清楚原有的狀態(tài).
if (actionMasked == MotionEvent.ACTION_DOWN) {
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//是否子view阻止ViewGroup進(jìn)行事件攔截,不阻止的話,ViewGroup判斷是否攔截.
//ViewGroup可以攔截事件.但是里邊的子view也可以不然ViewGroup攔截.并且子view的權(quán)限更高.
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
if (!canceled && !intercepted) {
事件派發(fā).獲取觸控點(diǎn)和索引號(hào)
final int actionIndex = ev.getActionIndex();
final View[] children = mChildren;
//遍歷子view.找到適合派發(fā)事件的view.進(jìn)行派發(fā)
for (int i = childrenCount - 1; i >= 0; i--) {
//進(jìn)行事件派發(fā),如果成功,就把這個(gè)子view加入到mFirstTouchTarget中.之后的move等事件直接派發(fā).
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
}
如果最后子view沒有處理事件.則要把時(shí)間交給ViewGroup自己.這樣又層層上報(bào)了回來.
}
}
dispatchTransformedTouchEvent這又會(huì)調(diào)用子view的dispatchTouchEvent.進(jìn)行派發(fā).這樣事件就從ViewRootImpl逐漸派發(fā)到底了.同時(shí)會(huì)保存執(zhí)行down事件的view.之后的move.up事件都有他來處理.
理解PhoneWindow
phonewindow是與activity 一對(duì)一匹配的.是在ActivityThread執(zhí)行 performLaunchActivity.activity創(chuàng)建后執(zhí)行activit.attach的時(shí)候生成的.他管理view樹,各種事件回調(diào).和WindowManager.layoutparams. 用來提供簡單的方法創(chuàng)造activity的外觀. activity.setcontentView. 就是調(diào)用PhoneWindow.setContentView. 而我們傳入的layout. 只是作為view樹的一個(gè)子view. PhoneWindow管理的view書是DocerView. ViewRootImpl里的mView也是他.他作為跟控件.把很多回調(diào)交給了PhoneWindow.
比如在dispatcTochEvent 中
final Window.Callback cb = mWindow.getCallback();final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event): super.dispatchKeyEvent(event);
直接交給. Window.callback處理.這個(gè)callback就是activity了.看看activity中的處理
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//這里又調(diào)用了DockerView的super.dispatchTouchEvent.也就是交還控件樹處理.
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
如果控件樹沒有處理.則返回給activity在處理.
return onTouchEvent(ev);
}
因此.activity的dispatchTouchEvent 是先于控件樹觸發(fā)的.我們可以重寫這個(gè)方法來實(shí)現(xiàn)我們的功能.
activity在創(chuàng)建后.現(xiàn)在執(zhí)行 attach.初始化一寫本地參數(shù).如下
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) {
先創(chuàng)建PhoneWindow.
mWindow = new PhoneWindow(this, window, activityConfigCallback);
//設(shè)置activity作為事件回調(diào).
mWindow.setCallback(this);
//和WMS交流的token
mToken = token;
//拿到windowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
}
attach之后.activity就有了和WMS交流的 PhoneWindow.WindowManager.和WMS交互的token記著這個(gè)token來自AMS. 但是在這之前AMS已經(jīng)把這個(gè)token注冊(cè)到WMS中.因此此時(shí)可直接使用.
Activity的顯示發(fā)送在onResume之后,由ActivithThread.handlerResumeActivity觸發(fā).
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
//執(zhí)行activity.onresume
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//此時(shí)窗口還沒注冊(cè).所以沒有顯示出來.
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;
//這里通過WindowManager-windowmanagerglobal-viewrootImp,把窗口注冊(cè)到WMS
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
//wms里注冊(cè)完成.讓頁面展示出來.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
}
Activity 啟動(dòng)整體流程
startActivity開始時(shí)會(huì)通過ActivityManagerNative.getDefault().startActivity調(diào)用AMS開始.又跳轉(zhuǎn)到了ActivityStack.startActivityMayWait.我們從這里分析.
ActivityStack.startActivityMayWait
- 通過packageManager解析intent得到ResolveInfo
ResolveInfo rInfo =AppGlobals.getPackageManager().resolveIntent()
得到acitivity的 source和result 也就是從哪里來,結(jié)束后返回到那里
創(chuàng)建activityRecord.
他在AMS里代表一個(gè)activity的記錄.每次啟動(dòng)一個(gè)activity就會(huì)生成一個(gè)ActivityRecord. ActivityRecord有個(gè)token變量.這個(gè)token會(huì)傳給WMS.這樣一個(gè)activity在AMS.WMS里就連在一起了.
處理activityRecord 的launchMode和flag.
通過WMS.addAppToken.把AMS里這個(gè)ActivityRecord的appToken關(guān)聯(lián)到WMS里的WindowToken.這樣activity.AMS.WMS.里就都可以通過這個(gè)token來識(shí)別對(duì)方.也就都能定位到這個(gè)activity了.這個(gè)apptoken會(huì)隨著activity的啟動(dòng).由AMS在發(fā)給Activity
為ActivityRecord 創(chuàng)建一個(gè)新的TaskRecord. 每個(gè)ActivityRecord都會(huì)屬于一個(gè)TaskRecord.表示他所屬的任務(wù)棧.
-
找到棧頂正在運(yùn)行的ActivityRecord. 暫停他.好為我們啟動(dòng)新的avtivity騰出空間.
ActivityRecord next = topRunningActivityLocked(null);
startPausingLocked(userLeaving, false); 這里會(huì)調(diào)用要pause的那個(gè)ActivityRecord對(duì)應(yīng)的thread.來執(zhí)行pause,這里的thread是ApplicationThread. 是APP進(jìn)程傳遞給AMS的binder.用于AMS向APP發(fā)信息.
AMS 和ApplicationThrad通過binder通信.把消息帶給舊activity進(jìn)程ActivityThread. 然后執(zhí)行ActivityThread.handlePauseActivity ->ActivityThread.ActivityThread ->AMS.activityPaused,這里回調(diào)AMS表示pause完成.然后AMS繼續(xù)執(zhí)行新activity的啟動(dòng).
WMS 隱藏前一個(gè)activity的窗口
mService.mWindowManager.setAppVisibility(prev.appToken, false);
- 舊activity執(zhí)行pause后.新activity的 ProcessRecord還始終是空, 這時(shí)會(huì)為他創(chuàng)建ProcessRecord,這時(shí)通過activity所在的APP的包名和他的uid創(chuàng)建.
? ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid);
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
? "activity", r.intent.getComponent(), false, false);
- 然后通過AMS.啟動(dòng)ActivityThread線程.這時(shí)通過socket想Zygote進(jìn)行發(fā)消息.然后zygote進(jìn)程fork出來的app進(jìn)程.然后執(zhí)行ActivityThread.main函數(shù)
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
? app.processName, uid, uid, gids, debugFlags, mountExternal,
? app.info.targetSdkVersion, null, null);
11.ActivityThread就是APP的住線程.他初始化了Lopper.并向AMS執(zhí)行AttachApplication方法表示自己啟動(dòng)成功.
12.AMS接受 ActivityThread的信息后.包裝這個(gè)進(jìn)程對(duì)應(yīng)的ProcessRecord.此時(shí)Activity對(duì)應(yīng)的進(jìn)程已經(jīng)啟動(dòng).要開始啟動(dòng)Activity了. AMS找到頂端要啟動(dòng)的activity.命令A(yù)PP進(jìn)程開始啟動(dòng)
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
? System.identityHashCode(r), r.info,
? new Configuration(mService.mConfiguration),
? r.compat, r.icicle, results, newIntents, !andResume,
? mService.isNextTransitionForward(), profileFile, profileFd,
? profileAutoStop);
- ActivityThread開始啟動(dòng)activity. 先根據(jù)package信息創(chuàng)建Application對(duì)象.也就是androidmainfest里Application標(biāo)簽的信息來創(chuàng)建的,然后反射創(chuàng)建activity對(duì)象.執(zhí)行activity.attach(),attach里.會(huì)為activity綁定一個(gè)phoneWindow對(duì)象,一個(gè)WindowManager對(duì)象. phoneWindow是窗口的抽象.他負(fù)責(zé)管理view樹,窗口屬性LayoutParams.是activity和view樹的橋梁.
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
activity.attach(appContext, this, getInstrumentation(), r.token,
? r.ident, app, r.intent, r.activityInfo, title, r.parent,
? r.embeddedID, r.lastNonConfigurationInstances, config);
-
繼續(xù)執(zhí)行activity.onCreate,這里又會(huì)執(zhí)行setContentView .而setContentView是執(zhí)行的PhoneWindow的同名方法,這里會(huì)初始化view樹的跟布局Docerview.并把我們的布局加入他的ziview中.這時(shí)整個(gè)控件樹就完成了.這也就看到.整個(gè)空間樹的根是DocerView并且由Phonewindow管理.
mDecor = generateDecor();
mLayoutInflater.inflate(layoutResID, mContentParent);
-
繼續(xù)執(zhí)行activity.onrestart. 然后在繼續(xù)有AcitvityThread處理activity的resume,這里是先執(zhí)行activity的onResume.然后要把a(bǔ)ctivity的窗口注冊(cè)到WMS中.并先隱藏窗口.等WMS窗口注冊(cè)成功,再把窗口顯示出來.
performResumeActivity,(token, clearHide);
View decor = r.window.getDecorView();
? decor.setVisibility(View.INVISIBLE);
? ViewManager wm = a.getWindowManager();
? wm.addView(decor, l);
-
這里的wm 就是之前activity執(zhí)行attach時(shí)創(chuàng)建的WindowManager.每個(gè)activity對(duì)應(yīng)一個(gè)WindowManager.不過這個(gè)WindowManager只是個(gè)代理.會(huì)把請(qǐng)求轉(zhuǎn)發(fā)給WindowManagerGlobal.這是個(gè)進(jìn)程唯一的管理類.而WindowManagerGlobal又創(chuàng)建ViewRoomImpl,這是每個(gè)view樹一個(gè)的對(duì)象,有viewrootimpl來addView.
root = new ViewRootImpl(view.getContext(), display);
root.setView(view, wparams, panelParentView);
viewrootimpl里 ,通過IWindowSession調(diào)用WMS來添加窗口,獲得surface,同時(shí)進(jìn)行第一次視圖繪制.
mWindowSession.addToDisplaymWindow, mSeq, mWindowAttributes,
? getHostVisibility(), mDisplay.getDisplayId(),
? mAttachInfo.mContentInsets, mInputChannel)
18.第一次繪制只會(huì)協(xié)商窗口的尺寸和view樹期望的尺寸. 這里又調(diào)用mWindowSession.relayout來同WM協(xié)商窗口的寬高.同時(shí)WMS會(huì)生成surface返回給APP.此后app的繪制都是在這個(gè)canvas的畫板上.
int relayoutResult = mWindowSession.relayout(
? mWindow, mSeq, params,
? (int) (mView.getMeasuredWidth() * appScale + 0.5f),
? (int) (mView.getMeasuredHeight() * appScale + 0.5f),
? viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
? mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
? mPendingConfiguration, mSurface);
-
之后viewrootImpl 通過performMeasure 調(diào)用view.measure 又遍歷調(diào)用view.onMeasure進(jìn)行控件樹的測量
viewrootimpl在通過performLayout調(diào)用view.layout又遍歷調(diào)用view.onLayout 進(jìn)行控件樹的布局
viewrootimpl在通過performDraw調(diào)用view.draw又遍歷調(diào)用view.onDraw 進(jìn)行控件樹的布局
這里.控件樹的繪制就都完成了. 此時(shí)activity已經(jīng)正確顯示出來.
這里再列出出現(xiàn)的一些對(duì)象
ProcessRecord. AMS里對(duì)APP進(jìn)程的抽象對(duì)象.代表一個(gè)進(jìn)程
TaskRecord AMS里對(duì)APP啟動(dòng)棧的抽象.這個(gè)和啟動(dòng)模式相關(guān).如singleTop.singleInstance
ActivityRecord AMS里對(duì)APP啟動(dòng)一個(gè)Activity的抽象.一個(gè)Activity在如果在棧中有多個(gè)對(duì)象,就有多個(gè)ACtivityRecord
ACtivityStack. AMS里操作APP啟動(dòng)的類. AMS把啟動(dòng)的很多功能都交給他來完成
ActivityRecord.appToken. 這個(gè)token是聯(lián)系一個(gè)activity在AMS和WMS里的指令令牌.由AMS創(chuàng)建.WMS會(huì)把這個(gè)令牌和一個(gè)WindowToken進(jìn)行匹配. 從而表示一個(gè)activity
ApplicationThread. 這是一個(gè)binder.提供給AMS 的 屬于app進(jìn)程.AMS通過他來調(diào)用App進(jìn)程
ActivityThread APP的主線程. ApplicationThread就是屬于ActivityThread的.ApplicationThread接到消息后發(fā)送到ActivityThread的小弟隊(duì)列中.等待ActivityThread處理.
AppWindowToken 是WindowToken的子類.用來標(biāo)識(shí)一個(gè)activity在WMS端的記錄.一個(gè)啟動(dòng)了的activity對(duì)應(yīng)一個(gè)AppwindowToken.
WindowState 是WMS用來標(biāo)識(shí)一個(gè)窗口.每個(gè)WindowState 有一個(gè)W的binder. 這個(gè)W是iWindow類.是APP進(jìn)程提供給WMS的回調(diào)binder. 每個(gè)WindowState 標(biāo)識(shí)一個(gè)窗口.而一個(gè)activity可能有多個(gè)窗口,因此,就是一個(gè)activity在WMS里對(duì)應(yīng)多個(gè)WindowState.也對(duì)應(yīng)多個(gè)IWindow的回調(diào). 但是他們通過同一個(gè)WindowToken來對(duì)應(yīng)APP端的一個(gè)activity.
PhoneWindow.每個(gè)activity里一個(gè).標(biāo)識(shí)一個(gè)窗口,他持有窗口的屬性layoutparams.控件樹,和控件觸摸事件的回調(diào).是Activity和控件樹的中間管理.
Viewrootimpl. 每個(gè)控件樹的管理者.每個(gè)控件樹有一個(gè). 真正和WMS的交互是通過Viewrootimpl.他同時(shí)負(fù)責(zé)繪制流程的傳遞. 觸摸事件的傳遞.activity的默認(rèn)控件樹是DocerView. ViewRootImpl里有內(nèi)部類IWindow,也就是他接受WMS的回調(diào). Viewrootimpl接受觸摸實(shí)際發(fā)生后.會(huì)傳給控件樹. 對(duì)應(yīng)activity則是docerview,而Docerview會(huì)先把時(shí)間傳遞給對(duì)應(yīng)的activity.如果activity不處理,在傳遞給控件樹,如果控件樹還不處理.最后還是返回給activity.
IWindowSession. 這是APP端和WMS通信的binder. 每個(gè)進(jìn)程唯一.
WindowManager.每個(gè)activity一個(gè).用來管理窗口的添加更新等.他只是一個(gè)代理.把事件會(huì)傳遞給WindowManagerGlobal
WindowManagerGlobal .每個(gè)進(jìn)程一個(gè). 負(fù)責(zé)管理該進(jìn)程所有窗口的添加等.ViewRootImpl就是由他為每個(gè)view控件樹創(chuàng)建的
最后附一張總圖