首页 文章 精选 留言 我的

精选列表

搜索[安全机制],共10000篇文章
优秀的个人博客,低调大师

android的窗口机制分析------UI管理系统

Activity可以看做是整个Android系统的人机接口,它提供了一个窗口来绘制UI,每个Activity在启动时,我们都需要给它设置一个Content view,作为Activity所呈现的UI内容,这个过程是通过setContentView()方法来实现的。 众所周知,android系统中强化了view的概念,主要是体现在对view的管理上,Android中的view以2种形态存在,单一的View和多个View组成的ViewGroup。Content view是以ViewGroup的形式存在的,也就是说在一个Activity窗口中可以添加多个View,这样就实现了Android窗口系统的UI多样化。activity启动时给activity窗口设置的Content view 是从xml文件中解析出来的,那么android是怎么样对这个ContentView进行管理的呢,它的内部实现逻辑又是怎样的呢? 在进行分析之前,首先给出一个Activity的window和view系统的层级关系,这个层级关系就是在Activity设置完ContentView之后的状况。 如下图。 下面来一一介绍各个层级的含义与作用 1.1PhoneWindow PhoneWindow是Android中的最基本的窗口系统,每个Activity 均会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。 1.2DecorView DecorView是当前Activity所有View的祖先,它并不会向用户呈现任何东西,它主要有如下几个功能,可能不全: A. Dispatch ViewRoot分发来的key、touch、trackball等外部事件; B. DecorView有一个直接的子View,我们称之为System Layout,这个View是从系统的Layout.xml中解析出的,它包含当前UI的风格,如是否带title、是否带process bar等。可以称这些属性为Window decorations。 C. 作为PhoneWindow与ViewRoot之间的桥梁,ViewRoot通过DecorView设置窗口属性。 1.3System Layout 目前android根据用户需求预设了几种UI 风格,通过PhoneWindow通过解析预置的layout.xml来获得包含有不同Window decorations的layout,我们称之为System Layout,我们将这个System Layout添加到DecorView中,目前android提供了8种System Layout,如下图。 预设风格可以通过PhoneWindow方法requestFeature()来设置,需要注意的是这个方法需要在setContentView()方法调用之前调用。 1.4Content Parent Content Parent这个ViewGroup对象才是真真正正的ContentView的parent,我们的ContentView终于找到了寄主,它其实对应的是System Layout中的id为”content”的一个FrameLayout。这个FrameLayout对象包括的才是我们的Activity的layout(每个System Layout都会有这么一个id为”content”的一个FrameLayout)。 1.5Activity Layout 这个ActivityLayout便是我们需要向窗口设置的ContentView,现在我们发现其实它的地位很低,同时这一部分才是和user交互的UI部分,其上的几层并不能响应并完成user输入所期望达到的目的。 本文转自 一点点征服 博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6671951.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android事件分发机制源码和实例解析

1.事件分发过程的理解 1.1. 概述 1.2. 主要方法 1.3. 核心行为 1.4. 特殊情况 2.案例分析 2.1. 案例1:均不消费 down 事件 2.2. 案例2:View0 消费 down 事件 2.3. 案例3:ViewGroup2nd 消费 down 事件 3.down 事件分发图 1. 事件分发过程的理解 1.1. 概述 事件主要有 down(MotionEvent.ACTION_DOWN),move(MotionEvent.ACTION_MOVE),up(MotionEvent.ACTION_UP)。 基本上的手势均由 down 事件为起点,up 事件为终点,中间可能会有一定数量的move 事件。这三种事件是大部分手势动作的基础。 事件和相关信息(比如坐标)封装成 MotionEvent。 大体的分发过程为:首先传递到 Activity,然后传给了 Activity 依附的 Window,接着由 Window 传给视图的顶层 View 也就是 DecorView,最后由 DecorView 向整个 ViewTree 分发。分发还会有回溯的过程。最后还会回到 Activity 的调用中。 Activity 的分发事件源码 publicbooleandispatchTouchEvent(MotionEventev){if(ev.getAction()==MotionEvent.ACTION_DOWN){ onUserInteraction(); }if(getWindow().superDispatchTouchEvent(ev)){returntrue; }returnonTouchEvent(ev); } 在 getWindow().superDispathTouchEvent 就是用来分发事件到 DecorView 中。如果整个 ViewTree 没有消费事件,会调用 Activity 的 onTouchEvent。 1.2. 主要方法 1.2.1. 概览 主要涉及到的 View 或 ViewGroup 的方法有: dispatchTouchEvent,该方法封装了事件分发的整个过程。是事件分发的 调度者 和 指挥官 。的核心过程均在该方法中。下面的 onInterceptTouchEvent 和 onTouchEvent 的回调的调用就在该方法体中。是否传递事件到 onInterceptTouchEvent 和 onTouchEvent 由 dispatchTouchEvent 决定。 onInterceptTouchEvent,该方法决定了是否拦截事件。只有 ViewGroup 有该回调。返回 true 表示拦截,返回 false 表示不拦截。自定义 View 的时候,可以重载该方法,通过一些特定的逻辑来决定是否拦截事件。如果拦截,接下来会调用该 ViewGroup 的 onTouchEvent 来处理事件。 onTouchEvent,该方法处理了事件,并决定是否继续消费后续事件。该方法调用的前置条件: 该 View 拦截了事件 子 View 都不消费事件 没有子 View 该方法正式处理 MotionEvent。返回 true 表示消费,返回 false 不消费。如果消费,接下来的事件还会传递到该 View 的 dispatchTouchEvent 中;如果不消费,后面的事件不会再传过来。 onTouchListener 的 onTouch 回调,和 onTouchEvent 一样,优先级比 onTouchEvent 高,如果有设置该监听,并且 onTouch 返回 true,就不会再调用 onTouchEvent 了。如果返回 false,事件还是会传递到 onTouchEvent 中。 1.2.2. dispatchTouchEvent 方法中的一些细节处理: 大部分手势的起点为 down 事件,dispatchTouchEvent 如果收到 down 事件,会重新设置一些变量和标记 重置变量和标记的源码 //Handleaninitialdown.if(actionMasked==MotionEvent.ACTION_DOWN){//Throwawayallpreviousstatewhenstartinganewtouchgesture. //Theframeworkmayhavedroppedtheuporcanceleventforthepreviousgesture //duetoanappswitch,ANR,orsomeotherstatechange. cancelAndClearTouchTargets(ev); resetTouchState(); } 实际的源码中,ViewGroup 继承于 View。 当子 View 不消费事件或者 ViewGroup 拦截了事件会传空值到 dispatchTransformedTouchEvent 中,内部会调用 super.dispatchTouchEvent,最终把事件传给 onTouchEvent 进行处理。 dispatchTransformedTouchEvent 关键部分 //Performanynecessarytransformationsanddispatch.if(child==null){ handled=super.dispatchTouchEvent(transformedEvent); }else{finalfloatoffsetX=mScrollX-child.mLeft;finalfloatoffsetY=mScrollY-child.mTop; transformedEvent.offsetLocation(offsetX,offsetY);if(!child.hasIdentityMatrix()){ transformedEvent.transform(child.getInverseMatrix()); } handled=child.dispatchTouchEvent(transformedEvent); } 也就是 dispatchTransformTouchEvent 完成了分发的最后过程: a. 传入的 child 不为空,转化坐标为 child 的坐标系,调用 child.dispatchTouchEvent向 child 分发事件 b. 传入的 child 为空,调用 super.dispatchTouchEvent 分发事件到 onTouchEvent 中 1.2.3 方法的主要关系 对于一个 ViewGroup 来说,几个重要方法的关系如下 几个重要方法关系伪代码 publicbooleandispatchTouchEvent(MotionEvente){booleanconsumed=false;if(onInterceptTouchEvent(e)){ consumed=onTouchEvent(e); }else{for(Viewview:childs){ consumed=view.dispatchTouchEvent(e);if(consumed){break; } }if(!consumed){ consumed=onTouchEvent(e); } } returnconsumed; } 这是事件分发过程的简单描述,具体远比这复杂的多。 1.3. 核心行为 View 或 ViewGroup 有两个核心的行为:拦截(intercept) 和 消费(consume)。这两者是相互独立的,拦截不一定消费。是否要拦截看 onIntercepTouchEvent。是否要消费看 onTouchEvent。 注意:是否拦截还有其他因素影响。如果不是 down 事件,并且 mFirstTouchTarget 为空值,就会直接拦截事件。 在 dispatchTouchEvent 中有这样的代码 拦截的关键源码 //Checkforinterception.finalbooleanintercepted;if(actionMasked==MotionEvent.ACTION_DOWN ||mFirstTouchTarget!=null){finalbooleandisallowIntercept=(mGroupFlags&FLAG_DISALLOW_INTERCEPT)!=0;if(!disallowIntercept){ intercepted=onInterceptTouchEvent(ev); ev.setAction(action);//restoreactionincaseitwaschanged }else{ intercepted=false; } }else{//Therearenotouchtargetsandthisactionisnotaninitialdown //sothisviewgroupcontinuestointercepttouches. intercepted=true; } 从上面的源码可以看出,在不是 down 事件,并且 mFirstTouchTarget 为空的情况下,不会走 onInterceptTouchEvent 而是直接拦截。如果满足了,还会看 FLAG_DISALLOW_INTERCEPT 标记,如果不允许拦截(disallowIntercept 为 true),也不会走onInterceptTouchEvent,直接标记不拦截。 处理调用 onTouchEvent 的源码 booleanresult=false; ... ListenerInfoli=mListenerInfo;if(li!=null&&li.mOnTouchListener!=null &&(mViewFlags&ENABLED_MASK)==ENABLED &&li.mOnTouchListener.onTouch(this,event)){ result=true; }if(!result&&onTouchEvent(event)){ result=true; } 可以看出,在该 View 为 ENABLE 的状态并且有 mTouchListener,会先调用 onTouch。在onTouch 返回 false 时才会继续调用 onTouchEvent。 onTouch 或者 onTouchEvent 的处理结果有: 返回 true,会继续消费后续事件。意味着,后面的事件将会继续传递到该 View 的 dispatchTouchEvent 方法中进行调度。父 View 会为该 View 创建一个 TouchTarget 实例加入链表中,链表的第一项为 mFirstTouchTarget。后续的 move 和 up 事件会直接交给该 View 的 dispatchTouchEvent。 返回 false,不再消费后续事件。意味着,后面的事件将会被父 View 拦截,而不再传递下来。 1.4. 特殊情况 比较特殊的情况有,子 View 可以使用 requestDisallowInterceptTouchEvent 影响去父 View 的分发,可以决定父 View 是否要调用 onInterceptTouchEvent 。比如,requestDisallowInterceptTouchEvent(true),父 View 就不用调用 onInterceptTouchEvent 来判断拦截,而就是不拦截。 该方法可以用来解决手势冲突。比如子 View 先消费了事件,但是后面父 View 也满足了手势触发的条件而拦截事件,导致子 View 手势执行一半后无法继续响应。可以使用 requestDisallowInterceptTouchEvent(true),这样后面的事件,父 View 不会走 onInterceptTouchEvent 回调来判断是否要拦截事件,而是直接把事件继续传下来。 2. 案例分析 下面举三个简单的例子,三个类 ViewGroup1st,ViewGroup2nd 和 View0,层级关系为 <ViewGroup1st> <ViewGroup2nd> <View0/> </ViewGroup2nd></ViewGroup1st> 这三个类有两层 ViewGroup,最底层为 View,这几个例子主要理解 消费 行为,所以不做事件的拦截。 2.1. 案例1:均不消费 down 事件 在触摸屏幕中 View0 的区域后,输出 log 信息如下 12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup1st:dispatchTouchEventbefore12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup1st:onInterceptTouchEventreturn:false12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup2nd:dispatchTouchEventbefore12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup2nd:onInterceptTouchEventreturn:false12-3014:06:03.69431323-31323/lyn.demoD/View0:dispatchTouchEventbefore12-3014:06:03.69431323-31323/lyn.demoD/View0:onTouchEventreturn:false12-3014:06:03.69431323-31323/lyn.demoD/View0:dispatchTouchEventreturn:false12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup2nd:onTouchEventreturn:false12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup2nd:dispatchTouchEventreturn:false12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup1st:onTouchEventreturn:false12-3014:06:03.69431323-31323/lyn.demoD/ViewGroup1st:dispatchTouchEventreturn:false 当 down 事件从 DecorView 开始了分发过程: ViewGroup1st 收到事件,执行 onInterceptTouchEvent 返回 false,不拦截,于是调用 ViewGroup2nd 的 dispatchTouchEvent 向 ViewGroup2nd分发。 ViewGroup2nd 收到事件,dispatchTouchEvent 重复 ViewGroup1st 的分发策略。因为都不拦截,所以调用了 View0 的 dispatchTouchEvent。 View0 收到事件,而 View0 不是 ViewGroup 类型,所以把事件直接交给了 onTouchEvent。 View0 不消费事件,onTouchEvent 返回 false,dispatchTouchEvent 方法因此也返回 false。 ViewGroup2nd 因为 View0 的 dispatchTouchEvent 返回 false,确定了子类不消费事件,于是把事件传递给 onTouchEvent。但本身也不消费事件,所以 onTouchEvent 也返回 false,继续把事件上抛到 ViewGroup1st。 ViewGroup1st 重复了 ViewGroup2nd 的过程。 随后,move 事件不会再往下传了,而是直接被 Activity 拦截。 2.2. 案例2:View0 消费 down 事件 首先是 down 事件的传递,log 如下 12-3014:14:09.3847350-7350/lyn.demoD/ViewGroup1st:dispatchTouchEventbefore12-3014:14:09.3847350-7350/lyn.demoD/ViewGroup1st:onInterceptTouchEventreturn:false12-3014:14:09.3847350-7350/lyn.demoD/ViewGroup2nd:dispatchTouchEventbefore12-3014:14:09.3847350-7350/lyn.demoD/ViewGroup2nd:onInterceptTouchEventreturn:false12-3014:14:09.3847350-7350/lyn.demoD/View0:dispatchTouchEventbefore12-3014:14:09.3847350-7350/lyn.demoD/View0:onTouchEventreturn:true12-3014:14:09.3847350-7350/lyn.demoD/View0:dispatchTouchEventreturn:true12-3014:14:09.3847350-7350/lyn.demoD/ViewGroup2nd:dispatchTouchEventreturn:true12-3014:14:09.3847350-7350/lyn.demoD/ViewGroup1st:dispatchTouchEventreturn:true ViewGroup1st 和 ViewGroup2st 的传递和案例1一样。区别在于 View0 onTouchEvent 返回 true 消费后续事件后,View0 的 dispatchTouchEvent 也返回 true,ViewGroup2nd 和 ViewGroup1st 不执行 onTouchEvent 也直接返回 true 然后稍微移动一下手指,move 事件往下传递 12-3014:14:09.4847350-7350/lyn.demoD/ViewGroup1st:dispatchTouchEventbefore12-3014:14:09.4847350-7350/lyn.demoD/ViewGroup1st:onInterceptTouchEventreturn:false12-3014:14:09.4847350-7350/lyn.demoD/ViewGroup2nd:dispatchTouchEventbefore12-3014:14:09.4847350-7350/lyn.demoD/ViewGroup2nd:onInterceptTouchEventreturn:false12-3014:14:09.4847350-7350/lyn.demoD/View0:dispatchTouchEventbefore12-3014:14:09.4847350-7350/lyn.demoD/View0:onTouchEventreturn:true12-3014:14:09.4847350-7350/lyn.demoD/View0:dispatchTouchEventreturn:true12-3014:14:09.4847350-7350/lyn.demoD/ViewGroup2nd:dispatchTouchEventreturn:true12-3014:14:09.4847350-7350/lyn.demoD/ViewGroup1st:dispatchTouchEventreturn:true 过程和 down 事件的传递一样。因为同样会经过 ViewGroup2nd 的 onInterceptTouchEvent,如果这时候 ViewGroup2nd 有拦截行为,move 事件就不会传到 View0 了。要避免这种情况发生,需要调用 View0 的requestDisallowInterceptTouchEvent,可见 1.4 部分。 2.3. 案例3:ViewGroup2nd 消费 down 事件 首先是 down 事件的传递,log 如下 12-3014:25:30.07418848-18848/lyn.demoD/ViewGroup1st:dispatchTouchEventbefore12-3014:25:30.07418848-18848/lyn.demoD/ViewGroup1st:onInterceptTouchEventreturn:false12-3014:25:30.08418848-18848/lyn.demoD/ViewGroup2nd:dispatchTouchEventbefore12-3014:25:30.08418848-18848/lyn.demoD/ViewGroup2nd:onInterceptTouchEventreturn:false12-3014:25:30.08418848-18848/lyn.demoD/View0:dispatchTouchEventbefore12-3014:25:30.08418848-18848/lyn.demoD/View0:onTouchEventreturn:false12-3014:25:30.08418848-18848/lyn.demoD/View0:dispatchTouchEventreturn:false12-3014:25:30.08418848-18848/lyn.demoD/ViewGroup2nd:onTouchEventreturn:true12-3014:25:30.08418848-18848/lyn.demoD/ViewGroup2nd:dispatchTouchEventreturn:true12-3014:25:30.08418848-18848/lyn.demoD/ViewGroup1st:dispatchTouchEventreturn:true 由于 View0 不消费事件,dispatchTouchEvent 返回 false,所以执行了 ViewGroup2nd 的 onTouchEvent 方法。 ViewGroup2nd 消费事件,onTouchEvent 返回 true,之后 ViewGroup2nd 和 ViewGroup1st 的 dispatchTouchEvent 均返回 true。 动一下手指,move 事件接着传 2-3014:25:30.17418848-18848/lyn.demoD/ViewGroup1st:dispatchTouchEventbefore12-3014:25:30.17418848-18848/lyn.demoD/ViewGroup1st:onInterceptTouchEventreturn:false12-3014:25:30.17418848-18848/lyn.demoD/ViewGroup2nd:dispatchTouchEventbefore12-3014:25:30.17418848-18848/lyn.demoD/ViewGroup2nd:onTouchEventreturn:true12-3014:25:30.17418848-18848/lyn.demoD/ViewGroup2nd:dispatchTouchEventreturn:true12-3014:25:30.17418848-18848/lyn.demoD/ViewGroup1st:dispatchTouchEventreturn:true 这时候,ViewGroup2nd 直接拦截了 move 事件,不再经过 onInterceptTouchEvent,也不再向 View0 分发,而是直接调用 onTouchEvent 进行处理。 3. down 事件分发图 在每个 View 都不拦截 down 事件的情况下,down 事件是这样传递的 本文作者:佚名 来源:51CTO

优秀的个人博客,低调大师

原来Android触控机制竟是这样的?

image 有什么料? 从这篇文章中你能获得这些料: 了解一次触摸事件究竟是如何产生的? 了解触摸事件究竟是如何传递的? 学会从根源处分析你的App中的滑动冲突。 能够更自信的创作出具有复杂交互的App。 收获一张图,帮助你理解和使用Android的触摸事件分发。 link 老规矩,先来看图吧。 image 在你触摸屏幕之后 首先在上图中找到那只黑手,它是一次触摸事件的开始。 当屏幕被触摸,Linux内核会将硬件产生的触摸事件包装为Event存到/dev/input/event[x]目录下。对,你没看错,事件被搞成文件保存了下来! 接着,系统创建的一个InputReaderThread线程loop起来让EventHub调用getEvent()不断的从/dev/input/文件夹下读取输入事件。 然后InputReader则从EventHub中获得事件交给InputDispatch。 而InputDispatch又会把事件分发到需要的地方,比如ViewRootImpl的WindowInputEventReceiver中。 以上过程是在底层中完成,大部分由c++实现,我们了解流程就行。 image 下面从ViewRootImpl收到触摸事件开始分析触摸事件的去向。 触摸事件的旅行 第一站:从InputEventReceiver开始 我们现在图中找到WindowInputReceiver。可以看到,它继承了InputEventReceiver,并且是ViewRootImpl的一个内部类。 ViewRootImpl.java final class WindowInputEventReceiver extends InputEventReceiver{ @Override public void onInputEvent(InputEvent event) { ... enqueueInputEvent(event, this, 0, true); ... } } 当一个输入事件产生时(这里我们认为是触摸事件),会回调InputEventReceiver.onInputEvent()。从名字也可以看出,它是接收输入事件的。然后进一步调用 ViewRootImpl.enqueueInputEvent() 将输入事件加入单链表队列。 ViewRootImpl.java void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) { ... mPendingInputEventTail = q; //进行队列操作后, //将ViewRootImpl的mPendingInputEventTail复制为新的触摸事件。 ... if (processImmediately) { doProcessInputEvents(); //立即处理事件 } else { scheduleProcessInputEvents(); //走一遍Handler延迟处理事件 } } 上面这个方法主要对触摸事件进行队列操作(即排了个序),然后再根据processImmediately参数,决定是立即处理,还是延后处理。 下面看看是如何进行处理的。 void doProcessInputEvents() { while (mPendingInputEventHead != null) { ... deliverInputEvent(q); //进一步派发事件处理 ... } } 在这个方法中有一个while{}循环体。它会将事件队列循环处理,直到队列中没有数据为止。如何处理呢?我们看看deliverInputEvent()。 private void deliverInputEvent(QueuedInputEvent q) { ... InputStage stage; if (q.shouldSendToSynthesizer()) { stage = mSyntheticInputStage; } else { stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage; } //上面决定将事件派发到那个InputStage中处理 if (stage != null) { stage.deliver(q); //派发事件到InputStage中处理 } else { finishInputEvent(q); } } 这里说一下有意思的InputStage。 image 在ViewRootImpl中,有这样一段代码: public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { ... // 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; ... } 如你所见,InputStage 是在setView()的时候创建的,也就是在Activity的onResume()阶段,关于这点,你可以看看我这篇文章:【用两张图告诉你,为什么你的App会卡顿?http://www.jianshu.com/p/df4d5ec779c8】。这些InputStage也相当于是单链表结构,一个套一个。也就是说,比如调用了mFirstPostImeInputStage.deliver(q)。那么SyntheticInputStage, ViewPostImeInputStage, NativePostImeInputStage, EarlyPostImeInputStage都将能够处理这个触摸事件。这里我们主要看看ViewPostImeInputStage是如何处理的。 final class ViewPostImeInputStage extends InputStage { public final void deliver(QueuedInputEvent q) { ... apply(q, onProcess(q)); //onProcess()中处理处理事件 ... } @Override protected int onProcess(QueuedInputEvent q) { ... return processPointerEvent(q); //处理点触摸事件 ... } private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; ... final View eventTarget = (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ? mCapturingView : mView; //DecorView ... boolean handled = eventTarget.dispatchPointerEvent(event); //eventTarget一般取到mView,即DecorView ... return handled ? FINISH_HANDLED : FORWARD; } } 啊哈,这段代码稍有点长。但是过程很简单。ViewPostImeInputStage.deliver(q)调用后,会进一步调用onProcess()处理。对于点触摸事件会再进一步调用processPointerEvent()处理,并且在这个方法中,通过eventTarget.dispatchPointerEvent(event)将触摸事件传递给了DecorView的dispatchPointerEvent()处理。 现在越来越清晰了。触摸事件几经辗转终于传递到了View上。赶紧接着看看触摸事件后面的旅途是怎样的? 奇怪的辗转 上面说到触摸事件传递到了DecorView.dispatchPointerEvent()中。在图中找到对应位置哦。看看在这个方法中发生了什么? 找了一会儿可能在DecorView中没有找到dispatchPointerEvent()方法?哈哈,那一定在更上级。事实上,这个方法是在View中实现的。View.java public final boolean dispatchPointerEvent(MotionEvent event) { ... return dispatchTouchEvent(event); //这个方法大家该熟悉了吧? ... } 由于调用的是DecorView.dispatchPointerEvent(),所以上面调用的dispatchTouchEvent(event)实际是DecorView中的。多态!知识点啊!忘了吧?面壁思过吧。DecorView.java @Override public boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); //还记得Activity实现了Window.Callback接口吗? return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) //通常是走这。 : super.dispatchTouchEvent(ev); } 在DecorView.dispatchTouchEvent()中,又把事件传递到了Activity中去了。WHAT?(需要说明一点,Activity实现了Window.Callback,并且在PhoneWindow创建后立即调用Window.setCallback(this),把Activity设置为PhoneWindow的Window.Callback。)好吧,再到Activity中看看发生了什么?Activity.java public boolean dispatchTouchEvent(MotionEvent ev) { ... if (getWindow().superDispatchTouchEvent(ev)) { //又把事件传到了Window中! return true; } return onTouchEvent(ev); //这就是为什么最后事件没有被消费的话,Activity会去处理的原因。 } 如你所见,事件又被传递到了PhoneWindow中去了。我的天,不是都到View里了吗?为什么还要一直传递?好吧,继续看应该就知道了。 image PhoneWindow.java @Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); } WTF?mDecor分明就是DecorView嘛。关于这点,你可以看看我这篇文章:【用两张图告诉你,为什么你的App会卡顿?http://www.jianshu.com/p/df4d5ec779c8】。为什么来回捣腾一圈又回到DecorView中了呢?我们继续看吧。DecorView.java public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); //千万不要告诉我你不知道这实际会调用ViewGroup的dispatchTouchEvent()方法啊。 } 好吧,来回绕啊绕,终于到了各大论坛,各大博客,各大书籍所讲的触摸事件分发流程了,即从ViewGroup的dispatchTouchEvent()开始。相信很多同学早已滚瓜烂熟了,但是下面我还是再撸一遍,以保持整个流程的完整性! 辗转的原因 在开始之前,我们思考一下。既然事件最后要看起来是从Activity的dispatchTouchEvent()开始往下分发传递,为什么在InputStage.processPointerEvent()中不直接把事件传递给Activity,而是这样来回绕一圈。这样DecorView -> Activity -> PhoneWindow -> DecorView的来回绕一圈不是很折腾吗?看吧,这就是为什么我一直在说你需要看看这篇文章:【用两张图告诉你,为什么你的App会卡顿?http://www.jianshu.com/p/df4d5ec779c8】的原因。 首先,为了解耦,ViewRootImpl并不知道有Activity这种东西存在!不知道!它只是持有了DecorView。所以,想要直接把触摸事件送到Activity.dispatchTouchEvent() 是不行的。 那么,既然触摸事件已经到了Activity.dispatchTouchEvent()中了,为什么不直接分发给DecorView ,而是要通过PhoneWindow 来间接发送呢?因为Activity 不知道有DecorView 这种奇怪的东西存在啊!不知道!但是,Activity持有PhoneWindow ,而PhoneWindow当然知道自己的窗口里有些什么了,所以能够把事件派发给DecorView 。你看,在Android中,Activity并不知道自己的Window中有些什么,这样耦合性就很低了。我们换一个Window试试?不管Window里面的内容如何,只要Window任然符合Activity制定的标准,那么它就能在Activity中很好的工作。这就是解耦所带来的扩展性的好处。 这是我的想法,童鞋们可以对照着图思考思考。 最后的事件分发 看图中辣眼睛的蓝色部分。这很熟悉了吧?一般讲触摸事件分发就是从这里开始的。 我从DecorView开始。假设ViewGroup_1是DecorView的子View,ViewGroup_2是ViewGroup_1的子View,View是ViewGroup_2的子View。现在,我要开始描述这个过程了啊,这个地方一定要对照着图撸啊。发车了啊,头手不要伸出窗外。 DecorView是ViewGroup,所以dispatchTouchEvent()【分发器】收到触摸事件后,会询问onInterceptTouchEvent()【拦截器】(注意,拦截器是ViewGroup类型的View特有的!)是否需要拦截此次触摸事件?如果拦截返回true,事件将会转到自己的onTouchEvent()【处理器】中处理。如果不拦截,事件将传到它的子View,ViewGroup_1的【拦截器】。 ViewGroup_1的【分发器】被调用后,会询问自己的【拦截器】是否需要拦截此次触摸事件?如果拦截放回true,事件将会转到自己的【处理器】中处理。如果不拦截,事件将传到它的子View,ViewGroup_2的【拦截器】。 ViewGroup_2重复上述步骤。 如果触摸事件到了最后的View中,由于没有【拦截器】,事件将会直接由【分发器】分发到【处理器】中。如果处理了返回true,此次触摸事件的传递到此结束,不会在往下传了。如果不处理,返回false,那么事件将传递到它的父级的【处理器】中。 事实上,不管个层级的【处理器】,如果处理事件返回true,一次触摸事件就结束了。如果不处理事件,返回false,就会传送到上一级的【处理器】中。最终,如果DecorView的【处理器】也不打算处理事件,那么事件将会被发送到Activity的【处理器】中处理。 总结 抽出空余时间写文章分享需要动力,还请各位看官动动小手点个赞,给我点鼓励吧 我一直在不定期的创作新的干货,想要上车只需进到我的【个人主页】点个关注就好了哦。发车喽~ 本篇从一次触摸事件的产生,讲了它的整个传递过程。对着图撸一撸,然后去教训你的App中的各种滑动冲突 、点击穿透吧。 这是一个彩蛋 image 很多筒靴可能走了很多路,但任然不知道onTouchEvent()和onTouch()有何区别?下面将简单的讲一讲。 首先需要知道onTouch()和onTouchEvent()都可以接收到触摸事件,但是onTouch()的优先级比onTouchEvent()更高。也就是说,如果onTouch()把事件处理了,那么onTouchEvent()就接收不到事件了。 onTouch()是专门被设计用来在View外部对触摸事件进行处理的,你可以通过View.setOnTouchListener()来设置一个OnTouchListener。 mView.setOnTouchListener((v, event) -> { LogUtils.e("onTouch"); return true; //返回true才能处理事件。 }); 你看,你其实不必为了处理触摸事件专门自定义View,然后重载onTouchEvent()的。你可以在View外部使用View.setOnTouchListener()来设置一个OnTouchListener 处理。 需要注意一点,不管onTouch()或是onTouchEvent(),即使是返回false,你任然可以收到一次ACTION_DWON事件,但是后面的事件就不能继续收到了。 参考链接 用两张图告诉你,为什么你的App会卡顿?http://www.jianshu.com/p/df4d5ec779c8 InputManagerService分析一:IMS的启动与事件传递http://blog.csdn.net/lilian0118/article/details/28617185 Android跨进程模拟触屏事件(sendevent)http://azard.me/blog/2015/06/13/android-cross-app-touch-event-injection/ Android输入事件流程中的EventHub分析及源码演示http://blog.csdn.net/a345017062/article/details/6417929 Android 4.0 事件输入(Event Input)系统http://blog.csdn.net/myarrow/article/details/7091061 Correctly implementing onInterceptTouchEvent and onTouchEvent methods for ViewGroup:http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html 看到这里的童鞋快奖励自己一口辣条吧!

优秀的个人博客,低调大师

Spark RDD简介与运行机制概述

RDD工作原理: 主要分为三部分:创建RDD对象,DAG调度器创建执行计划,Task调度器分配任务并调度Worker开始运行。 SparkContext(RDD相关操作)→通过(提交作业)→(遍历RDD拆分stage→生成作业)DAGScheduler→通过(提交任务集)→任务调度管理(TaskScheduler)→通过(按照资源获取任务)→任务调度管理(TaskSetManager) 举例:以下面一个按A-Z首字母分类,查找相同首字母下不同姓名总个数的例子来看一下RDD是如何运行起来的。 步骤1:创建RDD。上面的例子除去最后一个collect是个动作,不会创建RDD之外,前面四个转换都会创建出新的RDD。因此第一步就是创建好所有RDD(内部的五项信息)。 步骤2:创建执行计划。Spark会尽可能地管道化,并基于是否要重新组织数据来划分阶段(stage),例如本例中的groupBy()转换就会将整个执行计划划分成两阶段执行。最终会产生一个DAG(directed acyclic graph,有向无环图)作为逻辑执行计划。 步骤3:调度任务。将各阶段划分成不同的任务(task),每个任务都是数据和计算的合体。在进行下一阶段前,当前阶段的所有任务都要执行完成。因为下一阶段的第一个转换一定是重新组织数据的,所以必须等当前阶段所有结果数据都计算出来了才能继续。 假设本例中的hdfs://names下有四个文件块,那么HadoopRDD中partitions就会有四个分区对应这四个块数据,同时preferedLocations会指明这四个块的最佳位置。现在,就可以创建出四个任务,并调度到合适的集群结点上。 Task管理和序列化: Task的运行要解决的问题不外乎就是如何以正确的顺序,有效地管理和分派任务,如何将Task及运行所需相关数据有效地发送到远端,以及收集运行结果 Task的派发源起于DAGScheduler调用TaskScheduler.submitTasks将一个Stage相关的一组Task一起提交调度。 在TaskSchedulerImpl中,这一组Task被交给一个新的TaskSetManager实例进行管理,所有的TaskSetManager经由SchedulableBuilder根据特定的调度策略进行排序,TaskSchedulerImpl的resourceOffers函数中,当前被选择的TaskSetManager的ResourceOffer函数被调用并返回包含了序列化任务数据的TaskDescription,最后这些TaskDescription再由SchedulerBackend派发到ExecutorBackend去执行 系列化的过程中,上一节中所述App依赖文件相关属性URL等通过DataOutPutStream写出,而Task本身通过可配置的Serializer来序列化,当前可配制的Serializer包括如JavaSerializer,KryoSerializer等 Task的运行结果在Executor端被序列化并发送回SchedulerBackend,由于受到Akka Frame Size尺寸的限制,如果运行结果数据过大,结果会存储到BlockManager中,这时候发送到SchedulerBackend的是对应数据的BlockID,TaskScheduler最终会调用TaskResultGetter在线程池中以异步的方式读取结果,TaskSetManager再根据运行结果更新任务状态(比如失败重试等)并汇报给DAGScheduler等

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册