大概
延續(xù)上一篇文章(View·InputEvent事件投遞源碼分析(一))得出的結(jié)論撮躁,本文接著對(duì) View菩佑、ViewGroup 的事件派發(fā)灾螃、攔截進(jìn)行源碼分析凿试。
ViewRootImpl#setView 里的 View 是什么?
上一篇文章得到 View 的屏幕觸摸事件的處理由 ViewPostImeInputStage 類進(jìn)行處理褐鸥。
// ViewRootImpl.java
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
// ……
boolean handled = mView.dispatchPointerEvent(event);
// ……
return handled ? FINISH_HANDLED : FORWARD;
}
【 Tips : 閱讀源碼的時(shí)候线脚,時(shí)刻需要帶著問題去找答案〗虚牛】
分析上述代碼我們需要先確定 mView 指代的含義浑侥,再我們繼續(xù)分析之前。
先有如下猜想:
1晰绎、 mView 是獲得焦點(diǎn)的 View寓落;
2、mView 是頂層的 DecorView荞下;
我們通過查找 mView 的實(shí)例伶选,很容易找到 setView 方法。這個(gè)方法將 mView 與 ViewRootImpl 互相綁定尖昏,mView 的身份已經(jīng)呼之欲出了仰税。
但是在沒有足夠的證據(jù)(代碼)說明之前,我們對(duì)結(jié)果仍保持懷疑抽诉。
// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
requestLayout();
if (...) {
mInputChannel = new InputChannel();
}
...
try {
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
...
} finally {
...
}
}
}
}
因?yàn)?setView 是實(shí)例方法陨簇,所以可以將注意點(diǎn)先轉(zhuǎn)移到 ViewRootImpl 對(duì)象是如何被創(chuàng)建的。
我們發(fā)現(xiàn) ViewRootImpl 的構(gòu)造器權(quán)限是 public 的迹淌,所以不存在單例調(diào)用河绽。
一番查找在 WindowManagerGlobal 的 addView 中找到了如下代碼。WindowManagerGlobal 提供與系統(tǒng)窗口管理器的低級(jí)別通信唉窃,用于與任何特定上下文無關(guān)的操作耙饰。
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ……
ViewRootImpl root;
// ……
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 {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...
}
throw e;
}
}
原來 setView 在 WindowManagerGlobal 中被調(diào)用,而傳入的 View 就是 DecorView句携。
ViewRootImpl 與 WindowManagerService 通訊分析
WindowManagerImpl 作為 WindowManagerGlobal的代理持有了WindowManagerGlobal對(duì)象榔幸。
public final class WindowManagerImpl implements WindowManager {
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);
}
}
繼續(xù)跟蹤 WindowManagerImpl 對(duì)象允乐,發(fā)現(xiàn)其早已注冊(cè)到 WindowManagerService 中矮嫉。
// SystemServiceRegistry.java
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx.getDisplay());
}});
在得知WindowManagerImpl與WindowManagerService有關(guān)聯(lián)的情況下,第一時(shí)間去WindowManagerService中查找WindowManager對(duì)象牍疏。
但搜索一番之后并沒有發(fā)現(xiàn)WindowManager的實(shí)例蠢笋,但是他們之前必然是有聯(lián)系的。
既然沒有表現(xiàn)的那么直白鳞陨,那肯定是通過中介的形式轉(zhuǎn)發(fā)了昨寞。而WindowManagerService又有著對(duì) Window 的最終實(shí)現(xiàn)瞻惋,于是乎我們重新回到 ViewRootImpl 中找到 setView中的代碼片段。
// ViewRootImpl.java
final IWindowSession mWindowSession;
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
mWindowSession 顧名思義應(yīng)是兩者之間的會(huì)話機(jī)制援岩。
public interface IWindowSession extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements android.view.IWindowSession {
...
}
}
而Session是IWindowSession.Stub的實(shí)現(xiàn)類歼狼,Session中也持有了WindowManagerService的實(shí)例。
final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
final WindowManagerService mService;
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
}
代碼又回到 WindowManagerService 中享怀,最后通過 window 的openInputChannel方法打開了與 ViewRootImpl 通信的渠道羽峰。
最后是通過 InputManagerService 打開 InputChannel ,提供了通訊的環(huán)境添瓷。
// WindowServiceManagerImpl.java
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
...
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
...
}
// WindowState
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
// If the window died visible, we setup a dummy input channel, so that taps
// can still detected by input monitor channel, and we can relaunch the app.
// Create dummy event receiver that simply reports all events as handled.
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
void disposeInputChannel() {
if (mDeadWindowEventReceiver != null) {
mDeadWindowEventReceiver.dispose();
mDeadWindowEventReceiver = null;
}
// unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
mService.mInputManager.unregisterInputChannel(mInputChannel);
mInputChannel.dispose();
mInputChannel = null;
}
if (mClientChannel != null) {
mClientChannel.dispose();
mClientChannel = null;
}
mInputWindowHandle.inputChannel = null;
}
DecorView 的事件派發(fā)
// View.java
public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}
// DecorView.java
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final Window.Callback cb = mWindow.getCallback();
return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
當(dāng)有 cb 對(duì)象時(shí)即可傳遞給 cb 對(duì)象去處理梅屉,否則交給 view 去處理。而 Activity 又實(shí)現(xiàn)了 cb 對(duì)象的接口鳞贷。
- 所以首次的dispatchTouchEvent 事件交給 Activity 去處理坯汤。
- 接著在 Activity 處理時(shí)會(huì)再次將事件拋給 DecorView 的 superDispatchTouchEvent 方法。而 superDispatchTouchEvent 的方法就是 ViewGroup 的 dispatchTouchEvent 方法搀愧。
- 經(jīng)歷了上述兩步之后惰聂,ViewGroup 的事件分發(fā)也就真正意義上的開始了。
// Activity.java
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
結(jié)束語
到此咱筛,事件從觸發(fā)到分發(fā)的第一步已經(jīng)分析透徹了庶近,下一章將描述具體的分發(fā)過程。這也是大多博客反復(fù)寫眷蚓,且覆蓋率相當(dāng)高的一篇文章鼻种。倒不是執(zhí)意要去造輪子,只是想通過自己對(duì)源碼的分析加深對(duì)分發(fā)過程的理解沙热。
[[Android中的dispatchTouchEvent()叉钥、onInterceptTouchEvent()和onTouchEvent()]:http://blog.csdn.net/xyz_lmn/article/details/12517911
[Android中的dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()]:http://blog.csdn.net/xyz_lmn/article/details/12517911