在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()中的任务将不再执行
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的作用了。