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

来源:互联网 发布:qq伪装手机型号软件 编辑:程序博客网 时间:2024/05/21 15:33

在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:: 版本号:23com.cn.liuyz.customviewdemo D/liuyz:: 创建View后直接postcom.cn.liuyz.customviewdemo D/liuyz:: ViewGroup添加了addView()

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

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

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

com.cn.liuyz.customviewdemo D/liuyz:: 版本号:24com.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:: 版本号:24com.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的作用了。

原创粉丝点击