public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView tv = (TextView) findViewById(R.id.tv);
Log.d(TAG, "run0: " + Thread.currentThread().getId());
new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "run1: " + Thread.currentThread().getId());
Looper.prepare();
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "run2: " + Thread.currentThread().getId());
tv.setText("错误");
}
});
Looper.loop();
}
}).start();
}
}
tv正确执行了更新
输出结果是:
03-02 10:32:18.932 13244-13244/com.kyle.myapplication D/MainActivity: run0: 1
03-02 10:32:18.942 13244-14758/com.kyle.myapplication D/MainActivity: run: 408
03-02 10:32:18.942 13244-14758/com.kyle.myapplication D/MainActivity: run2: 408
感谢[@赵凯强]给出的解答(http://blog.youkuaiyun.com/zhaokaiqiang1992/article/details/43410351)
虽然是在子线程中setText,但是这时候View还没画出来呢,所以并不会调用之后的invalidate,而相当于只是设置TextView的一个属性,不会invalidate,就没有后面的那些方法调用了,归根结底,就不会调用ViewRootImpl的checkThread,也就不会报错
还有补充
Toast和View本质上是不一样的,Toast的显示是系统服务回调TN.show()显示的,Toast的显示需要添加到一个MessageQueue中,然后Looper取出来,发给Handler调用显示,子线程因为没有Looper,所以需要加上Looper.prepare和Looper.loop创建一个Looper,但是实质上,这还是在子线程调用。本质上Toast并不涉及到子线程更新UI的操作