View.post在Android 7.0 api24(以上)已不再100%执行

在Android 7.0 api24,Android 8.0 api25的手机上如果通过new创建的View,如果没有将它通过addView()加入到ViewGroup布局中,那通过View.post()发送出去的任务将不再执行,也就无法通过Viwe.post更新UI。

实例

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            mTv1.setText("这是Handler发送的Message");
        }
    };

    private void btn2Click() {
        final View view = new View(this);
        Log.d("liuyz:", "版本号:" + Build.VERSION.SDK_INT);
        view.post(new Runnable() {
            @Override
            public void run() {
                Log.d("liuyz:", "创建View后直接post");
            }
        });

        //Handler发送一个延迟2秒的任务
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("liuyz:", "ViewGroup添加了addView()");
                mRoot.addView(view);
            }
        }, 2000);
    }

很简单,创建一个View,一个直接调用View.post(),一个通过Handler.post()延迟2秒执行ViewGroup.addView()方法。

当在api23上不注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:

com.cn.liuyz.customviewdemo D/liuyz:: 版本号:23
com.cn.liuyz.customviewdemo D/liuyz:: 创建View后直接post
com.cn.liuyz.customviewdemo D/liuyz:: ViewGroup添加了addView()

当注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:

com.cn.liuyz.customviewdemo D/liuyz:: 版本号:23
com.cn.liuyz.customviewdemo D/liuyz:: 创建View后直接post

当在api24上不注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:

com.cn.liuyz.customviewdemo D/liuyz:: 版本号:24
com.cn.liuyz.customviewdemo D/liuyz:: ViewGroup添加了addView()
com.cn.liuyz.customviewdemo D/liuyz:: 创建View后直接post

当注释掉Handler.post()方法中mRoot.addView(view)时,打印结果:

com.cn.liuyz.customviewdemo D/liuyz:: 版本号:24
com.cn.liuyz.customviewdemo D/liuyz:: ViewGroup添加了addView()

由此可得知在Android 7.0 api24的手机上,自定义View如果不通过addView()加入到ViewGroup中,那view.post()中的任务将不再执行

Android 7.0源码分析

View.post源码

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

        //假设它待会会成功执行
        getRunQueue().post(action);
        return true;
    }

当AttachInfo不为空时,调用Handler.post()执行任务,这个没啥说的。
当AttachInfo为空时,官方给的注释 假设它待会会成功执行 也就是说可能不执行。

那下面就看下怎么不执行

getRunQueue方法

private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

返回一个HandlerActionQueue对象

再看post()方法

public void post(Runnable action) {
        postDelayed(action, 0);
    }

public void postDelayed(Runnable action, long delayMillis) {
        //创建一个HandlerAction对象
        final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

        synchronized (this) {
            if (mActions == null) {
                //创建一个大小为4的数组
                mActions = new HandlerAction[4];
            }
            //给HandlerAction数组赋值即添加任务
            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
            mCount++;
        }
    }

post方法就是把任务封装到HandlerAction对象中,再添加到HandlerAction数组中,当需要执行的时候,通过循环执行任务。

那看下最终处理任务的方法

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

当需要处理任务时,调用的是executeActions方法,通过for循环执行封装到HandlerAction对象中的任务,最后任务的执行是通过handler.postDelayed()执行,也是Handler的那套机制。

那看起来也挺靠谱啊,为什么就不是100%执行呢?请往下看

executeActions方法是在View的dispatchAttachedToWindow()方法中调用的

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        ....省略....
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        ....省略....
    }

dispatchAttachedToWindow方法的调用,是依赖于ViewGroup的addView(View child)方法,看源码

public void addView(View child) {
        addView(child, -1);
    }

public void addView(View child, int index) {
        ....省略....
        addView(child, index, params);
    }

public void addView(View child, int index, LayoutParams params) {
        ....省略....
        addViewInner(child, index, params, false);
    }

private void addViewInner(View child, int index, LayoutParams params,
            boolean preventRequestLayout) {
        ....省略....
        AttachInfo ai = mAttachInfo;
        if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
            boolean lastKeepOn = ai.mKeepScreenOn;
            ai.mKeepScreenOn = false;

            //在此调用了自定义View的方法
            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
            if (ai.mKeepScreenOn) {
                needGlobalAttributesUpdate(true);
            }
            ai.mKeepScreenOn = lastKeepOn;
        }
        ....省略....
    }

看到这里应该能稍微看明白点了吧,其实只要记住,在Android 7.0以后不要使用View.post了,使用Handler.post或其他方式代替就行

总结

如果使用new或者LayoutInflater创建一个View,而没有通过addView()将它添加到ViewGroup布局中,那View.post()发出去的Runnable任务将不会被执行到,也就起不到更新UI的作用了。

你可能感兴趣的:(普通)