深入理解View知识系列三-Window机制、Canvas的由来、Android事件的由来

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yulong0809/article/details/79277633

我们在第一篇和第二篇中都涉及到了Window、WindowManager、PhoneWindow,而他们到底是什么,在第二篇又出现了WindowManagerImpl、WindowManagerGlobal、WindowSession、WindowManagerService,这么一堆的东西又都是干什么?起到什么样的作用?每个Activitiy都会存在一个Window,那么整个应用又一共有多少个Window?你听说过Surface吗?你知道Canvas是怎么来的吗?本篇就来详细的说说他们

本篇你会学到什么

  • 上面提到一堆Windowxxx都是干什么的
  • Window的类型和级别
  • Surface是干什么的
  • View中的ondraw方法里的Canvas对象是哪来的?
  • WMS添加Window的过程,和事件传递机制

同样的我们先来回顾一下上一篇的内容

Activity是在哪里准备显示View的

在setContentView之后,Activity会继续走它自己的生命周期方法,接着会走到ActivityThread中的handleResumeActivity中,在这个方法中会执行如下几部(只涉及View知识,其他的逻辑会在以后说)。

  • 首先会执行performResumeActivity方法,在这里会回调Activity的onResume方法
  • 接着会获取Activity的PhoneWindow和PhoneWindow中的DecorView,并把DecorView设置为INVISIBLE
  • 然后获取在Activity中设置的WindowManager,并调用addView方法添加DecorView
  • 最后调用Activity的makeVisible方法来将DecorView设置为VISIBLE

通过这几步我们还总结了两点

1.Activity是先回调了Activity的onResume之后才开始添加DecorView的,也就是说在oResume中是不能得到View的宽高参数的

2.Activity也是通过WindowManager来添加View的,这和我们平时自己使用WindowManger来添加一个View的流程类似。

WindowManger添加View的过程

WindowManger的是实现类是WindowManagerImpl,在WindowManagerImpl中包括addView在内操作View的三大方法全部使用了桥接到了WindowManagerGloble中,WindowManagerGloble是一个单例类,那么也就说明每个进程中所有对View的三大操作全部都只在这一个类中进行管理,在这里主要执行了如下几步。

  • 检查传入参数的合法性
  • 判断是否存在父Window,如果有会根据父Window调整一些信息参数
  • 创建ViewRootImpl,并调用它的setView方法
  • 将ViewRootImpl、LayoutParams、View添加进各自的集合中,这是因为WindowManagerGloble是一个单例类,无论存在多少个Window但是只存在一个WindowManagerGloble,所以要将这些东西存起来,以便于后面删除更新等操作。

ViewRootImpl的绘制开始点

在WindowManagerGloble中创建了ViewRootImpl,并调用了它的setView方法,逻辑转入到ViewRootImpl中,主要逻辑如下

  • setView方法中主要通过requestLayout方法来准备开始View的绘制流程,通过WindowSession.addToDisplay完成Window添加的过程,还调用了View的assignParent方法来设置顶层View的Parent指向ViewRootImpl
  • 在requestLayout的方法中先调用了checkThread来检查当前是否主线程
  • 又调用了scheduleTraversals方法,在这里首先使用MessageQueue开启了一个同步消息屏障,这样就导致了主线程同步的消息将会暂停处理,目的是加快绘制的速度。
  • 接着通过了Choreographer传递了一个Runnable,最终会执行Runnable中的run方法调用performTraversals来真正调用View的三大流程方法,即measure、layout、draw,
  • 还有一些其他的知识点,例如为什么在onCreate中调用View.post方法就可以得到View的宽高、requestLayout请求重绘的过程等,详细的请看上一篇

开始之前我们先说一下一堆涉及Windowxxx的概念,本编源码基于android 7.1.1

Window :

顾名思义就是一个窗口,Android中所有的视图都需要通过Window来展示,它是一个抽象类,例如Activity、Toast、Dialog、PopupWindow等都必须通过Window才可以展示,它内部直接操作管理着View,可以说它是View的直接管理者,也可以说是相辅相成,互相依赖,谁离了谁都不行,而Window又分为三大类,这一点体现在WindowManager.LayoutParams中声明的常量中,这些常量用于type这个参数上,而且从声明中可以看出它们是分层级的,层级大的会覆盖在层级小的上面,如下

  • 应用Window : 应用Window的层级对应于1-99,一般对应一个Activity
    public static final int FIRST_APPLICATION_WINDOW = 1;
public static final int TYPE_BASE_APPLICATION = 1;
public static final int TYPE_APPLICATION = 2;
public static final int TYPE_APPLICATION_STARTING = 3;
public static final int TYPE_DRAWN_APPLICATION = 4;
public static final int LAST_APPLICATION_WINDOW = 99;
  • 子Window : 子Window表示需要依赖于父Window才能存在的Window,它不能单独存在,例如Dialog、PopupWindow等,需要依赖Activity的应用Window才能存在。它的层级对应于1000-1999
    public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
public static final int LAST_SUB_WINDOW = 1999;
  • 系统Window : 系统Window是级别最高的Window,一般如果使用的话需要申请权限,例如Toast、输入法锁屏、系统警告、系统状态栏等都是系统Window,它的层级对应于2000-2999
public static final int FIRST_SYSTEM_WINDOW     = 2000;
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
....
public static final int LAST_SYSTEM_WINDOW = 2999;

如果没有设置type这个字段的值的话,默认是应用及Window,是TYPE_APPLICATION,这里从源码的WindowManager.LayoutParams的构造方法中可以看出来,

//LayoutParams的构造方法
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
//默认值是2
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}

PhoneWindow :

PhoneWindow是Window的唯一实现类,内部管理着最顶层的View,即DecorView

ViewManager :

一个接口,内部定义了操作View的三个方法,即addView、updateViewLayout、removeView

WindowManager:

也是一个接口继承了ViewManager,也是WindowManagerService代理包装类,这一点从context.getSystemService的源码中有体现,来看一下

    //1.ContextImpl
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
//2.SystemServiceRegistry
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
//3.registerService
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher)
{
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
//4.注册服务
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});

WindowManagerImpl:

WindowManager的实现类,从上面注册服务的源码可以看出来,最终返回的是WindowManagerImpl,但是它并没有真正实现View的三大操作,全部桥接到了WindowMangerGloble

WindowManagerGloble :

真正实现View三大操作的类,它是一个单例类,它还管理着所有Window中的ViewRootImpl、DecorView、LayoutParams

ViewRootImpl :

整个View树的根,在WindowManagerGloble中的addView中被创建,是顶层DecorView的ViewParent,控制着整个View树的运作,包括measure、layout、draw、重绘、事件传递派发等

IWindowSession :

一个aidl接口,它的真的实现类是Session,它是一个Binder对象,用来和WindowManagerService建立连接,在ViewRootImpl的setView中最终也是通过它和WindowManagerService通信完成了Window的添加的。这个Session是应用唯一的,它的创建时在WindowManagerGloable中通过getWindowSession获取的

Session :

IWindowSession的真正实现类,这一点在WindowManagerGloble的获取中可以体现

//WindowManagerGloble
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
//调用WMS的openSession
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
//WindowManagerService
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext)
{
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
//真正返回的是Session
Session session = new Session(this, callback, client, inputContext);
return session;
}

IWindow :

也是一个aidl接口,它的实现是在ViewRootImpl中的内部类W,在添加window的时候会将ViewRootImpl中的W传递到WMS中并封装成WindowState对象,这个类会监听一些窗口的变化并回调给ViewRootImpl中的W类,也可以简单的理解成就是一个WMS的远程回调接口

    static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
mWindowSession = viewAncestor.mWindowSession;
}
@Override
public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
Configuration newConfig, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar)
{
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
visibleInsets, stableInsets, outsets, reportDraw, newConfig, backDropFrame,
forceLayout, alwaysConsumeNavBar);
}
}
@Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchMoved(newX, newY);
}
}
@Override
public void dispatchAppVisibility(boolean visible) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchAppVisibility(visible);
}
}
@Override
public void dispatchGetNewSurface() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchGetNewSurface();
}
}
.....
}

Surface :

其实可以把它理解成是真正的画布,我们应用绘制View用的Canvas就是通过Surface来创建的,每一个Window中都存在一个自己的Surface,这个声明在ViewRootImpl中,但是ViewRootImpl中声明的Surface只是一个空壳,在绘制之前会调用relayoutWindow到WMS中去真正的创建Surface,它需要通过系统的SurfaceFlinger来整合显示,简单说就是Window需要通过Surface创建的Canvas来绘制要显示的内容,然后系统底层来控制Surface的整合展示,我们看来看一下Surface真正的创建过程和Canvas是怎么得来的

//1.ViewRootImpl中声明的Surface
final Surface mSurface = new Surface();
//2.在ViewRootImpl的绘制方法中,也就是performTraversals中会在执行performDraw之前先去WMS端获取到native层Sureface
private void performTraversals() {
//顶层的View,可以理解成是DecorView
final View host = mView;
...
//通知被添加到窗口了,传入了mAttachInfo
host.dispatchAttachedToWindow(mAttachInfo, 0);
//调用relayoutWindow方法去获取native层的Surface
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
...
//measure过程
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
....
//layout过程
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
....
//通知layout完成了
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
....
//draw过程
performDraw();
}
//3.ViewRootImpl的relayoutWindow方法,去获取native行Surface
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending)
throws RemoteException
{
....
//调用了mWindowSession的relayout方法,上面我们知道IWindowSession真正实现是Session
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, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
mSurface);//最后一个参数传入了ViewRootImpl中的空壳Sureface
....
return relayoutResult;
}
//4.Session中的relayoutWindow
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags,
int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
Configuration outConfig, Surface outSurface)
{
//直接调用了WMS的relayoutWIndow
int res = mService.relayoutWindow(this, window, seq, attrs,
requestedWidth, requestedHeight, viewFlags, flags,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outBackdropFrame, outConfig, outSurface);
return res;
}
//5.WindowManagerService中的relayoutWindow方法
public int relayoutWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
Configuration outConfig, Surface outSurface)
{
....
//创建SurfaceControl并获取native中创建Surface
result = createSurfaceControl(outSurface, result, win, winAnimator);
....
}
//6.WindowManagerService中的createSurfaceControl方法
private int createSurfaceControl(Surface outSurface, int result, WindowState win,
WindowStateAnimator winAnimator)
{
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
WindowSurfaceController surfaceController = winAnimator.createSurfaceLocked();
if (surfaceController != null) {
//获取native层的Surface
surfaceController.getSurface(outSurface);
} else {
outSurface.release();
}
return result;
}
//7.WindowSurfaceController中的getSurface方法
void getSurface(Surface outSurface) {
//调用了Surface中的copyFrom方法,从方法名体现的是将native层的Surface复制到客户端的空壳中
outSurface.copyFrom(mSurfaceControl);
}
//8.在调用draw之前会先创建Canvas对象
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty)
{
final Canvas canvas;
try {
//使用Surface创建一个Canvas,方法中也是调用了native方法
anvas = mSurface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
canvas.setDensity(mDensity);
}catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
}
....
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
//调用View的draw方法,并传入刚刚创建的Canvas对象
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
....
}

好了,到这里涉及的相关类基本上都介绍清楚了,Window添加View的过程我们在上一遍已经分析过了,接下来会通过IWindowSession最终到WMS里去完成Window的添加,在添加的过程中会注册事件的接收通常,也就是android中的事件分发机制,事件分发是一个很重要的模块,一般大家都会从ViewGroup和View说起,但是有没有想过又是谁分发给了它们呢?下面看看Android的事件到底是从哪里开始的

源码位置:/frameworks/base/core/java/android/view/ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
//最终在这里调用了绘制
requestLayout();
...
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//创建了一个InputChannel实例,可以理解为是和Server端事件消息通道
mInputChannel = new InputChannel();
}
//通过WMS添加Window,mWindow是IWindow类型,ViewRootImpl的内部类W,最后一个参数传入了刚刚创建的通道对象
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
//根据WMS返回的值判断Window是否添加成功,不成功则抛出相应的异常
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
...
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//创建一个用来接收事件的类
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
//设置DecorView的Parent为ViewRootImpl
view.assignParent(this);
...
//创建了一堆的InputStage来处理相应的输入事件,在下面会分析触摸事件时用到
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}

1. 在上面我们看到了首先创建了InputChannel,接着调用了mWindowSession.addToDisplay方法去添加窗口,并且传入了mWindow,这个是ViewRootImpl中的内部类W,还有客户端的事件通道InputChannel,mWindowSession上边我们已经说过,他的真正实现是通过WMS的openSession方法返回的Session,最终还创建了一个用来接收事件的WindowInputEventReceiver类,这个类我们暂且放一会,先接续看Session中的addToDisplay方法

源码位置:/frameworks/base/services/core/java/com/android/server/wm/Session.java

    @Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel)
{
//什么都没做,直接调用了WMS的addWindow方法
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}

2.什么都没做直接调用了WMS中的addWindow方法,接着看

源码位置:源码位置:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.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)
{
int[] appOp = new int[1];
//如果是系统窗口检查是否有权限
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...
final int type = attrs.type;
synchronized(mWindowMap) {
...
//如果mWindowMap中包含这个Window,说明之前添加过,返回添加失败
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
//如果要添加的是一个子Window
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
//获取子Window依赖的父Window
attachedWindow = windowForClientLocked(null, attrs.token, false);
//如果父Window不存在,也返回添加失败,这些失败的处理在ViewRootImpl中
if (attachedWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
//如果父Window的type也是一个子Window,返回添加失败,子Window不能依赖子Window
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
...
//创建WindowState,传入了App端传过来的client,就是IWindow,当窗口发生变化会回调客户端的ViewRootImpl中的W类
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
...
//如果传入的客户端通道不为空,则注册消息通道
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
//android7.1对于类型是TYPE_TOAST的窗口做了限制,防止一个应用的悬浮窗一直悬浮在另一个应用上造成干扰,如果重复添加类型是TOAST的窗口则会抛出异常,如果没有则会正常展示,但是3.5秒后会自动消失,但是3.5未完成窗口的取消则会抛出ANR异常
if (type == TYPE_TOAST) {
if (!getDefaultDisplayContentLocked().canAddToastWindowForUid(callingUid)) {
Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
if (addToastWindowRequiresToken
|| (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
|| mCurrentFocus == null
|| mCurrentFocus.mOwnerUid != callingUid) {
//发送ANR异常消息
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
win.mAttrs.hideTimeoutMilliseconds);
}
}
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
....
return res;
}

3. 在上面的代码中我们可以发现很多WMS对要添加Window的检查工作,并且在7.1里对于类型是TOAST的窗口做了限制,这里还判断了如果客户端传过来的事件消息通道不为空则会注册这个通道到InPutManagerService中,我们来看一下这段代码

源码位置:源码位置:/frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    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 {
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
//将消息通道注册到native层,当有事件产生的时候会出发ViewRootImpl中的WindowInputEventReceiver
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}

4. 上面通过InputManagerService将服务的和客户端的两个InputChannel注册到了native层,而我们客户端的InputChannel是和ViewRootImpl中的WindowInputEventReceiver关联的,当窗口接受到输入事件就会调用ViewRootImpl中的WindowInputEventReceiver,现在我们再回去看看WindowInputEventReceiver这个类把

源码位置:/frameworks/base/core/java/android/view/ViewRootImpl.java

    final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
//接受到事件会到这里进行分发处理
@Override
public void onInputEvent(InputEvent event) {
//然后调用了enqueueInputEvent进行处理
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
if (mUnbufferedInputDispatch) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
}
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}

5.WindowInputEventReceiver继承了InputEventReceiver,它是一个抽象类,其实在这个类创建的时会在构造方法中会到native层做一些初始化,用于来监听输入事件

    //构造函数
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
//nativeInit是一个native方法
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
//当native层发来事件时会走到这里,也就是会调用到ViewRootImpl中WindowInputEventReceiver的这个方法
@SuppressWarnings("unused")
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
//调用实现类的onInputEvnet方法
onInputEvent(event);
}

6.上面的InputEventReceiver当收到事件的时候会调用到实现类的onInputEvent方法,然后又调用了enqueueInputEvent方法来处理,来看看

源码位置:/frameworks/base/core/java/android/view/ViewRootImpl.java

   void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately
)
{
adjustInputEventForCompatibility(event);
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
//将事件消息进行排队处理
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
if (processImmediately) {
//如果是立即处理
doProcessInputEvents();
} else {
//否则使用Handler进行调度,最后也会执行doProcessInputEvents
scheduleProcessInputEvents();
}
}

7. 通过enqueueInputEvent方法可以看出,事件消息会一个一个的进行处理,就例如我们收到的事件是按down、move、up一样,下面接着看

    //doProcessInputEvents方法
void doProcessInputEvents() {
//遍历队列里的所有事件
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
if (q.mEvent instanceof MotionEvent) {
MotionEvent me = (MotionEvent)q.mEvent;
if (me.getHistorySize() > 0) {
oldestEventTime = me.getHistoricalEventTimeNano(0);
}
}
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
//处理每一个事件
deliverInputEvent(q);
}
//当上面所有事件处理完成则移除标记
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
//deliverInputEvent方法
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
//根据事件的不同获取到不同的InputStage
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
//接着使用对应的InputStage处理事件,在上面最开始的时候我们看的ViewRootImpl的setView
//方法中创建了很多不同InputStage,这里我们分析一个View的事件处理,即ViewPostImeInputStage
// InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
if (stage != null) {
//调用deliver
stage.deliver(q);
} else {
finishInputEvent(q);
}
}

8.继续追踪,调用了InputStage的deliver方法

        //InputStage
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
//这里又会调用子类重写的onProcess
apply(q, onProcess(q));
}
}
//ViewPostImeInputStage
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
//按键事件
return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
//触摸屏事件
return processPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
return processTrackballEvent(q);
} else {
return processGenericMotionEvent(q);
}
}
}

8.1 先来看按键事件方法proessKeyEvent

        //ViewRootImpl中的内部类ViewPostImeInputStage中的processKeyEvent方法
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
// 当前的mView是DecorView,调用了它的dispatchKeyEvent
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
}
...
return FORWARD;
}
//DecorView中的dispatchKeyEvent方法
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
//获取事件类型
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
...
//如果Window还存在
if (!mWindow.isDestroyed()) {
//获取Window的Callback,这里我们以前分析过
final Window.Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ?
//调用callback的dispatchKeyEvent
cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
: mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}

8.2 在DecorView中判断如果Window在存在,则获取Window的Callback,然后调用Callback的dispatchKeyEvent方法,之前我们分析过Window的实现类是PhoneWindow,在Activity的attach方法中创建的,并设置Callback为this,也就是Activity

    //Activity中的dispatchKeyEvent方法
public boolean dispatchKeyEvent(KeyEvent event) {
//这是一个空方法
onUserInteraction();
...
View decor = mDecor;
if (decor == null) decor = win.getDecorView();
//最终调用了KeyEvent自己的dispatch方法,注意传入的this
return event.dispatch(this, decor != null
? decor.getKeyDispatcherState() : null, this);
}
//KeyEvent中dispatch方法,上面传递的第一参数this,也就是Activity自己
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
case ACTION_DOWN: {
mFlags &= ~FLAG_START_TRACKING;
//onKeyDown,还是会回调会Activity中的onKeyDown
boolean res = receiver.onKeyDown(mKeyCode, this);
...
return res;
}
case ACTION_UP:
...
//还是会回调会Activity中的onKeyUp
return receiver.onKeyUp(mKeyCode, this);
case ACTION_MULTIPLE:
final int count = mRepeatCount;
final int code = mKeyCode;
if (receiver.onKeyMultiple(code, count, this)) {
return true;
}
if (code != KeyEvent.KEYCODE_UNKNOWN) {
mAction = ACTION_DOWN;
mRepeatCount = 0;
boolean handled = receiver.onKeyDown(code, this);
if (handled) {
mAction = ACTION_UP;
receiver.onKeyUp(code, this);
}
mAction = ACTION_MULTIPLE;
mRepeatCount = count;
return handled;
}
return false;
}
return false;
}

9. 我们知道了Activity中的onKeyDown和onKeyUp事件是从ViewRootImpl中传递过来的,首先调用了DecorView的dispatchKeyEvent,接着通过PhoneWindow拿到Callback接口调用到Activity的dispatchKeyEvent,最后调用通过KeyEvent的dispatch在分发回Activity的onKeyDown和onKeyUp方法,接着我们再来看看触摸事件的分发

        //ViewRootImple中的内部类ViewPostImeInputStage中的processPointerEvent方法
private int processPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
mAttachInfo.mUnbufferedDispatchRequested = false;
//获取当前事件的View,最开始肯定是DecorView
final View eventTarget =
(event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
mCapturingView : mView;
mAttachInfo.mHandlingPointerEvent = true;
//因为DecorView并没有重写dispatchPointerEvent方法,所以最终调用View的dispatchPointerEvent,
boolean handled = eventTarget.dispatchPointerEvent(event);
...
return handled ? FINISH_HANDLED : FORWARD;
}

10. 由于DecorView并没有重写View的dispatchPointerEvent方法,所以最终调用到了View里的dispatchPointerEvent方法,来看看如何处理的

    public final boolean dispatchPointerEvent(MotionEvent event) {
if (event.isTouchEvent()) {
//熟悉了吧
return dispatchTouchEvent(event);
} else {
return dispatchGenericMotionEvent(event);
}
}

好了,到这里事件的由来到此结束,剩下的就是ViewGroup和View等自己处理机制了。到现在相信应该对Android中的Window机制、Canvas和事件的由来应该有一个较深刻的印象了,对于以后我们解决问题和对Android系统的原理理解都将有很大的帮助,下一篇View的三大方法流程以及相关参数的产生。

猜你喜欢

转载自blog.csdn.net/yulong0809/article/details/79277633