android之我们可不可以在子线程中更新UI系列一

我们先来看一段代码:


    Button btn;
    TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        btn=findViewById(R.id.button);
        textView=findViewById(R.id.text);
        btn.setText(Thread.currentThread().getName());
    }
    @Override
    protected void onResume() {
        super.onResume();
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(){
                    @Override
                    public void run() {
                        super.run();

                        try {
                       
                            textView.setText("子线程更新UI"+getName());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }


                    }
                }.start();
            }
        });
    }

 

这段代码是在子线程中更新UI,按理说根据android中View是非线程安全,不可以在子线程更新,如果这样做,应该会抛出一个Only the original thread that created a view hierarchy can touch its views    这样的异常,但是我们跑在模拟器上看一看:

android之我们可不可以在子线程中更新UI系列一

竟然可以绘制出来,并且没有报错,是不是很神奇,那么我们就DEBUG进去看看,我们在

TextView的这个checkForRelayout发现当我们控件高度并没有改变时,android系统并没有去检查线程,而直接去绘制了。这里我摘了这个方法里面的主要代码
..
..
    // Dynamic height, but height has stayed the same,
    // so use our new text layout.
    if (mLayout.getHeight() == oldht
            && (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
        autoSizeText();
        invalidate();
        return;
 // 由于控件的宽度并没有改变,好像并不是网上说的ViewRootImpl这个对象还没创建,在这里已经就绘制了,并且return了
        
    }
}

// We lose: the height has changed and we have a dynamic height.
// Request a new view layout using our new text layout.
requestLayout(); //这里最终调用ViewRootImpl里面的回调方法,并且去检查线程
invalidate();

...

...

}

是不是很神奇,那么我就在子线程中重新设置textView的宽高

LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(300,100);
textView.setLayoutParams(lp);
textView.setText("子线程更新UI"+getName());

所以结果可想而知了,系统给我们抛出了异常

2019-03-26 18:52:29.606 32010-32041/com.example.test E/AndroidRuntime: FATAL EXCEPTION: Thread-4
    Process: com.example.test, PID: 32010
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

所以我们最好还是不要在子线程中更新UI。

参考博客:https://blog.csdn.net/qq_21937107/article/details/79998194

                  https://blog.csdn.net/l81372500/article/details/79908139

                  https://blog.csdn.net/mountain_hua/article/details/81288194

下一篇,我准备整理一下Handler机制和原理,未完待续...