本文讲解Android中Handler的使用,要讲清楚Handler,就需要附带引入几个概念——Looper、MessageQueue、Message。
一、为什么要用Handler
大家都知道,每一个Android应用启动时,都会启动一个UI线程(也叫主线程)。只有在UI线程里才可以对控件、布局进行获取或设置。
另外,在Android的设计思想中,为了确保流畅的操作体验。一些耗时的任务不能够在UI线程中运行,需要用户单独开一个子线程,这些耗时的操作在子线程中执行。
那么问题来了——子线程执行过程中或者执行结束后,如果需要修改UI,该怎么做呢?(例如:下载完成后,需要将TextView修改为“下载完毕”)
这个时候,我们就需要用到Handler消息传递机制了。
二、什么是Handler
Handler是Android SDK中处理异步消息的核心类。
Handler的作用是让子线程通过与UI线程通信来更新UI界面(也就是说解决子线程不能更新UI的问题)。
在 主线程 中使用Handler很简单,举个例子。
private Handler tvHandler = new Handler(){
public void handleMessage(Message msg) {
textview.setText("下载完成");
};
};
三、什么是Message、MessageQueue
刚才说到子线程需要与UI线程进行通信,所以才有了Handler消息传递机制,那么既然要通信,肯定要有个信使,Message就是担当了信使的作用。
通信过程中可能会有多条消息发送出去,所以就有了MessageQueue,即消息队列。
消息队列里面有很多条消息,那么先处理谁后处理谁呢?这就需要用到Looper。
四、什么是Looper
Looper就是负责管理MessageQueue的一个工具,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。
每个线程只有一个Looper.
五、为什么有的地方不需要使用Looper
也许大家会有个疑问——我也用到了Handler,但是我没有用Looper啊,这是为什么?
在UI线程中,系统已经初始化了一个Looper对象,因此不需要我们手动创建,直接使用Handler就可以了。
但是,假如你在子线程中使用Handler,则必须手动创建一个Looper对象,并启动它。
Looper.prepare();//创建Looper对象
//...
Looper.loop();//启动Looper
所以,在子线程中使用Handler的步骤如下:
- 调用Looper.prepare()方法,创建Looper对象。此时系统会自动为其创建配套的MessageQueue。
- 有了Looper之后,创建Handler,重写handlerMessage()方法。
- 调用Looper.loop()方法启动Looper。
六、实例演示
1、在主线程中使用Handler模拟下载
public class DownLoadActivity extends Activity {
private TextView countTV;
private TextView finishTV;
private int count;
//在主线程中创建Handler实例
private Handler handler = new Handler(){
//handlerMessage方法负责接收子线程传来的消息
public void handleMessage(android.os.Message msg) {
if(msg.what==1){
countTV.setText(count+"%");
}else if(msg.what==2){
finishTV.setText("下载完成");
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_load);
bindID();
//启动子线程
new Thread(new Runnable() {
@Override
public void run() {
while(count<100){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通过sendEmptyMessage方法发送消息
handler.sendEmptyMessage(1);
count+=10;
}
//下载完成后,再次发送消息
handler.sendEmptyMessage(2);
}
}).start();
}
private void bindID() {
countTV = (TextView) findViewById(R.id.downloadCount);
finishTV = (TextView) findViewById(R.id.finishTV);
}
}
2、在主线程、子线程中均使用Handler模拟质数的计算
public class CalculateActivity extends Activity {
private EditText editText;
private Button btn;
private TextView textview;
private ArrayList<Integer> nums ;
private int upperNum;
CalThread cThread;//子线程
//主线程中创建Handler实例,更新UI
private Handler tvHandler = new Handler(){
public void handleMessage(Message msg) {
textview.setText(nums.toString());
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calculate);
bindID();
//实例化子线程
cThread = new CalThread();
//启动线程
cThread.start();
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
upperNum = Integer.parseInt(editText.getText().toString());
//获取消息对象,使用obtainMessage而非new Message(),这样能减小内存开支
Message msg = cThread.calHandler.obtainMessage();
msg.arg1 = 1;
msg.arg2 = 2;
msg.what = 3;
//发送消息至子线程的Handler
cThread.calHandler.sendMessage(msg);
}
});
}
private void bindID() {
editText = (EditText) findViewById(R.id.num_edit);
btn = (Button) findViewById(R.id.btn);
textview = (TextView) findViewById(R.id.num_text);
}
/**
* 计算质数的方法
*/
private void calc() {
nums = new ArrayList<Integer>();
A: for (int i = 1; i <= upperNum; i++) {
for (int j = 2; j < Math.sqrt(i); j++) {
if (i != 2 && i % j == 0) {
continue A;
}
}
nums.add(i);
}
}
class CalThread extends Thread {
Handler calHandler;
@Override
public void run() {
Looper.prepare();
System.out.println("run..........");
calHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
System.out.println("handleMessage............."+msg);
System.out.println(msg.arg1+"*"+msg.arg2+"*"+msg.what+"*"+msg.getWhen()+"*"+SystemClock.uptimeMillis());
//计算质数是个耗时操作,所以要在子线程中执行
calc();
Toast.makeText(CalculateActivity.this, nums.toString(), Toast.LENGTH_LONG).show();
//计算质数完成后,发送消息给主线程Handler,要求其更新UI
tvHandler.sendEmptyMessage(1);
}
};
Looper.loop();
}
}
}