Android 深入了解 Window 、Activity、 View 三者关系(下)

简介: addView 成功有一个标志就是能够接收触屏事件,通过对 setContentView 流程的分析,可以看出添加 View 的操作实质上是 PhoneWindow 在全盘操作,背后负责人是 WMS,反之 Activity 自始至终没什么参与感。但是我们也知道当触屏事件发生之后,Touch 事件首先是被传入到 Activity,然后才被下发到布局中的 ViewGroup 或者 View(Touch事件分发 了解一下)。那么 Touch 事件是如何传递到 Activity 上的呢?

又回到Activity


       addView 成功有一个标志就是能够接收触屏事件,通过对 setContentView 流程的分析,可以看出添加 View 的操作实质上是 PhoneWindow 在全盘操作,背后负责人是 WMS,反之 Activity 自始至终没什么参与感。但是我们也知道当触屏事件发生之后,Touch 事件首先是被传入到 Activity,然后才被下发到布局中的 ViewGroup 或者 View(Touch事件分发 了解一下)。那么 Touch 事件是如何传递到 Activity 上的呢?


       ViewRootImpl 中的 setView 方法中,除了调用 IWindowSession 执行跨进程添加 View 之外,还有一项重要的操作就是设置输入事件的处理:


ViewRootImpl.setView


    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
          ..
          res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                      getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                      mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                      mAttachInfo.mDisplayCutout, inputChannel,
                      mTempInsets, mTempControls);
          // 注释:设置输入管道。
          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);
        ...
    }


注释:设置了一系列的输入通道。一个触屏事件的发生是由屏幕发起,然后经过驱动层一系列的优化计算通过 Socket 跨进程通知 Android Framework 层(实际上就是 WMS),最终屏幕的触摸事件会被发送到代码中的输入管道中。


这些输入管道实际上是一个链表结构,当某一个屏幕触摸事件到达其中的 ViewPostImeInputState 时,会经过 onProcess 来处理,如下所示:


ViewRootImpl.ViewPostImeInputStage


 final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }
        @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);
                }
            }
        }
        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            ...
            boolean handled = mView.dispatchPointerEvent(event);
            maybeUpdatePointerIcon(event);
            maybeUpdateTooltip(event);
            mAttachInfo.mHandlingPointerEvent = false;
            ...
            return handled ? FINISH_HANDLED : FORWARD;
        }
  }


processPointerEvent 在 ViewPostImeInputStage 中别找错了。可以看到在 onProcess 中最终调用了一个 mView的dispatchPointerEvent 方法,mView 实际上就是 PhoneWindow 中的 DecorView,而 dispatchPointerEvent 是被 View.java 实现的,如下所示:


View.dispatchPointerEvent


public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }


DecorView.dispatchTouchEvent


@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);
    }


 最好调用了 Window.Callback 中 cb.dispatchTouchEvent(ev) 方法,那这个 Callback 是不是 Activity 呢?


Activity.attach


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, IBinder assistToken) {
        ...
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(mWindowControllerCallback);
        //this:Activity
        mWindow.setCallback(this);
        ...
    }


    Activity 将自身传递给了 PhoneWindow,再接着看 Activity的dispatchTouchEvent。


Activity.dispatchTouchEvent


public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            //该方法是用户交互,每当向Activity分派按键、触摸或轨迹球事件时调用。
            //在这里仅用于ACTION_DOWN的判断
            onUserInteraction();
        }
        //返回true
        if (getWindow().superDispatchTouchEvent(ev)) {
            //Activity.dispatchTouchEvent()就返回true,则方法结束。
            //该点击事件停止往下传递&事件传递过程结束
            return true;
        }
        return onTouchEvent(ev);
    }


PhoneWindow.superDispatchTouchEvent


1.@Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }


DecorView.superDispatchTouchEvent


public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }


Touch 事件在 Activity 中只是绕了一圈最后还是回到了 PhoneWindow 中的 DecorView 来处理。 剩下的就是从 DecorView 开始将事件层层传递给内部的子 View 中了


ViewGroup. dispatchTouchEvent


   @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    }


  到这里就不做多的重复了。感兴趣的可以看看 事件分发机制


小结


       通过 setContentView 的流程,分析了 Activity、Window、View 之间的关系。整个过程 Activity 表面上参与度比较低,Activity持有Window的对象,View在Window上的增删等操作又是通过WindowManager来管理的,而WindowManager又是通过Binder机制获取到的WMS的映射,WMS把View真正显示到屏幕上。


三者关系:


  1. 一个 Activity 中有一个 window,也就是 PhoneWindow 对象,在Activity中调用attach,创建了一个PhoneWindow。


  1. 在 PhoneWindow 中有一个 DecorView,Activity在 调用setContentView 中会将 layout 填充到此 DecorView 中。


  1. 一个应用进程中只有一个 WindowManagerGlobal 对象,因为在 ViewRootImpl 中它是 static 静态类型。


  1. 每一个 PhoneWindow 对应一个 ViewRootImple 对象。


  1. WindowMangerGlobal 通过调用 ViewRootImpl 的 setView 方法,完成 window 的添加过程。


  1. 调用ViewGroup的removeAllView(),先将所有的view移除掉


  1. ViewRootImpl 的 setView 方法中主要完成两件事情:View 渲染(requestLayout)以及接收触屏事件。  


往期回顾


OkHttp使用和源码详解


RecyclerView 绘制流程及Recycler缓存


Glide 缓存机制及源码(二)


Glide 的简单使用(一)

相关文章
|
2月前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
96 0
|
1月前
|
XML 前端开发 Android开发
Android:UI:Drawable:View/ImageView与Drawable
通过本文的介绍,我们详细探讨了Android中Drawable、View和ImageView的使用方法及其相互关系。Drawable作为图像和图形的抽象表示,提供了丰富的子类和自定义能力,使得开发者能够灵活地实现各种UI效果。View和ImageView则通过使用Drawable实现了各种图像和图形的显示需求。希望本文能为您在Android开发中使用Drawable提供有价值的参考和指导。
40 2
|
1月前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
39 5
|
2月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
94 6
|
2月前
|
缓存 数据处理 Android开发
在 Android 中使用 RxJava 更新 View
【10月更文挑战第20天】使用 RxJava 来更新 View 可以提供更优雅、更高效的解决方案。通过合理地运用操作符和订阅机制,我们能够轻松地处理异步数据并在主线程中进行 View 的更新。在实际应用中,需要根据具体情况进行灵活运用,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在 Android 中使用 RxJava 更新 View 的技巧和方法,为开发高质量的 Android 应用提供有力支持。
|
2月前
|
缓存 调度 Android开发
Android 在子线程更新 View
【10月更文挑战第21天】在 Android 开发中,虽然不能直接在子线程更新 View,但通过使用 Handler、AsyncTask 或 RxJava 等方法,可以实现子线程操作并在主线程更新 View 的目的。在实际应用中,需要根据具体情况选择合适的方法,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在子线程更新 View 的技巧和方法,为开发高质量的 Android 应用提供支持。
41 2
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
2月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
31 3
|
2月前
|
缓存 前端开发 Android开发
Android实战之如何截取Activity或者Fragment的内容?
本文首发于公众号“AntDream”,介绍了如何在Android中截取Activity或Fragment的屏幕内容并保存为图片。包括截取整个Activity、特定控件或区域的方法,以及处理包含RecyclerView的复杂情况。
27 3
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
29 2