android中子线程可以更新UI,是真的吗?

看到这个问题,相信很多小伙伴认为,android开启子线程更新UI,是不对的,为什么呢?

因为只有主线程(UI线程)才可以进行UI的修改,如果在子线程进行UI更新,会抛出异常:

    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views

是的,上面的表述是对的,但是,为什么说子线程又可以更新UI呢?这种说法可靠吗?

现在,我们用事实(代码)说话,来一段,

场景1:在MainActivity中开启子线程,设置TextView的内容,布局文件中有个TextView,比较简单,这里忽略,如下:

public class MainActivity extends AppCompatActivity {
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v("MainActivity", "onCreate执行...");
        initView();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.v("MainActivity", "准备UI更新...");
                tv.setText("测试子线程更新UI");
                Log.v("MainActivity", "完成UI更新...");
            }
        }).start();

    }

    private void initView() {
        tv = (TextView) this.findViewById(R.id.tv);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.v("MainActivity", "onResume执行...");
    }
}

可以在日志看到:

    onCreate执行...
    onResume执行...
    准备UI更新...
    完成UI更新...
    I/ActivityManager:Displayed com.rockykou.debug/.MainActivity: +868ms

运行结果:正常,界面显示“测试子线程更新UI

场景2:在子线程的更新UI代码前,加上Thread.sleep(),其他与上面场景一致,如下:

public class MainActivity extends AppCompatActivity {
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.v("MainActivity", "onCreate执行...");
        initView();
        new Thread(new Runnable() {
            @Override
            public void run() {
                //添加部分
                try {
                    Thread.sleep(5 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.v("MainActivity", "准备UI更新...");
                tv.setText("测试子线程更新UI");
                Log.v("MainActivity", "完成UI更新...");
            }
        }).start();

    }

    private void initView() {
        tv = (TextView) this.findViewById(R.id.tv);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.v("MainActivity", "onResume执行...");
    }
}

可以在日志看到:

    onCreate执行...
    onResume执行...
    I/ActivityManager: Displayed com.rockykou.debug/.MainActivity: +1s33ms
    准备UI更新...
    AndroidRuntime: FATAL EXCEPTION: Thread-161
              android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
              at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:4607)

              ...

运行结果:异常,app崩溃

比较下,场景1情况,子线程更新UI正常,场景2情况,加上Thread.sleep(5000),子线程更新UI异常,而且注意场景2中有个ViewRootImpl.checkThread方法,

怎么解释上述问题呢?

首先,这是两个线程,

场景1,主线程执行onCreate,开启子线程,主线程继续执行onResume,接下来,子线程执行,这里明确一点,ViewRootImpl类l对象没有被创建,checkThread没有被执行,直到子线程执行完,UI更新正常完成

场景2,主线程执行onCreate, 开启子线程,主线程继续执行onResume,子线程处于睡眠5秒状态,当继续执行子线程代码时,ViewRootImpl对象已经被创建,checkThead方法被调用,检查出当前线程(也就是子线程)与原来的线程(主线程)不一致,抛出异常

实际上,子线程执行更新UI代码时,如果ViewRootImpl对象如果已经创建,UI更新异常,未被创建,UI更新正常,


最终,根本在cpu对时间片的分配上,

当主线程有足够的时间执行时,ViewRootImpl对象被创建,子线程更新UI异常

当主线程没有足够时间(此时子线程在占有时间片)执行时,ViewRootImpl对象未创建,子线程更新UI正常



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值