谁说Android子线程一定不能更新UI

谁说的Android子线程一定不能更新UI,不用handler、View的post和Activity的runOnUiThread特定情况下一样可以再子线程更新UI。

天下武功,唯快不破。

先说一下结论,在Activity的onCreate方法执行入口创建子线程然后内部更新UI是可以正常更新的,不会抛异常发生闪退。但是,如果晚一会就不行了,前面加个1秒延时就会闪退。

这里测试的一个场景是在onCreate中创建一个子线程来更新状态栏颜色。

代码如下:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {

//延时 1秒钟
                Thread.sleep(1000);
                Log.e(TAG, "setListener-end: 2   "+Thread.currentThread().getName() );
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.e(TAG, "setListener-end: " + Thread.currentThread().getName());
            //子线程只有上面的延时不会闪退;子线程无延时只修改状态栏颜色不闪退;子线程先修改状态栏颜色再执行延时不闪退;但是先延时再该颜色就闪退。。。。。
            StatusBarUtil.getInstance().setStatusBar(MainActivity.this, getResources().getColor(R.color.red), true);

        }
    }).start();

}
 /**
     * lightColor代表在黑白两个模式设置颜色,true展示wifi、电池、时间等为黑色,false时以上为白色,所以请设置色值的时候注意一下用哪个合适
     * @param activity
     * @param color
     * @param lightColor
     */
    public void setStatusBar(BaseActivity activity,int color, boolean lightColor) {
        Log.e("setStatusBar  ", "currentThread: "+Thread.currentThread().getName() );
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        activity.getWindow().setStatusBarColor(color);
        if (lightColor) {
            activity.getWindow().getDecorView()
                    .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        } else {
            activity.getWindow().getDecorView()
                    .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        }

    }

   

这是不加延正常更新了状态栏为红色:

这是加了延时以后的闪退报错信息,这里我就截图了,文字可能会换行导致阅读性变差。

抛异常的内容是“Only the original thread that created a view hierarchy can touch its views”,顺着日志一点一点查问题吧!

1、根据第一行很快定位直接原因是,ViewRootImpl类的checkThread方法:

2、根据第二行确定:ViewRootImpl类的requestLayout方法调用checkThread方法

3、根据第三行确定:View类的requestLayout方法调用了ViewRootImpl类的requestLayout方法

4、根据第三行确定:View类的setLayoutParams

调用了requestLayout方法

5、然后现在基本可以确定是按这个链路走的,但是为什么加了1秒延时就会触发异常不加就没事呢?

问题就是上面第四部:

if (mParent != null && !mParent.isLayoutRequested()) {
    mParent.requestLayout();
}

如果不满足这两个条件便不会走到最后的抛异常部分。

所以这个mParent是谁?给它赋值的只有两处,一处是 mParent=null,所以我们直接看另一处了。

他是在ViewRootImpl类的setView方法被调用的:

这个方法代码有点多,直接去有用部分:

  1. 所以上面第四部那个mParent就是ViewRootImpl了
  2. 那就看ViewRootImpl什么时候初始化的了。

7、ViewRootImpl的setView方法往上追踪是

WindowManagerGlobal的updateViewLayout方法调用:

8、再向上,到了ActivityThread的handleResumeActivity方法,这调用Activity的onResume声明周期的方法。

这是其中调用WindowManagrImple的updateViewLayout方法

因此再onCreate的开启子线程更新ui不跑异常是因为当时ViewRootImpl还没被初始化,而他的初始化时机是在onResume方法中。如果了延时等到延时结束如果ViewRootImpl正好已经初始化成功了,那就抛异常闪退了。

才疏学浅,如有错误,欢迎指正,多谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值