参考:https://www.cnblogs.com/joy99/p/6121280.html
子线程是不能直接更新UI的。Android实现View更新有两组方法,分别是invalidate和postInvalidate。前者在UI线程中使用,后者在非UI线程即子线程中使用。换句话说,在子线程调用 invalidate 方法会导致线程不安全。熟悉View工作原理的人都知道,invalidate 方法会通知 view 立即重绘,刷新界面。作一个假设,现在我用 invalidate 在子线程中刷新界面,同时UI线程也在用 invalidate 刷新界面,这样会不会导致界面的刷新不能同步?这就是invalidate不能在子线程中使用的原因。
但是我们可以在子线程执行某段代码,需要更新UI的时候去通知主线程,让主线程来更新。如何做呢?常见的方法,除了前面提到的在UI线程创建Handler,在子线程发送消息到UI线程,通知UI线程更新UI,还有 handler.post(Runnable r)、 view.post(Runnable r)、activity.runOnUIThread(Runnable r)等方法。跟进去看源码,发现其实它们的实现原理都还是一样,最终都是通过Handler发送消息来实现的。下面分别用这几种方法实现一下在子线程更新UI。
public class MainActivity extends Activity implements View.OnClickListener{ Button bt1; Button bt2; Button bt3; Button bt4; Button bt5; TextView tv; Handler mhandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if(msg.what==100){ tv.setText("Handler更新的"); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = findViewById(R.id.tv); bt1 = findViewById(R.id.bt1); bt2 = findViewById(R.id.bt2); bt3 = findViewById(R.id.bt3); bt4 = findViewById(R.id.bt4); bt5 = findViewById(R.id.bt5); bt1.setOnClickListener(this); bt2.setOnClickListener(this); bt3.setOnClickListener(this); bt4.setOnClickListener(this); bt5.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.bt1: new Thread(new Runnable() { @Override public void run() { tv.setText("子线程更新UI:报错!!!"); } }).start(); break; case R.id.bt2: mhandler.sendEmptyMessage(100); break; case R.id.bt3: new Handler().post(new Runnable() { @Override public void run() { tv.setText("Handler post更新UI"); } }); break; case R.id.bt4: tv.post(new Runnable() { @Override public void run() { tv.setText("View post更新UI"); } }); break; case R.id.bt5: runOnUiThread(new Runnable() { @Override public void run() { tv.setText("Activity runOnUiThread更新UI"); } }); break; } } }