精华内容
下载资源
问答
  • 做程序员的,基本一看api就知道,用这个可以取消上一个toast的显示,然后显示下一个,这样就能解决...可是在测试的过程中,发现却没有想象中的那么简单,不信可以百度一下,很多很多人发现toast的cancel()方法不起作用
  • cancel指令用来取消已经存在的打印任务。 语法格式:cancle [参数] [打印任务号] 常用参数: -a 取消所有打印任务 -E 当连接到服务器时强制使用加密 -U 指定连接服务器时使用的用户名 -h 指定连接的...
  • 取消 免费和开源取消! 使用它可以取消任何内容,例如言论自由,甚至是自由软件运动的创造者! 您可以在gopher://bitreich.org/1/cancel上查看当前的暴民排名
  • 本文主要介绍了Android中ProgressDialog的dismiss()与cancel()方法的区别,具有很好的参考价值。下面跟着小编一起来看下吧
  • 详解AsyncTask的cancel的有效用法,强制停止AsyncTask异步任务.
  • Ajax-axios-cancel.zip

    2019-09-17 10:44:28
    Ajax-axios-cancel.zip,在使用Axios库时简化对HTTP请求的取消,ajax代表异步javascript和xml。它是多种web技术的集合,包括html、css、json、xml和javascript。它用于创建动态网页,其中网页的小部分在不重新加载网页...
  • SIP 请求方法(4)-CANCEL & OPTIONS

    千次阅读 2020-12-22 10:07:48
    CANCEL CANCEL方法用于终止还未完成的INVITE。UA或代理都可以生成CANCEL消息,前提是收到1xx应答但没收到最终应答。UA使用这个方法来取消先前发起的,但还未建立的呼叫。代理如果有分支并行处理,可以用这个方法取消...

    CANCEL

            CANCEL方法用于终止还未完成的INVITE。UA或代理都可以生成CANCEL消息,前提是收到1xx应答但没收到最终应答。UA使用这个方法来取消先前发起的,但还未建立的呼叫。代理如果有分支并行处理,可以用这个方法取消之前的所有分支。CANCEL是一种逐跳处理的请求,它的应答由信令路径中下一个有状态元素生成。这个方法的CSeq序号不会递增,这样后面的代理或UA就能匹配CANCEL和对应的INVITE。

    端到端与逐跳处理请求

            CANCEL的branch ID与它要取消的INVITE相匹配。CANCEL只针对INVITE,是因为INVITE可能持续几秒(或几分钟)才能完成。而其它SIP请求都是立刻完成的(UAS必须立刻生成最终应答),因此,没必要CANCEL之前的(非INVITE)请求。

            代理服务器收到CANCEL时,把它转发给之前初始INVITE请求相同的位置集。代理不需要等待下游的应答,而是直接向上游发出应答。UA确认取消时,对CANCEL请求回应一条200 OK消息,同时对初始INVITE请求回应487 Request Terminated。

            如果已经收到最终应答,那么UA需要发BYE请求来终止会话。CANCEL与INVITE的最终应答在网络中有可能形成竞争条件。在这个示例中,CANCEL请求与INVITE的200 OK应答都经过代理服务器转发。代理服务器依然对CANCEL请求回应200 OK,同时它也会向UAC转发INVITE的200 OK应答消息。CANCEL请求的200 OK应答只是说明代理已经收到并转发CANCEL请求,UAC依然必须准备接收INVITE请求的最终应答。这个示例中收到的不是487而是200。但这时会话已经被UAC取消了,所以UAC必须先发一条ACK,再发一条BYE,UAS对它回应200 OK。

    CANCEL与INVITE的竞争条件

            因为CANCEL是逐跳处理的请求,所以它不能携带消息体。下面是一个CANCEL请求消息实例:

    CANCEL sip:i.newton@cambridge.example.com SIP/2.0
    Via: SIP/2.0/UDP 10.downing.example.org:5060;branch=z9hG4bK3134134
    Max-Forwards:70
    To: Isaac Newton <sip:i.newton@cambridge.example.com>
    From: Rene Descartes <sip:visitor@10.downing.example.org>;tag=034323
    Call-ID: 23d8e0e4e2e505329299e288bbd4155a
    CSeq: 32156 CANCEL
    Content-Length: 0

     

    CANCEL的必要头域
    Via
    To
    From
    Call-ID
    CSeq
    Max-Forwards

     

    OPTIONS

            OPTIONS方法用于查询UA或服务器的能力及发现它当前的可用性。OPTIONS请求的应答消息中描述UA或服务端的能力集。代理服务器永远不会生成OPTIONS请求。UA或服务器对OPTIONS响应与INVITE一样(比如说,他不想理会,可以回应4xx或6xx)。成功类应答(2xx)可以包含Allow, Accept, Accept-Encoding, Accept-Language, 和 Supported头域,用于说明它的能力集。功能tag(比如audio, video , 和 isfocus)应该包含在Contact头域字段中。

            OPTIONS请求不应包含消息体,但是也可以通过消息体说明媒体能力。代理服务器通过检查Request-URI是不是指向自己判断它是不是查询自己。如果Request-URI包含代理服务器的地址或主机名,那么请求就是发给代理的。否则,OPTIONS请求是发给其它代理或UA的,那么直接转发请求。OPTIONS请求消息的实例如下:

    OPTIONS sip:user@carrier.example.com SIP/2.0
    Via: SIP/2.0/UDP cavendish.kings.cambridge.example.com;branch=z9hG4bK1834
    Max-Forwards:70
    To: <sip:wiliamhopkins@cambridge.example.com>
    From: J.C. Maxwell <sip:james.maxwell@kings.cambridge.example.com> ;tag=34
    Call-ID: 747469e729acd305
    CSeq: 29 OPTIONS
    Content-Length: 0

    对应的应答消息实例:

    SIP/2.0 200 OK
    Via: SIP/2.0/UDP cavendish.kings.cambridge.example.com;tag=512A6;branch=z9hG4bK0834 ;received=192.0.0.2
    To: <sip:wiliamhopkins@cam.ac.uk>;tag=432
    From: J.C. Maxwell <sip:james.maxwell@kings.cambridge.example.com> ;tag=34
    Call-ID: 747469e729acd305
    CSeq: 29 OPTIONS
    Contact: <sip:william@tutors.cambridge.example.com>;audio;video
    Allow: INVITE, OPTIONS, ACK, BYE, CANCEL, REFER
    Supported: replaces, join
    Accept-Language: en, de, fr
    Content-Type: application/sdp
    Content-Length: ...
    
    v=0
    o=jc 2590845378 2590945578 IN IP4 tutors.cambridge.example.com
    s=
    c=IN IP4 tutors.cam.ac.uk
    t=0 0
    m=audio 32852 RTP/AVP 96 0
    a=rtpmap:96 SPEEX/8000
    a=rtpmap:0 PCMU/8000
    m=video 82852 RTP/AVP 34
    a=rtpmap:34 H263/90000

     

    OPTIONS的必要头域
    Via
    To
    From
    Call-ID
    CSeq
    Max-Forwards

     

    展开全文
  • 现在我们来学习MOVE,UP事件的传递以及CANCEL事件的产生和传递。我们可能都知道(听说)MOVE,UP的传递会受到DOWN事件的传递影响。为什么?下面我们慢慢来分析。 注:我们先排除一误区 @Override public ...

    前言

    我们在View的DOWN事件分发机制源码分析中学习了DOWN事件在Activity,ViewGroup,View三者这间的传递。现在我们来学习MOVE,UP事件的传递以及CANCEL事件的产生和传递。我们可能都知道(听说)MOVE,UP的传递会受到DOWN事件的传递影响。为什么?下面我们慢慢来分析。

    注:我们先排除一误区

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_MOVE " );
                return true;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_UP " );
                return false;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return true;
        }
    

    这个例子是在dispatchTouchEvent方法中,DOWN事件时调用super.dispatchTouchEvent()方法将DOWN事件传递下去,而当MOVE和UP事件时,不调用super.dispatchTouchEvent()而是直接返回true或false,事件直接消费调。如果这样写,MOVE,UP事件的传递就不受DOMN事件的传递有影响了。

    我们学习的场景时这样的:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return true;
        }
    

    即DOWN事件返回的时什么,到MOVEMOVE和UP事件时返回的就是什么。

    View的MOVE,UP事件传递

    我们先来看一张图:图来自己图解 Android 事件分发机制
    在这里插入图片描述
    即我们在ViewGroup 2 的onTouchEvent 返回true消费这次事件,那么红色的箭头代表ACTION_DOWN 事件的流向,蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向。为什么出现这样的情况呢?(MOVE和UP事件没有走到View的dispatchTouchEvent和onTouchEvent方法中而是直接到ViewGroup2的onTouchEvent()方法中)
    解开疑问我们需要再来看ViewGroup的dispatchTouchEvent()方法的源码;

     public boolean dispatchTouchEvent(MotionEvent ev) {
            if (mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
            }
            if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
                ev.setTargetAccessibilityFocus(false);
            }
            boolean handled = false;
            if (onFilterTouchEventForSecurity(ev)) {
                final int action = ev.getAction();
                final int actionMasked = action & MotionEvent.ACTION_MASK;
                if (actionMasked == MotionEvent.ACTION_DOWN) {
                    cancelAndClearTouchTargets(ev);
                    resetTouchState();
                }
                final boolean intercepted;
                // 分析【1】intercepted 的值影响后面代码执行
                if (actionMasked == MotionEvent.ACTION_DOWN  || mFirstTouchTarget != null) {
                    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;
                    }
                } else {
                    intercepted = true;
                }
                if (intercepted || mFirstTouchTarget != null) {
                    ev.setTargetAccessibilityFocus(false);
                }
                final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;
                final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
                TouchTarget newTouchTarget = null;
                boolean alreadyDispatchedToNewTouchTarget = false;
                // 分析【2】
                if (!canceled && !intercepted) {
                    View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null;
                    if (actionMasked == MotionEvent.ACTION_DOWN
                            || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                            || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                        final int actionIndex = ev.getActionIndex(); // always 0 for down
                        final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                                : TouchTarget.ALL_POINTER_IDS;
                        removePointersFromTouchTargets(idBitsToAssign);
                        final int childrenCount = mChildrenCount;
                        if (newTouchTarget == null && childrenCount != 0) {
                            final float x = ev.getX(actionIndex);
                            final float y = ev.getY(actionIndex);
                            final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                            final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled();
                            final View[] children = mChildren;
                            for (int i = childrenCount - 1; i >= 0; i--) {
                                final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
                                final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex);
                                if (childWithAccessibilityFocus != null) {
                                    if (childWithAccessibilityFocus != child) {
                                        continue;
                                    }
                                    childWithAccessibilityFocus = null;
                                    i = childrenCount - 1;
                                }
                                if (!canViewReceivePointerEvents(child)
     || !isTransformedTouchPointInView(x, y, child, null)) {
                                    ev.setTargetAccessibilityFocus(false);
                                    continue;
                                }
                                newTouchTarget = getTouchTarget(child);
                                if (newTouchTarget != null) {
                                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                                    break;
                                }
                                resetCancelNextUpFlag(child);
                                // 分析【3】
                                if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                    mLastTouchDownTime = ev.getDownTime();
                                    if (preorderedList != null) {
                                        for (int j = 0; j < childrenCount; j++) {
                                            if (children[childIndex] == mChildren[j]) {
                                                mLastTouchDownIndex = j;
                                                break;
                                            }
                                        }
                                    } else {
                                        mLastTouchDownIndex = childIndex;
                                    }
                                    mLastTouchDownX = ev.getX();
                                    mLastTouchDownY = ev.getY();
                                    // 分析【4】给mFirstTouchTarget赋值
                                    newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                    alreadyDispatchedToNewTouchTarget = true;
                                    break;
                                }
                                ev.setTargetAccessibilityFocus(false);
                            }
                            if (preorderedList != null) preorderedList.clear();
                        }
                        if (newTouchTarget == null && mFirstTouchTarget != null) {
                            newTouchTarget = mFirstTouchTarget;
                            while (newTouchTarget.next != null) {
                                newTouchTarget = newTouchTarget.next;
                            }
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                        }
                    }
                }
                // 分析【5】
                if (mFirstTouchTarget == null) {
                    handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
                } else {
                    TouchTarget predecessor = null;
                    TouchTarget target = mFirstTouchTarget;
                    while (target != null) {
                        final TouchTarget next = target.next;
                         // 分析【6】
                        if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                            handled = true;
                        } else {
                         // 分析【7】
                            final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
                            if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
                                handled = true;
                            }
                            if (cancelChild) {
                                if (predecessor == null) {
                                    mFirstTouchTarget = next;
                                } else {
                                    predecessor.next = next;
                                }
                                target.recycle();
                                target = next;
                                continue;
                            }
                        }
                        predecessor = target;
                        target = next;
                    }
                }
                if (canceled
                        || actionMasked == MotionEvent.ACTION_UP
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    resetTouchState();
                } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                    final int actionIndex = ev.getActionIndex();
                    final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                    removePointersFromTouchTargets(idBitsToRemove);
                }
            }
            if (!handled && mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
            }
            return handled;
        }
    

    我们在前一篇中提到DOWN事件时是一定会调用onInterceptTouchEvent()方法的。但是MOVE和UP事件就不一定了,从dispatchTouchEvent方法的分析【1】出一可以看出,MOVE和UP事件时会不会调用onInterceptTouchEvent()由mFirstTouchTarget和disallowIntercept(mGroupFlags)共同决定。我们暂时不考虑disallowIntercept(mGroupFlags)的影响,只考初始值情况下,所以只受mFirstTouchTarget的影响。那mFirstTouchTarget在分析【4】处的addTouchTarget()方法中赋值。

     private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
            final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
            target.next = mFirstTouchTarget;
            mFirstTouchTarget = target;
            return target;
        }
    

    看上图中的红色路线,DOWN事件在View的onTouchEvent()方法中并没有消费掉,所以在ViewGroup2中的dispatchTouchEvent()方法中(其实是ViewGroup的)分析【3】处的if()条件没有满足,所以分析【4】的addTouchTarget()没有被执行,即mFirstTouchTarget没有被赋值。最终中DOWN事件就传递到了ViewGroup2的onTocuhEvent()方法中并消费。当MOVE和UP事件到来后(ViewGroup2的dispatchTouchEvent()),由于分析【1】处的mFirstTouchTarget = null,所以intercepted被赋值为true 。intercepted = true 影响了分析【2】的执行,if()条件不成立。即分析【3】处dispatchTransformedTouchEvent()方法没有被执行,即事件没有传递到子View的机会。由于mFirstTouchTarget = null ,所以分析【5】处的if条件满足,调用 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) 方法,参数 child = null , 所以MOVE和UP事件传递了View(系统的,不是上图中说的View)的dispatchTouchEvent()方法,dispatchTouchEvent()中调用过来onTouchEvent()方法,ViewGroup2重写了onTouchEvent()方法,所以MOVE和UP事件最终传递到了ViewGroup2重写了onTouchEvent()的方法中。

    上面分析一种情况,DOWN事件传递会影响MOVE和UP事件的传递。还有很多中情况,分析的原理都是一样的。大家可以自己举例分析。

    View的CANCEL事件传递

    我们在View的DOWN事件分发机制源码分析提到了CANCEL事件是分人为的,DOWN事件是手指按下,MOVE事件是手指滑动,UP事件是手指抬起产生的。那CANCEL是非人认为产生的,那是怎样产生的?细心的同学可能已经注意到,我们在学习View的DOWN事件分发机制源码分析中说到了,产生的具体位置在dispatchTransformedTouchEvent()方法中

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {
            final boolean handled;
            final int oldAction = event.getAction();
            // 分析【1】
            if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
                event.setAction(MotionEvent.ACTION_CANCEL);
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    handled = child.dispatchTouchEvent(event);
                }
                // 分析【2】
                event.setAction(oldAction);
                return handled;
            }
            final int oldPointerIdBits = event.getPointerIdBits();
            final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
            if (newPointerIdBits == 0) {
                return false;
            }
            final MotionEvent transformedEvent;
            if (newPointerIdBits == oldPointerIdBits) {
                if (child == null || child.hasIdentityMatrix()) {
                    if (child == null) {
                        handled = super.dispatchTouchEvent(event);
                    } else {
                        final float offsetX = mScrollX - child.mLeft;
                        final float offsetY = mScrollY - child.mTop;
                        event.offsetLocation(offsetX, offsetY);
                        handled = child.dispatchTouchEvent(event);
                        event.offsetLocation(-offsetX, -offsetY);
                    }
                    return handled;
                }
                transformedEvent = MotionEvent.obtain(event);
            } else {
                transformedEvent = event.split(newPointerIdBits);
            }
            if (child == null) {
                handled = super.dispatchTouchEvent(transformedEvent);
            } else {
                final float offsetX = mScrollX - child.mLeft;
                final float offsetY = mScrollY - child.mTop;
                transformedEvent.offsetLocation(offsetX, offsetY);
                if (! child.hasIdentityMatrix()) {
                    transformedEvent.transform(child.getInverseMatrix());
                }
                handled = child.dispatchTouchEvent(transformedEvent);
            }
            transformedEvent.recycle();
            return handled;
        }
    

    我们看dispatchTransformedTouchEvent方法分析【1】就知道,事件变为CANCEL事件有两个条件,参数 cancel 和 事件本身就是CANCEL事件。但最初最初,事件只有DOWN,MOVE和UP事件,所以产生CANCEL事件最初最初是由参数cancel来决定的。dispatchTransformedTouchEvent()方法在ViewGroup的dispatchTouchEvent方法中由分析【3】【5】【7】调用到。看分析【3】传递的cancel参数为false,所以不可能在分析【3】这里产生的,再看分析【5】处,传递的参数cancel是canceled变量的值,canceled 的赋值代码如下:

    final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL;
    

    当事件本身是CANCEL事件是,canceled为true,但经过刚才的分析,最初最初事件只有DOWN,MOVE和UP事件,所以最初最初canceled = false,所以也不可能在分析【5】处产生。那只分析【7】处了。

    到这里我们需要排除一个误区:DOWN事件是不可能变为CANCEL事件的

    分析【7】处传递的cancel参数是cancelChild的值,cancelChild的赋值代码如下:

    final boolean cancelChild = resetCancelNextUpFlag(target.child)  || intercepted;
    

    即intercepted = true ,事件被拦截了,那么cancelChild = true ,这样就产生了CANCEL事件了。即两个条件:

    • 1.能执行到分析【7】处,那么mFirstTouchTarget != null ,不然会执行分析【5】处。有子View消费了DOWN事件,分析【3】处的if条件成立,分析【4】处addTouchTarget()方法得到执行从而mFirstTouchTarget得到赋值,mFirstTouchTarget != null 。
    • 2.intercepted = true。在条件一中我们说DOWN事件有子View消费了,即mFirstTouchTarget != null , 那么m和UP事件到来后(ViewGroup的dispatchTocuhEvent())方法的分析【1】处if()条件成路,所以 intercepted = true 就必须调用onInterceptTouchEvent方法并且返回true。所以第二个就是,重写onInterceptTouchEvent()方法,并且在MOVE和UP事件返回true。

    我们来测试一下,看是不是这样的:
    测试类RootGroupView和SubView。

    <?xml version="1.0" encoding="utf-8"?>
    <com.xy.eventest.base.view.RootGroupView xmlns:android="http://schemas.android.com/apk/res/android"
        android:background="#ffffff"
        android:gravity="center"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.xy.eventest.base.view.SubView
            android:id="@+id/subview_one"
            android:background="#ff0000"
            android:layout_height="100dp"
            android:layout_width="100dp"/>
    </com.xy.eventest.base.view.RootGroupView>
    
    

    HomeActivity的代码如下:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return true;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_DOWN " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    

    RootViewGroup的代码如下:

      @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , " onIntercept actoin = ACTION_DOWN " );
                return super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , " onIntercept actoin = ACTION_MOVE " );
                return true;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , " onIntercept actoin = ACTION_UP " );
                return true;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , " onIntercept actoin = ACTION_CANCEL " );
                return super.onInterceptTouchEvent(event);
            }
            return super.onInterceptTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_DOWN " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    

    即RootViewGroup的onInterceptTouchEvent()方法中拦截了MOVE是UP事件

    SubView的代码如下:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_DOWN " );
                return true ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_MOVE " );
                return true;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "onTouchEvent actoin = ACTION_UP " );
                return true;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "onTouchEvent actoin = ACTION_CANCEL " );
                return true;
            }
            return super.onTouchEvent(event);
        }
    

    SubView在onTocuhEvent()方法中消费所以事件。

    我们打印出来的日志:

    10-08 16:03:05.214 30933-30933/com.xy.eventest D/MotionEvent_TEST: HomeActivity dispatchTouch actoin = ACTION_DOWN 
    10-08 16:03:05.215 30933-30933/com.xy.eventest D/MotionEvent_TEST: RootGroupView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:03:05.215 30933-30933/com.xy.eventest D/MotionEvent_TEST:  RootGroupView onIntercept actoin = ACTION_DOWN 
    10-08 16:03:05.216 30933-30933/com.xy.eventest D/MotionEvent_TEST: SubView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:03:05.217 30933-30933/com.xy.eventest D/MotionEvent_TEST: SubView onTouchEvent actoin = ACTION_DOWN 
    10-08 16:03:05.301 30933-30933/com.xy.eventest D/MotionEvent_TEST: HomeActivity dispatchTouch actoin = ACTION_UP 
    10-08 16:03:05.302 30933-30933/com.xy.eventest D/MotionEvent_TEST: RootGroupView dispatchTouch actoin = ACTION_UP 
    10-08 16:03:05.302 30933-30933/com.xy.eventest D/MotionEvent_TEST:  RootGroupView onIntercept actoin = ACTION_UP 
    10-08 16:03:05.302 30933-30933/com.xy.eventest D/MotionEvent_TEST: SubView dispatchTouch actoin = ACTION_CANCEL 
    10-08 16:03:05.302 30933-30933/com.xy.eventest D/MotionEvent_TEST: SubView onTouchEvent actoin = ACTION_CANCEL 
    

    我们看日志可以知道,UP事件传到SubView后就办成了CANCEL事件。

    到这里我们学习完了CNACEL事件最初最初的产生过程了。

    那CANCEL事件的传递呢?我们再来测试一种情况。代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <com.xy.eventest.base.view.RootGroupView xmlns:android="http://schemas.android.com/apk/res/android"
        android:background="#ffffff"
        android:gravity="center"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.xy.eventest.base.view.SubGroupView
            android:layout_height="match_parent"
            android:layout_width="match_parent">
            <com.xy.eventest.base.view.SubView
                android:id="@+id/subview_one"
                android:background="#ff0000"
                android:layout_height="100dp"
                android:layout_width="100dp"/>
        </com.xy.eventest.base.view.SubGroupView>
    </com.xy.eventest.base.view.RootGroupView>
    

    HomeActivity的代码:

      @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return true;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_DOWN " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    

    RootViewGroup的代码:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_DOWN " );
                return super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_MOVE " );
                return true;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_UP " );
                return true;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_CANCEL " );
                return super.onInterceptTouchEvent(event);
            }
            return super.onInterceptTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_DOWN " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    
    

    SubViewGroup的代码:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , " onIntercept actoin = ACTION_DOWN " );
                return super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , " onIntercept actoin = ACTION_MOVE " );
                return  super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , " onIntercept actoin = ACTION_UP " );
                return super.onInterceptTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , " onIntercept actoin = ACTION_CANCEL " );
                return super.onInterceptTouchEvent(event);
            }
            return super.onInterceptTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "onTouch actoin = ACTION_DOWN " );
                return true;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "onTouch actoin = ACTION_MOVE " );
                return true;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "onTouch actoin = ACTION_UP " );
                return true;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "onTouch actoin = ACTION_CANCEL " );
                return true;
            }
            return super.onTouchEvent(event);
        }
    

    SubView的代码:

    @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_DOWN " );
                return super.onTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    

    RootGroupView在onInterceptTouchEvent()方法中都MOVE和UP事件进行拦截,SubView不消费事件,事件最终在SubViewGroup的onTouchEvent()方法中消费。
    我们看日志:

    10-08 16:28:26.901 32758-32758/com.xy.eventest D/MotionEvent_TEST: HomeActivity dispatchTouch actoin = ACTION_DOWN 
    10-08 16:28:26.902 32758-32758/com.xy.eventest D/MotionEvent_TEST: RootGroupView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:28:26.902 32758-32758/com.xy.eventest D/MotionEvent_TEST:  RootGroupView onIntercept actoin = ACTION_DOWN 
    10-08 16:28:26.902 32758-32758/com.xy.eventest D/MotionEvent_TEST: SubGroupView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:28:26.902 32758-32758/com.xy.eventest D/MotionEvent_TEST:  SubGroupView onIntercept actoin = ACTION_DOWN 
    10-08 16:28:26.902 32758-32758/com.xy.eventest D/MotionEvent_TEST: SubView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:28:26.902 32758-32758/com.xy.eventest D/MotionEvent_TEST: SubView onTouchEvent actoin = ACTION_DOWN 
    10-08 16:28:26.903 32758-32758/com.xy.eventest D/MotionEvent_TEST: SubGroupView onTouch actoin = ACTION_DOWN 
    10-08 16:28:26.954 32758-32758/com.xy.eventest D/MotionEvent_TEST: HomeActivity dispatchTouch actoin = ACTION_UP 
    10-08 16:28:26.955 32758-32758/com.xy.eventest D/MotionEvent_TEST: RootGroupView dispatchTouch actoin = ACTION_UP 
    10-08 16:28:26.955 32758-32758/com.xy.eventest D/MotionEvent_TEST:  RootGroupView onIntercept actoin = ACTION_UP 
    10-08 16:28:26.955 32758-32758/com.xy.eventest D/MotionEvent_TEST: SubGroupView dispatchTouch actoin = ACTION_CANCEL 
    10-08 16:28:26.955 32758-32758/com.xy.eventest D/MotionEvent_TEST: SubGroupView onTouch actoin = ACTION_CANCEL 
    

    我们看日志知道:UP事件传递到SubViewGroup时就变成了CANCEL事件了,并且没有传递到SubView。

    结论:MOVE,UP,CANCEL三者事件的传递机制是一样的,并且某一View能不能接收到MOVE,UP,CANCEL事件就看当前View或他的子View以及间接子View没有没消费DOWN事件。

    我们来看一中情况:

    <?xml version="1.0" encoding="utf-8"?>
    <com.xy.eventest.base.view.RootGroupView xmlns:android="http://schemas.android.com/apk/res/android"
        android:background="#ffffff"
        android:gravity="center"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.xy.eventest.base.view.SubGroupView
            android:gravity="center"
            android:layout_height="match_parent"
            android:layout_width="match_parent">
            <com.xy.eventest.base.view.SubView
                android:id="@+id/subview_one"
                android:background="#ff0000"
                android:layout_height="100dp"
                android:layout_width="100dp"/>
        </com.xy.eventest.base.view.SubGroupView>
    </com.xy.eventest.base.view.RootGroupView>
    

    HomeActivity的代码:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "HomeActivity dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return true;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_DOWN " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "HomeActivity onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    

    RootViewGroup的代码:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "RootGroupView dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_DOWN " );
                return super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_MOVE " );
                return true;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_UP " );
                return true;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , " RootGroupView onIntercept actoin = ACTION_CANCEL " );
                return super.onInterceptTouchEvent(event);
            }
            return super.onInterceptTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_DOWN " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "RootGroupView onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    

    SubViewGroup的代码:

    @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "SubGroupView dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "SubGroupView dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event) ;
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "SubGroupView dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "SubGroupView dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , " SubGroupView onIntercept actoin = ACTION_DOWN " );
                return super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , " SubGroupView onIntercept actoin = ACTION_MOVE " );
                return  super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "SubGroupView onIntercept actoin = ACTION_UP " );
                return super.onInterceptTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "SubGroupView onIntercept actoin = ACTION_CANCEL " );
                return super.onInterceptTouchEvent(event);
            }
            return super.onInterceptTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "SubGroupView onTouch actoin = ACTION_DOWN " );
                return super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "SubGroupView onTouch actoin = ACTION_MOVE " );
                return super.onInterceptTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "SubGroupView onTouch actoin = ACTION_UP " );
                return super.onInterceptTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "SubGroupView onTouch actoin = ACTION_CANCEL " );
                return true;
            }
            return super.onTouchEvent(event);
        }
    

    SubView的代码:

     @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_DOWN " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_MOVE " );
                return super.dispatchTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_UP " );
                return super.dispatchTouchEvent(event) ;
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "SubView dispatchTouch actoin = ACTION_CANCEL " );
                return super.dispatchTouchEvent(event);
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if( event.getAction() == MotionEvent.ACTION_DOWN ){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_DOWN " );
                return true ;
            }else if( event.getAction() == MotionEvent.ACTION_MOVE ){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_MOVE " );
                return super.onTouchEvent(event);
            }else if( event.getAction() == MotionEvent.ACTION_UP ){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_UP " );
                return super.onTouchEvent(event);
            }else if(event.getAction() == MotionEvent.ACTION_CANCEL){
                Log.d(TAG , "SubView onTouchEvent actoin = ACTION_CANCEL " );
                return super.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    

    RootViewGroup拦截了MOVE和UP事件,SubView消费了DOWN事件,但不消费MOVE,UP,CANCEL事件。我们看日志:

    10-08 16:42:13.665 2140-2140/com.xy.eventest D/MotionEvent_TEST: HomeActivity dispatchTouch actoin = ACTION_DOWN 
    10-08 16:42:13.665 2140-2140/com.xy.eventest D/MotionEvent_TEST: RootGroupView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:42:13.666 2140-2140/com.xy.eventest D/MotionEvent_TEST:  RootGroupView onIntercept actoin = ACTION_DOWN 
    10-08 16:42:13.666 2140-2140/com.xy.eventest D/MotionEvent_TEST: SubGroupView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:42:13.666 2140-2140/com.xy.eventest D/MotionEvent_TEST:  SubGroupView onIntercept actoin = ACTION_DOWN 
    10-08 16:42:13.666 2140-2140/com.xy.eventest D/MotionEvent_TEST: SubView dispatchTouch actoin = ACTION_DOWN 
    10-08 16:42:13.666 2140-2140/com.xy.eventest D/MotionEvent_TEST: SubView onTouchEvent actoin = ACTION_DOWN 
    10-08 16:42:13.795 2140-2140/com.xy.eventest D/MotionEvent_TEST: HomeActivity dispatchTouch actoin = ACTION_UP 
    10-08 16:42:13.795 2140-2140/com.xy.eventest D/MotionEvent_TEST: RootGroupView dispatchTouch actoin = ACTION_UP 
    10-08 16:42:13.795 2140-2140/com.xy.eventest D/MotionEvent_TEST:  RootGroupView onIntercept actoin = ACTION_UP 
    10-08 16:42:13.795 2140-2140/com.xy.eventest D/MotionEvent_TEST: SubGroupView dispatchTouch actoin = ACTION_CANCEL 
    10-08 16:42:13.796 2140-2140/com.xy.eventest D/MotionEvent_TEST: SubGroupView onIntercept actoin = ACTION_CANCEL 
    10-08 16:42:13.796 2140-2140/com.xy.eventest D/MotionEvent_TEST: SubView dispatchTouch actoin = ACTION_CANCEL 
    10-08 16:42:13.796 2140-2140/com.xy.eventest D/MotionEvent_TEST: SubView onTouchEvent actoin = ACTION_CANCEL 
    10-08 16:42:13.796 2140-2140/com.xy.eventest D/MotionEvent_TEST: HomeActivity onTouchEvent actoin = ACTION_UP 
    

    UP事件不是变为CANCEL事件了吗?为什么到Activity的onTouchEvent()方法中还是UP事件呢?我们看dispatchTransformedTouchEvent方法的源码:

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
                View child, int desiredPointerIdBits) {
            final boolean handled;
            final int oldAction = event.getAction();
            if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
                event.setAction(MotionEvent.ACTION_CANCEL);
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    handled = child.dispatchTouchEvent(event);
                }
                // 分析【2】
                event.setAction(oldAction);
                return handled;
            }
            final int oldPointerIdBits = event.getPointerIdBits();
            final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
            if (newPointerIdBits == 0) {
                return false;
            }
            final MotionEvent transformedEvent;
            if (newPointerIdBits == oldPointerIdBits) {
                if (child == null || child.hasIdentityMatrix()) {
                    if (child == null) {
                        handled = super.dispatchTouchEvent(event);
                    } else {
                        final float offsetX = mScrollX - child.mLeft;
                        final float offsetY = mScrollY - child.mTop;
                        event.offsetLocation(offsetX, offsetY);
                        handled = child.dispatchTouchEvent(event);
                        event.offsetLocation(-offsetX, -offsetY);
                    }
                    return handled;
                }
                transformedEvent = MotionEvent.obtain(event);
            } else {
                transformedEvent = event.split(newPointerIdBits);
            }
            if (child == null) {
                handled = super.dispatchTouchEvent(transformedEvent);
            } else {
                final float offsetX = mScrollX - child.mLeft;
                final float offsetY = mScrollY - child.mTop;
                transformedEvent.offsetLocation(offsetX, offsetY);
                if (! child.hasIdentityMatrix()) {
                    transformedEvent.transform(child.getInverseMatrix());
                }
                handled = child.dispatchTouchEvent(transformedEvent);
            }
            transformedEvent.recycle();
            return handled;
        }
    

    看分析【2】处,把CANCEL事件传递子View后,事件又变回了原来的事件,这里是UP事件,如果是MOVE事件变为CANCEL事件,那么传递给子View后,就是变为原来的MOVE事件。

    到这里我们就学习完了View的事件传递机制。

    上一篇:View的DOWN事件分发机制源码分析

    展开全文
  • pthread_testcancel pthread_kill pthread_cancel 的使用例子
  • TimerTask.cancel() 与 Timer.cancel() 的区别

    千次阅读 2019-01-20 19:54:12
    TimerTask.cancel() 与 Timer.cancel()都可以理解为清除任务队列中的任务。看源码 TimerTask.cancel() /** * Cancels this timer task. If the task has been scheduled for one-time * execution an...

            TimerTask.cancel() 与 Timer.cancel() 都可以理解为清除任务队列中的任务。看源码

            TimerTask.cancel() 

        /**
         * Cancels this timer task.  If the task has been scheduled for one-time
         * execution and has not yet run, or has not yet been scheduled, it will
         * never run.  If the task has been scheduled for repeated execution, it
         * will never run again.  (If the task is running when this call occurs,
         * the task will run to completion, but will never run again.)
         *
         * <p>Note that calling this method from within the <tt>run</tt> method of
         * a repeating timer task absolutely guarantees that the timer task will
         * not run again.
         *
         * <p>This method may be called repeatedly; the second and subsequent
         * calls have no effect.
         *
         * @return true if this task is scheduled for one-time execution and has
         *         not yet run, or this task is scheduled for repeated execution.
         *         Returns false if the task was scheduled for one-time execution
         *         and has already run, or if the task was never scheduled, or if
         *         the task was already cancelled.  (Loosely speaking, this method
         *         returns <tt>true</tt> if it prevents one or more scheduled
         *         executions from taking place.)
         */
        public boolean cancel() {
            synchronized(lock) {
                boolean result = (state == SCHEDULED);
                state = CANCELLED;
                return result;
            }
        }

            Timer.cancel(): 

        /**
         * Terminates this timer, discarding any currently scheduled tasks.
         * Does not interfere with a currently executing task (if it exists).
         * Once a timer has been terminated, its execution thread terminates
         * gracefully, and no more tasks may be scheduled on it.
         *
         * <p>Note that calling this method from within the run method of a
         * timer task that was invoked by this timer absolutely guarantees that
         * the ongoing task execution is the last task execution that will ever
         * be performed by this timer.
         *
         * <p>This method may be called repeatedly; the second and subsequent
         * calls have no effect.
         */
        public void cancel() {
            synchronized(queue) {
                thread.newTasksMayBeScheduled = false;
                queue.clear();
                queue.notify();  // In case queue was already empty.
            }
        }

            从源码中可以看出,TimerTask.cancel() 只是将当前正在执行的任务终止,state = CANCELLED,return true;如果当前任务已经执行结束,也是将当前任务终止,return false。由此我们也可以看出,当同一项任务调用多次 .cancel() 方法时,该方法只会有效执行一次。而Timer.cancel() 是将当前定时任务中的所有正在运行的任务都清除,使timer没有可执行的任务。所以两者的区别之处在TimerTask.cancel() 只是将当前任务终止,也可以理解为从定时任务(任务队列)中清除掉,但实际上并没有对Timer进行任何操作,所以在调用 TimerTask.cancel() 时,Timer定时任务(任务队列)中的其它任务还是正常执行,不会影响其它任务的正常执行而Timer.cancel() 是将整个定时任务(任务队列)的中的所有任务全部清除

           是不是调用了cancel()后任务会立即结束呢?我们从TimerTask.cancel源码的注释中也可以看到,If the task is running when this call occurs, the task will run to completion, but will never run again. 大概意思就是 当调用TimerTask.cancel() 方法时,该任务正在运行中,这项任务会正常运行到结束,但不会再次运行。同样的在Timer.cancel源码的注释中也可以看到一个类似的解释Does not interfere with a currently executing task (if it exists)翻译为中文大概就是 不干扰当前正在运行的任务(如果存在)。说明当调用cancel()方法后,当前正在运行的任务还能正常运行下去,还有一口气能把这项任务执行完毕,当任务执行完毕后,下次不会再执行,而没有开始执行的任务也不会执行

    测试

    测试1:task1和task2都不执行cancel()

    public class TestTimerCancel {
    
        public static void main(String args[]) throws ParseException, InterruptedException {
            Timer timer = new Timer();
            TimerTask task1 =  new TimerTask() {
                @Override
                public void run() {
                    System.out.println("任务执行时间1:" + this.scheduledExecutionTime());
                    // this.cancel();
                    // timer.cancel();
                }
            };
            TimerTask task2 =  new TimerTask() {
                @Override
                public void run() {
                    System.out.println("任务执行时间2:" + this.scheduledExecutionTime());
                    // this.cancel();
                    // timer.cancel();
                }
            };
            
            long delay1 = 1000;      // 任务延迟毫秒数
            long delay2 = 2000;      // 任务延迟毫秒数
            long period = 1000;      // 任务间隔毫秒数
            timer.schedule(task1, delay1, period);
            timer.schedule(task2, delay2, period);
        }
    }

    测试结果:两个任务正常执行

    任务执行时间1:1547991032207
    任务执行时间2:1547991033208
    任务执行时间1:1547991033208
    任务执行时间1:1547991034209
    任务执行时间2:1547991034209
    任务执行时间2:1547991035224
    任务执行时间1:1547991035224
    任务执行时间1:1547991036225
    任务执行时间2:1547991036225
    任务执行时间2:1547991037226
    任务执行时间1:1547991037226
    任务执行时间1:1547991038227
    任务执行时间2:1547991038227

    测试2:仅task1调用TimerTask.cancel()

    TimerTask task1 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间1:" + this.scheduledExecutionTime());
            this.cancel();
        }
    };

    测试结果:task1只执行了1次,调用TimerTask.cancel()不影响其它任务的正常执行

    任务执行时间1:1547991235852
    任务执行时间2:1547991236854
    任务执行时间2:1547991237857
    任务执行时间2:1547991238859
    任务执行时间2:1547991239860
    任务执行时间2:1547991240875
    任务执行时间2:1547991241890
    任务执行时间2:1547991242895
    任务执行时间2:1547991243900
    任务执行时间2:1547991244906

    测试3:仅task1调用Timer.cancel()

    TimerTask task1 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间1:" + this.scheduledExecutionTime());
            timer.cancel();
        }
    };

    测试结果:仅task1执行了一次,task2没有执行。

    任务执行时间1:1547991575545

     测试4:仅task2调用TimerTask.cancel()

    TimerTask task2 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间2:" + this.scheduledExecutionTime());
            this.cancel();
        }
    };

    测试结果:task2仅执行了一次,task1正常进行固定延迟执行

    任务执行时间1:1547991944113
    任务执行时间2:1547991945126
    任务执行时间1:1547991945126
    任务执行时间1:1547991946130
    任务执行时间1:1547991947144
    任务执行时间1:1547991948147
    任务执行时间1:1547991949161
    任务执行时间1:1547991950162
    任务执行时间1:1547991951175

    测试5:仅task2调用Timer.cancel()

    TimerTask task2 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间2:" + this.scheduledExecutionTime());
            timer.cancel();
        }
    };

    测试结果:task1 和task2都仅执行了一次。由于task1早于task2执行,task2在执行时task1处于执行完毕或者执行中,所以task2执行完后,整个任务队列清除。task2仅能执行一次,task1的执行次数跟task2执行结束时间有关,task2执行结束时间越晚task1执行次数越多。

    任务执行时间1:1547992056180
    任务执行时间2:1547992057187
    

    测试6:task1调用TimerTask.cancel(),task2调用Timer.cancel()

    TimerTask task1 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间1:" + this.scheduledExecutionTime());
            this.cancel();
            // timer.cancel();
        }
    };
    TimerTask task2 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间2:" + this.scheduledExecutionTime());
            // this.cancel();
            timer.cancel();
        }
    };

    测试结果:task1和task2都仅执行一次

    任务执行时间1:1547992543756
    任务执行时间2:1547992544753
    

    测试7:task1调用Timer.cancel(),task2调用TimerTask.cancel()

    TimerTask task1 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间1:" + this.scheduledExecutionTime());
            // this.cancel();
            timer.cancel();
        }
    };
    TimerTask task2 =  new TimerTask() {
        @Override
        public void run() {
            System.out.println("任务执行时间2:" + this.scheduledExecutionTime());
            this.cancel();
            // timer.cancel();
        }
    };

    测试结果:仅task1执行了1次

    任务执行时间1:1547992633651
    

     

    展开全文
  • 背景 Java 8的CompletableFuture提供了强大的...下面的 handleResult() 方法作为task完成时的回调函数(注意:task完成并不意味着task的实际工作一定已经运行结束了,比如timeout和cancel场景)。 public static vo

    背景

    Java 8的CompletableFuture提供了强大的task管理能力,比如通知机制,以及更加简单抽象的task管理。

    本文中,将用CompletableFuture的 supplyAsync() 方法来创建一个异步task,task会完成特定的工作,并且返回一个String。

    下面的 handleResult() 方法作为task完成时的回调函数(注意:task完成并不意味着task的实际工作一定已经运行结束了,比如timeout和cancel场景)。

        public static void handleResult(String result) {
            System.out.println("==========result: " + result + "===========");
        }
    

    一般来说,task的完成,有以下几种原因:

    • 正常结束
    • 异常结束
    • timeout
    • cancel

    本文将以代码示例,如何处理task的不同完成状态。

    注:本文中使用的有些方法(比如 completeOnTimeout() ),是Java 9提供的。

    单独流程

    正常流程

    使用 thenAccept() 方法来处理task结果,代码如下:

            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "hello");
    
            future.thenAccept(e -> handleResult(e));
            
            try {
                Thread.sleep(10* 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    

    注:在主线程里面sleep一段时间,其目的是确保task在主线程结束前完成工作。

    运行结果如下:

    ==========result: hello===========
    
    Process finished with exit code 0
    

    异常流程

    使用 exceptionally() 方法来处理task的异常,代码如下:

            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                throw new RuntimeException("xxx");
            });
    
            // future.thenAccept(e -> handleResult(e)); // will not take effect
    
            future.exceptionally(e -> {
                System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
                handleResult("default exception result");
                return "default exception result";
            });
    
            try {
                Thread.sleep(10* 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    

    运行结果如下:

    got exception: class java.util.concurrent.CompletionException, java.lang.RuntimeException: xxx
    ==========result: default exception result===========
    

    正常和异常流程合并处理

    使用 handle() 方法,它有2个参数:task结果和task异常,代码如下:

            future.handle((r, e) -> {
                if (e != null) {
                    System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
                    handleResult("default exception result");
                    return "default exception result";
                } else {
                    handleResult(r);
                    return r;
                }
            });
    

    timeout流程

    使用 completeOnTimeout() 方法,设置timeout时间,并且在timeout发生时指定task结果。代码如下:

            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(5 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "hello";
            });
    
            future.completeOnTimeout("default timeout result", 3 * 1000, TimeUnit.MILLISECONDS);
    
            future.handle((r, e) -> {
                if (e != null) {
                    System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
                    handleResult("default exception result");
                    return "default exception result";
                } else {
                    handleResult(r);
                    return r;
                }
            });
    
            try {
                Thread.sleep(10* 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    

    运行结果如下:

    ==========result: default timeout result===========
    

    注意:在 handle() 方法里走的是正常流程,而不是异常流程。

    注:也可以用 orTimeout() 方法,指定timeout时间,则在timeout发生时,task会抛出 TimeoutException 异常。

    注意:timeout并不会真正停止task的运行,也不会给task发interrupt信号。

    cancel流程

    使用 cancel() 方法来cancel task,并抛出 CancellationException 异常。代码如下:

            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(5 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "hello";
            });
    
            future.handle((r, e) -> {
                if (e != null) {
                    System.out.println("got exception: " + e.getClass() + ", " + e.getCause());
                    handleResult("default exception result");
                    return "default exception result";
                } else {
                    handleResult(r);
                    return r;
                }
            });
    
            try {
                Thread.sleep(3 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            future.cancel(true);
    
            try {
                Thread.sleep(10* 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    

    运行结果如下:

    got exception: class java.util.concurrent.CancellationException, null
    ==========result: default exception result===========
    

    注意:cancel操作的触发时机不可知,一般是在类之外被触发的,所以本例中把cancel操作放在了最后(前面都是对future的基本操作)。

    注意:cancel时,在 handle() 方法里走的是异常流程,其Exception为 CancellationException

    注意:timeout并不会真正停止task的运行,也不会给task发interrupt信号。

    合并流程

    现在,考虑所有4种情况,把逻辑处理合到一起,代码如下:

    package com.example.test0721;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.concurrent.CompletableFuture;
    import java.util.concurrent.TimeUnit;
    
    public class Test0724 {
        public static void handleResult(String result) {
            System.out.println("============result: " + result + "=============");
        }
    
        public static void main(String[] args) {
            Logger log = LoggerFactory.getLogger(Test0724.class);
    
            log.info("main: started");
    
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                log.info("async: started");
                try {
                    Thread.sleep(10 * 1000);
                } catch (InterruptedException e) {
                    log.info("async: interrupted");
                    return "default interrupted result";
                }
    
                boolean exceptional = false;
                if (exceptional) {
                    log.info("async: run into exception");
                    throw new RuntimeException("async exception");
                } else {
                    log.info("async: finished");
                    return "hello";
                }
            }).completeOnTimeout("default timeout result", 100 * 1000, TimeUnit.MILLISECONDS);
    
            // attention: split the line, because the cancel operation is against the above "future"
            future.handle((result, throwable) -> {
                log.info("async: result: " + result + ", throwable: " + throwable);
                if (throwable != null) {
                    log.info("async: got exception from async: " + throwable.getClass() + ", " + throwable.getCause());
                    handleResult("default exception result");
                    return "default exception result";
                } else {
                    log.info("got normal result: " + result);
                    handleResult(result);
                    return result;
                }
            });
    
    //        try {
    //            Thread.sleep(2 * 1000);
    //        } catch (InterruptedException e) {
    //            e.printStackTrace();
    //        }
    //
    //        log.info("default cancel value");
    //        future.cancel(true);
    //        log.info("main: async cancelled");
    
            try {
                Thread.sleep(15 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            log.info("main ended");
        }
    }
    

    正常流程

    直接运行代码,就是正常流程,运行结果如下:

    02:33:39.239 [main] INFO com.example.test0721.Test0724 - main: started
    02:33:39.259 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
    02:33:49.265 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: finished
    02:33:49.298 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: result: hello, throwable: null
    02:33:49.299 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - got normal result: hello
    ============result: hello=============
    02:33:54.268 [main] INFO com.example.test0721.Test0724 - main ended
    

    异常流程

    exceptional 变量设置为 true

                boolean exceptional = true;
    

    运行结果如下:

    02:34:23.351 [main] INFO com.example.test0721.Test0724 - main: started
    02:34:23.368 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
    02:34:33.371 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception
    02:34:33.410 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: result: null, throwable: java.util.concurrent.CompletionException: java.lang.RuntimeException: async exception
    02:34:33.411 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: got exception from async: class java.util.concurrent.CompletionException, java.lang.RuntimeException: async exception
    ============result: default exception result=============
    02:34:38.373 [main] INFO com.example.test0721.Test0724 - main ended
    

    timeout流程

    把task的timeout时间设置为5秒钟:

                    Thread.sleep(5 * 1000);
    

    运行结果如下:

    02:35:27.985 [main] INFO com.example.test0721.Test0724 - main: started
    02:35:27.996 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
    02:35:33.040 [CompletableFutureDelayScheduler] INFO com.example.test0721.Test0724 - async: result: default timeout result, throwable: null
    02:35:33.042 [CompletableFutureDelayScheduler] INFO com.example.test0721.Test0724 - got normal result: default timeout result
    ============result: default timeout result=============
    02:35:37.999 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception
    02:35:43.007 [main] INFO com.example.test0721.Test0724 - main ended
    

    注意:timeout并不会真正停止task的运行,也不会给task发interrupt信号。本例中,由于把 exception 设置为 true ,可以看到console有 async: run into exception 的输出。同理,假设task没有抛异常,则最终将会在console显示 async: finished

    cancel流程

    把下面的代码反注释:

            try {
                Thread.sleep(2 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            log.info("default cancel value");
            future.cancel(true);
            log.info("main: async cancelled");
            handleResult("default cancel value");
    

    即:在task运行到2秒钟的时候,cancel task。运行结果如下:

    02:41:03.815 [main] INFO com.example.test0721.Test0724 - main: started
    02:41:03.841 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: started
    02:41:05.841 [main] INFO com.example.test0721.Test0724 - default cancel value
    02:41:05.870 [main] INFO com.example.test0721.Test0724 - async: result: null, throwable: java.util.concurrent.CancellationException
    02:41:05.871 [main] INFO com.example.test0721.Test0724 - async: got exception from async: class java.util.concurrent.CancellationException, null
    ============result: default exception result=============
    02:41:05.875 [main] INFO com.example.test0721.Test0724 - main: async cancelled
    02:41:13.842 [ForkJoinPool.commonPool-worker-3] INFO com.example.test0721.Test0724 - async: run into exception
    02:41:20.876 [main] INFO com.example.test0721.Test0724 - main ended
    

    注意:cancel并不会真正停止task的运行,也不会给task发interrupt信号。本例中,由于把 exception 设置为 true ,可以看到console有 async: run into exception 的输出。同理,假设task没有抛异常,则最终将会在console显示 async: finished

    注意:本例中没有区分task自身的异常和cancel task造成的异常。若想取分的话,只需在 handle() 方法里对异常加以判断。timeout异常也同理。

    展开全文
  • redmine_enter_cancel-源码

    2021-06-11 06:40:09
    Redmine Enter Cancel插件 Redmine 插件忽略在工单主题文本字段中按下 Enter 键。 作者 @suer 安装 键入以下命令: $ cd $RAILS_ROOT/plugins $ git clone git://github.com/suer/redmine_enter_cancel.git 然后...
  • CANCEL

    2015-10-11 21:51:46
    我们讨论一个通用的方法,CANCELCANCEL请求,就像名字所说的,是用来取消客户端发起的上一个请求。特别是,它请求UAS去终止上一个请求并且对上一个请求产生一个错误的应答。CANCEL对UAS已经给出终结应答的请求无效...
  • AsyncTask之cancel操作

    2015-04-29 11:02:04
    详情请看文章:AsyncTask中cancel方法的误读(http://blog.csdn.net/chenleicpp/article/details/45363723)
  • Mysql cancel分析

    千次阅读 2020-12-22 17:40:38
    最近看了下mysql jdbc的cancel功能的源码,做个笔记记录下 mysql-connector-java-8.0.18.jar Mysql有个CancelQueryTask(CancelQueryTaskImpl)定时任务器,继承了TimerTask 在ClientPreparedStatement....
  • 前言 ...这时候,我们最好的办法是cancel掉这些无用的请求。 传统的cancel方式是这样的: 1.在类里面需要持有请求对象 @property (strong/weak, nonatomic) XXRequest *xxrequest1; 属性具体用stro
  • Android事件分发之ACTION_CANCEL机制及作用

    千次阅读 多人点赞 2019-04-23 14:11:04
    如果要查看ACTION_MOVE与ACTION_UP的事件传递机制,查看Android事件分发之ACTION_MOVE与ACTION_UP的传递机制 ACTION_CANCEL产生场景
  • centOS安装问题started cancel waiting for multipath siblings of sda。
  • timer cancel 计时器类cancel()方法 (Timer Class cancel() method) cancel() method is available in java.util package. cancel()方法在java.util包中可用。 cancel() method is used to cancel this Timer and ...
  • 线程取消(pthread_cancel)

    2018-12-25 18:02:28
    pthread_cancel调用并不等待线程终止,它只提出请求。线程在取消请求(pthread_cancel)发出后会继续运行, 直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进行动作的一个位置.  ...
  • C++ 多线程——pthread_cancel 取消线程的疑惑 测试环境:Ubuntu18.04 pthread_cancel 简介 pthread_cancel(threadID)会发送终止信号给thread线程,如果成功则返回0,否则为非0值。 pthread_cancel调用并不等待线程...
  • 文章目录Timer常见问题方法schedule(Timer Task task, long delay)方法schedule(Timer Task task, long delay, long period)cancel方法TimerTask类的cancel()方法Timer类的cancel()方法timer.cancel的最常见一个问题...
  • 解决 Kali Linux 虚拟机中 fsckd-cancel-msg: Press Ctrl+C to cancel all filesystem checks in progress 的报错问题   首先,博主简要介绍一下,导致该报错的原因:   博主打开虚拟机的bios设置,移动过Boot的...
  • 本地环境:Windows 10 系统 编译器:IDEA 2018.3.4 代码仓库:GitHub 在用【git push】提交测试代码的时候,出现了“登录失败,使用ctrl+c取消基本凭证”的提示,如下: Logon failed, use ctrl+c to cancel basic ...
  • 在使用边界计时器事件时,事件有一个参数,Cancel activity: 作用上图,如下: Cancel activity选中 在事件中设置5秒计时,当开始流程后,如果task1在5秒内没有处理完,则中断流程,创建分支实例走向task2,不在...
  • https://www.rabbitmq.com/consumer-cancel.html Overview When a channel is consuming from a queue, there are various reasons which could cause the consumption to stop. One of these is obviously if ....
  • cancel三个方法(try和cancel可被裁减),就能自动进行事务驱动 通过异常自动控制事务流程进入异步重试还是回滚流程 提供积压任务监控页面,方便查看当前任务状态 Development guide 1. 增加Maven依赖 在项目pom.xml...
  • REST API级别0-Eclipse Vert.x Booster 重要的 ...$ cd test-cancel-build $ mvn compile vertx:run 在本地与Booster交互 要在本地运行的助推器进行交互,请使用的表单或curl命令: $ curl http://lo
  • cancel-so-also-cancel-po 取消销售订单同时也取消对应自动生成的采购询价单 使用及时(JIT)生产计划管理模块 由 销售驱动 采购 :当销售订单被确认时,能自动生成对应的采购询价单。 当 销售订单 被取消的时候, ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 316,945
精华内容 126,778
关键字:

cancel