为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区

开门见山:

这个误区是:子线程不能更新 UI ,其应该分类讨论,而不是绝对的。

半小时前,我的 XRecyclerView 群里面,一位群友私聊我,问题是:

为什么我的子线程更新了 UI 没报错?

我叫他发下代码我看,如下,十分简单的代码。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    title = (TextView) findViewById(R.id.title_tips);
    doGet("http;//www.baidu.com", new Callback() {
        @Override
        public void onFailure(Request request, IOException e) {
            
        }
        @Override
        public void onResponse(Response response) throws IOException {
            title.setText(response.body().string()); // 这里在子线程更新了 text
        }
    });
}

private void doGet(String url,Callback callback) {
    OkHttpClient client = new OkHttpClient();
    
    Request.Builder builder = new Request.Builder();
    Request request = builder.url(url).get().build();

    client.newCall(request).enqueue(callback);
}

简单解析下。他用了 OkHttp 的异步 enqueue 的请求,并在成功后更新了 textView 的 text。

明确一点:

  • okhttp 的同步异步的回调都是在子线程里面的。

那么这样来说,按照我们被一直灌输的原理: 子线程不能刷新UI,上面这段代码妥妥地爆错啊。

而我要说的是:

上面的代码不一定爆错,它还会稳稳的顺利执行。

你十分怀疑了?

你可以尝试下。嫌麻烦,你可以运行下下面这段通透的子线程更新UI代码

public class TestActivity extends Activity {
    private TextView title;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        title = (TextView) findViewById(R.id.title_tips);
        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        // 子线程更新UI
                        title.setText("我 tm 妥妥地执行完毕");
                    }
                }
        ).start();
    }
}

试了的都知道,真 tm 执行了没爆错。

颠覆了吗?

原因

在看到他发给我的代码,onCreate 里面的部分,一切已经明了,这也是我之前面试几年经验的人设过的坑。下面我直接讲原因,源码分析那些你们自己去看吧,你应该去看

  • 子线程不能更新 UI 的限制是 viewRootImpl.java 内部限制了 void checkThread() { // 该方法是 viewRootImpl.java 内部代码 if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
  • 对组件 Activity 而言,viewRootImpl 的初始化在 onCreate 之后,onResume 之后。
  • 如果你的子线程更新代码在满足下面的条件下,那么它可以顺利运行:
    • 修改应用层的 viewRootImpl.java 源码,解除限制
    • 把你更新代码写在 onResume 之前,例如 onCreate 里面,且,更新之际要赶在 viewRootImpl 初始化之前。

修改验证 --- 抛出错误

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    title = (TextView) findViewById(R.id.title_tips);
    new Thread(
            new Runnable() {
                @Override
                public void run() {
                    try {
                        // 等待 onResume 执行完,让 viewRootImpl 初始化完成
                        Thread.sleep(3000); // ---------- 这里,看这里
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    title.setText("我执行不了");
                }
            }
    ).start();
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏双十二技术哥

Android fragment 标签加载过程分析

在上一篇文章中我们介绍了 AsyncLayoutInflater 使用的注意事项及改进方案。

781
来自专栏腾讯Bugly的专栏

《Android基础:Fragment,看这篇就够了》

| 导语 Fragment作为Android最基本,最重要的基础概念之一,在开发中经常会和他打交道。本文从为什么出现Fragment开始,介绍了Fragment...

1K4
来自专栏7号代码

Android应用界面开发——Fragment(实现图书详情界面)

Fragment代表了Activity的子模块,因此可以把Fragment理解成Activity片段。

1172
来自专栏青青天空树

安卓动态添加碎片

  2.   新建一个类Fragment1.java,继承自Fragment,注意Fragment有两个不同的包,推荐使用support-v4中的,兼容性更好,...

742
来自专栏程序员互动联盟

【Android基础】Fragment 详解之Fragment介绍

Fragment在Android 3.0( API 11)引入,是为了支持在大屏上显示更加动态、灵活的UI,比如在平板和电视上。Fragment可以看作是嵌套的...

3508
来自专栏向治洪

顺序广播和无序广播

普通广播(Normal Broadcast): 一,优缺点:和有序广播的优缺点相反! 二,发送广播的方法:sendBroadcast() 有序广播(Or...

1879
来自专栏Android-JessYan

传统MVP用在项目中是真的方便还是累赘?

原文地址: http://www.jianshu.com/p/ac51c9b88af3 qq群:301733278

761
来自专栏yang0range

Activity 堆栈相关处理方法

841
来自专栏Android开发经验

事件分发主要过程

1132
来自专栏Java与Android技术栈

用kotlin打造简化版本的ButterKnife

大名鼎鼎的 ButterKnife 库相信很多 android 开发者都听过,在 Github 上star的数目已经快15k了,而且很多知名的app都在使用。

813

扫码关注云+社区