在非主线程中更新UI
在非主线程中调用了showMessage方法,结果报错:Can't create handler inside thread that has not called Looper.prepare()
- private void showMessage(String msg) {
- Toast toast = Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT);
- toast.setGravity(Gravity.CENTER, 0, 0);
- toast.show();
- }
原来Android中非主线程不能更新UI,Handler.post()方法可以解决这个问题:于是将showMessage方法稍作修改就可以了:
- private void showMessage(String msg) {
- mSg = msg;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Toast toast = Toast.makeText(getApplicationContext(), mSg, Toast.LENGTH_SHORT);
- toast.setGravity(Gravity.CENTER, 0, 0);
- toast.show();
- }
- });
对下面程序的一些个人看法:
1: package com.example.progressbardemo;
2:
3: import java.security.PublicKey;
4:
5: import android.os.Bundle;
6: import android.os.Handler;
7: import android.app.Activity;
8: import android.view.Menu;
9: import android.widget.ProgressBar;
10:
11: public class MainActivity extends Activity {
12:
13:
14: private ProgressBar mProgressBar;
15: private int mProgressStatus = 0;
16:
17: //创建一个Handler对象
18:
19: private Handler mHandler = new Handler();
20:
21: @Override
22: protected void onCreate(Bundle savedInstanceState) {
23: super.onCreate(savedInstanceState);
24: setContentView(R.layout.activity_main);
25:
26: mProgressBar = (ProgressBar) findViewById(R.id.progressbar2);
27: //设定进度条的最大值,其将为该进度条显示的基数
28: mProgressBar.setMax(1000);
29: //新创建一个线程
30: new Thread( new Runnable() {
31: @Override
32: public void run() {
33: // TODO Auto-generated method stub
34: //循环1000次,不停更新mprogresstatus 的值
35: while(mProgressStatus++<1000)
36: {
37: //将一个runnable对象添加到消息队列中,并且当执行到该对象时执行run方法
38: mHandler.post(new Runnable() { //http协议里面也有post,他的意思是将此线程里面的东西交给另一个线程处理
39:
40: @Override
41: public void run() {
42: // TODO Auto-generated method stub
43: //重新设置进度条当前的值
44: mProgressBar.setProgress(mProgressStatus);
45: }
46: });
47: }
48: }
49: }).start();
50: }
51:
52: @Override
53: public boolean onCreateOptionsMenu(Menu menu) {
54: // Inflate the menu; this adds items to the action bar if it is present.
55: getMenuInflater().inflate(R.menu.main, menu);
56: return true;
57: }
58:
59: }
public final boolean post (Runnable r)
Since: API Level 1
Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.
是不是可以这么理解:mhandle.post(Runnable r) 执行这条语句时,其实主线程UI已经与handle建立了联系,然后Runnable其实就是在主线程中运行的,只不过他是在主线程的消息队列当中运行的。这句话就好比在非主线程中调用handler的sendmessage()方法,然后在主线程中调用handlemessage()方法去处理截获到的消息。
handler.post(Runnable r) 中的Runnable 会在handler 所在的线程执行, 也就是View 所在的UI线程。这个就是所谓的线程间异步通信。也就是说,非主线程中可以调用主线程中的handle类以及方法去处理主线程的消息队列。也就是老师您说的候车室一样,主线程当中的handle可以处理消息队列中的很多消息。然后Looper一般默认存在在主线程中,他里面就是一个消息队列Looper可以控制消息队列的出与进。
new Handler()默认的Looper是主线程的, //每个Looper里面都有一个消息队列
h = new Handler(Looper l)//l为主线程的Looper。此时h为l所附属的handler,h的消息由post放在l的线程的队列中,由l所在的线程处理消息。(每个线程都有一个消息队列,h的消息由post放进I的线程所在的消息队列中去),这些也就是说明了,异步可以更新主线程UI。
参考这篇文章:
http://blog.sina.com.cn/s/blog_70677d110100s2kz.html
Thread + Handler方法:
具体是在需要重绘的地方调用handler的sendMessage方法发送消息,紧接着会os会触发handler中的handlerMessage方法,在handlerMessage方法中再调用view的invalidate或者postInvalidate方法就能实现重绘。
下面是我分别针对invalidate方法,给出view重绘代码,仅供参考:
- class CustomizeView extends WhichView {
- public CustomizeView(Context context) {
- super(context);
- final Handler handler = new Handler();
- new Thread(new Runnable() {
- @Override
- public void run() {
- // delay some minutes you desire.
- handler.post(new Runnable() {
- public void run() {
- concreteUpdateUI();
- invalidate();
- }
- });
- }
- }).start();
- }
- protected void concreteUpdateUI() {
- // Add concrete movement for UI updates.
- // ...
- }
- }
或者这样实现也可以。
- class CustomizeView extends TextView {
- public CustomizeView(Context context) {
- super(context);
- new Thread(new UIUpdateThread()).start();
- }
- class UIUpdateThread implements Runnable {
- final Handler mHandler = new Handler();
- final Runnable mUpdateResults = new Runnable() {
- public void run() {
- concreteUpdateUI();
- invalidate();
- }
- };
- public void run() {
- // delay some minutes you desire.
- mHandler.post(mUpdateResults);
- }
- }
- protected void concreteUpdateUI() {
- // Add concrete movement for UI updates.
- // ...
- }
- }
其他方法:
如果你对于Android的Thread+Handler方式感觉繁琐,不妨试试Activity提供的另外一种简单的方法runOnUiThread,runOnUiThread可以帮助你在线程中执行UI更新操作,我们只需要在线程中写上类似
MyActivity .this.runOnUiThread(new Runnable() {
@Override
public void run() {
// refresh ui 的操作代码
}
});
这里需要注意的是runOnUiThread是Activity中的方法,在线程中我们需要告诉系统是哪个activity调用,所以前面显示的指明了activity。