zoukankan      html  css  js  c++  java
  • 为什么我的子线程更新了 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();
    }
    

  • 相关阅读:
    虚函数和纯虚函数
    MS CRM 2011中PartyList类型字段的实例化
    MS CRM 2011的自定义与开发(12)——表单脚本扩展开发(4)
    MS CRM 2011的自定义与开发(12)——表单脚本扩展开发(2)
    MS CRM 2011的自定义和开发(10)——CRM web服务介绍(第二部分)——IOrganizationService(二)
    MS CRM 2011 SDK 5.08已经发布
    MS CRM 2011 Q2的一些更新
    最近很忙
    Microsoft Dynamics CRM 2011最近的一些更新
    补一篇,Update Rollup 12 终于发布了
  • 原文地址:https://www.cnblogs.com/linguanh/p/7898996.html
Copyright © 2011-2022 走看看