[Android]View.post(),android7.0(sdk24以上)不执行的问题(部分Click点击事件无效的原因)

原创 2017年01月03日 19:11:07

我们熟知View.post()和Handler.post(),虽然最后执行过程还会走到Handler的post()方法中,但是View.post()做了许多额外的工作,所以我认为如非迫不得己,建议直接使用Handler.post()方法,详情见此文。

如果在android7.0(sdk 24及以上)开发过程中,如果你的view没有通过addView添加到视图的时候,就会导致对应view的点击事件无效,以及view.post不执行,可能就是本文原因了,

以下是view.post在sdk版本24及以上的post方法

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

下面是sdk23及以下的post方法

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);
        return true;
    }


我们很容易的发现,24以上的sdk使用的是        getRunQueue().post(action);而sdk23以及以下是用的ViewRootImpl.getRunQueue().post(action);

其中,getRunQueue()的方法是

   /** 
     *  Returns the queue of runnable for this view.
     *
     * @return the queue of runnables for this view
     */
    private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

这个mRunQueue是View类中一个私有变量。

ViewRootImpl可以理解是一个activity的View树的树根,每个ViewRootImpl管理对应的DecoView和View树

ViewRootImpl中的队列是一个静态变量,也就是只有一个这个队列存在于这个app的生命周期中

static HandlerActionQueue getRunQueue() {
        HandlerActionQueue rq = sRunQueues.get();
        if (rq != null) {
            return rq;
        }
        rq = new HandlerActionQueue();
        sRunQueues.set(rq);
        return rq;
    }

在view.post中,并不是post完毕后就会执行,无论高低版本的View.post,只是把Runnable添加到队列,等待进行操作,这和Handler.post不同

其中,SDK24以上是HandlerActionQueue类中

public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }

sdk 23以下是ViewRootImpl的静态方法

void executeActions(Handler handler) {
            synchronized (mActions) {
                final ArrayList<HandlerAction> actions = mActions;
                final int count = actions.size();

                for (int i = 0; i < count; i++) {
                    final HandlerAction handlerAction = actions.get(i);
                    handler.postDelayed(handlerAction.action, handlerAction.delay);
                }

                actions.clear();
            }
        }

在SDk24及以上我们可以了解到只有在View的dispatchAttachedToWindow方法中执行,如果这个view不是通过addview等方法加入父视图的话,就无法调用dispatchAttachedToWindow,从而无法执行View.post,而post方法影响着click方法,即为

case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                       }

                        if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                            // This is a tap, so remove the longpress check
                            removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    mIgnoreNextUpEvent = false;
                    break;

从而高版本sdk24及以上可能导致点击事件失效。

而在23及以下版本中,ViewRootImpl的executeActions会频繁的调用,ViewRootImpl中的TraversalRunnable进行调用doTraversa来()进行调用。而TraversalRunnable是通过Choreographer的postCallBack循环调用,这个Choreographer通过doScheduleCallback进行一个MSG_DO_SCHEDULE_CALLBACK类型的循环操作(它每隔一段时间操作(ms级别))。详情请查看以后的Choreographer文章

说完了问题原因,解决方法如下:

关于点击事件,首先一定保证对应的view已经addview到父视图中,这样可能解决问题,当然不一定满足业务需求,也不一定能完美解决,那么可以通过重写View的对应post方法进行处理,如下

 private Handler mHandler;
    @Override
    public boolean post(Runnable action) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && action!= null && !isAttachedToWindow()) {
            mHandler = new Handler();
            return mHandler.post(action);
        }
        return super.post(action);
    }

    @Override
    public boolean removeCallbacks(Runnable action) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && action != null && !isAttachedToWindow()&& mHandler != null) {
            mHandler.removeCallbacks(action);
            return true;
        }
        return super.removeCallbacks(action);
    }



版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android 自定义View的post(Runnable)方法非100%执行的原因和处理方法解析

最近在写一个需求,需要在view.post(Runnable)方法当中进行一些操作。但是实际使用中(特定场景)发现并不靠谱。得到的现象是: 如果调用了view的post(Runnable)方法,该...

关于Android中调用了post方法后貌似没有执行run方法的解释及解决办法

(真纠结,刚刚发了之后才发现排版太乱了,稍作修改再发了哈~) 哎……之前纠结过Handler的运行机制,后来貌似懂了,但是近几天又被自己的工程绕的好像又不懂了一样!! ...

Android7.0 Activity(第一次)点击无响应,onClick,onTouch不执行

最近开发两个项目,遇到了同一个问题:进入Activity中,点击任何地方都没反应。这个问题太奇怪了,问了很多人都没能解决,最后自己一行一行注释,看执行到哪里出的问题,终于解决了。系统:华为v8:And...

Android中几种click点击事件

package com.dongyonghui.mrdong916.fuzhushou; import android.graphics.Color; import android.support...

Android开发之PullToRefresh的Click点击事件的监听实现长按删除Item

本文为原创博客,出自http://blog.csdn.net/minimicall 到今天为止,搜芽的卖家版本应该来说已经基本完成,攻坚克难的一路过来。速度也控制的比较好。 项目过程进度 ...

Android chart、图表、条形图、饼图 pie chart 等的实现及点击事件、监听器的实现 click ,achartengine

最近需要在自己的android应用上实现统计图的功能,后来定位实现饼图(扇形图),网上虽然有很多的例子和各种实现方法,但是就老是没有找到一个可以在统计图尤其是饼图中实现点击(click)功能的demo...

android子view点击事件(click)和父view长点击事件(longclick)冲突

引用块内容 android子view点击事件(click)和父view长点击事件(longclick)冲突工作中想要实现这么一个效果: 如图中,当child有一个click事件,parent有一...

Android7.0 自定义控件addView(...)无效,View的绘制流程(onMeasure、onLayout等)完全没执行的解决办法。

问题描述昨天在写一个自定义控件的时候遇到一个问题,就是我通过addView(View child) 方法添加View之后再7.0上没有显示出来,6.0上没有问题。通过AS自带的Layout Inspe...

Android 实现单击View,中间出现水波纹效果,在执行点击事件。

1.自定义布局 package com.example.waterview; import java.util.ArrayList; import android.content.Contex...

Android好奇宝宝_开山篇_解析ListView点击事件冲突原因

Android好奇宝宝系列主要是尝试解释一些开发过程中遇到的一些疑问。特别是一些知道该怎么做,但不知道为什么要这么做的问题。本猿能力有限,有错误处请各路大牛不吝赐教,万分感谢!!
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)