博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
为什么我的子线程更新了 UI 没报错?借此,纠正一些Android 程序员的一个知识误区...
阅读量:6568 次
发布时间:2019-06-24

本文共 2929 字,大约阅读时间需要 9 分钟。

开门见山:

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

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

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

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

@Overrideprotected 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 初始化之前。

修改验证 --- 抛出错误

@Overrideprotected 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();}

转载地址:http://bspjo.baihongyu.com/

你可能感兴趣的文章
前端面试通关指南
查看>>
制作首页的显示列表。
查看>>
同样加班 不同收获
查看>>
数据公钥加密和认证中的私钥公钥
查看>>
c语言中的位移位操作
查看>>
object-c语言的nonatomic,assign,copy,retain的区别
查看>>
js 正则之检测素数
查看>>
linux-多线程
查看>>
第40周二
查看>>
使用虚拟机运行Ubuntu时,主机与宿主机共享文件的方法。
查看>>
EJB究竟是什么,真的那么神奇吗??
查看>>
海茶3 らぶデス3 入门经典教程
查看>>
pstree命令
查看>>
css选择器顺序的小技巧
查看>>
dojo 学习笔记之dojo.query - query(id) 与query(class)的差别
查看>>
Java基础加强总结(三)——代理(Proxy)
查看>>
一步一步写算法(之hash表)
查看>>
C99规范
查看>>
BZOJ3799 : 字符串重组
查看>>
数据持久化的复习
查看>>