Handler的作用:
主要接受子线程发送的数据, 并用此数据配合主线程更新UI. Android主线程 (也就是UI线程) 操作5秒钟还没有完成的话,界面会出现假死现象,会收到Android系统的一个错误提示 "强制关闭". 这个时候我们需要把这些耗时的操作,放在一个子线程中
Android不允许子线程更新主线程维护的UI组件,这样对主线程是不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的.
Handler为android系统的线程通信工具,承担着主线程与分线程,分线程之间的通信功能,经常用到的有post(),
sendMessage() 方法,前者是将一个线程加入线程队列,后者是发送一个消息到消息队列中
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外
个部分逐个的在消息队列中将消息取出,然后对消息进行处理,就是发送消息和接收消息不是同步的处理。这种机制通
常用来处理相对耗时比较长的操作。
Handler的特点:
Handler绑定的有两个队列,一个为消息队列,另一个为线程队列。Handler可以通过这两个队列来分别:
发送、接受、处理消息–消息队列;
启动、结束、休眠线程–线程队列;
Android OS中,在主线程中创建新的线程,这些新的线程都通过Handler与主线程进行通信。通信通过新线程调用
Handler的post()方法和sendMessage()方法实现,分别对应功能:
post() 将一个线程加入线程队列;
sendMessage() 发送一个消息对象到消息队列;
post()方法和sendMessage()还有一些变体,比如postDelayed()、postAtTime()、 sendMessageDelayed ()分别用来延
迟发送、定时发送;
SendMessage()将Message压入消息队列,当消息队列中有消息,HandleMessage()回调函数能自动回调.
Post()将线程对象压入线程队列,队列中的线程会依次弹出运行.
Handler的使用方法:
Handler 中分发消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上 post 类方法允许你排列一个 Runnable 对象到主线程队列中
sendMessage 类方法 , 允许你安排一个带数据的 Message 对象到队列中
与Handler一起工作的组件:
Message:Handler接收和处理的消息对象
Looper:每个线程只能拥有一个Looper。它的loop方法负责读取MessageQueue中的消息,读到消息之后就把消息交给发送该消息的Handler进行处理
MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时会在它的构造器中创建MessageQueue对象
Handler:它有两个作用--发送消息和处理消息,程序使用Handler发送消息,被Handler发送的消息必须被送到指定的MessageQueue。也就是说,如果希望Handler正常工作,必须在当前线程中有一个MessageQueue。不过MessageQueue是由Looper负责管理,也就是说,如果希望Handler正常工作,必须在当前线程中有一个Looper对象。
归纳起来,Looper,MessageQueue,Handler各自的作用如下:
Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断地从MessageQueue中取出消息,并将消息分给对应的Handler处理。
MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。
Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
为了保证当前线程中有Looper对象可以分如下两种情况处理,
主UI线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息,处理消息。(如下第一个程序)
程序员自己启动的子线程,程序员必须自己创建一个Looper对象,并启动它。创建Looper对象调用它的prepare()方法即可。(如下第二个程序)
分别对应如下两个例子程序。
public class TestBarHandler extends Activity {
/** Called when the activity is first created. */
// 声明控件变量
ProgressBar bar = null;
Button startButton = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 根据控件的ID得到代表控件的对象,并为按钮设置监听器
bar = (ProgressBar) findViewById(R.id.bar);
startButton = (Button) findViewById(R.id.startButton);
startButton.setOnClickListener(new ButtonListener());
}
// 当点击startButton按钮时,就会执行ButtonListener的onClick方法
class ButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
bar.setVisibility(View.VISIBLE);
updateBarHandler.post(updateThread);
}
}
// 使用匿名内部类来复写Handler当中的handleMessage方法
Handler updateBarHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
bar.setProgress(msg.arg1);
updateBarHandler.post(updateThread);
}
};
// 线程类,该类使用匿名内部类的方式进行声明
Runnable updateThread = new Runnable() {
int i = 0;
@Override
public void run() {
System.out.println("Begin Thread");
i = i + 10;
// 得到一个消息对象,Message类是有Android操作系统提供
Message msg = updateBarHandler.obtainMessage();
// 将msg对象的arg1参数的值设置为i,用arg1和arg2这两个成员变量传递消息,优点是系统性能消耗较少
msg.arg1 = i;
try {
// 设置当前显示睡眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 将msg对象加入到消息队列当中
updateBarHandler.sendMessage(msg);
if (i == 100) {
// 如果当i的值为100时,就将线程对象从handler当中移除
updateBarHandler.removeCallbacks(updateThread);
}
}
};
}
在线程中使用Handler的步骤如下:
调用Looper的prepare()方法为当前线程创建Looper对象,创建Looper对象时,它的构造器会创建与之配套的MessageQueue。
有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自其他线程的消息。
调用Looper的Looper()方法启动Looper。
public class CalPrime extends Activity {
static final String UPPER_NUM = "upper";
EditText etNum;
CalThread calThread;
// 定义一个线程类
class CalThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
// 定义处理消息的方法
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x123) {
int upper = msg.getData().getInt(UPPER_NUM);
List<Integer> nums = new ArrayList<Integer>();
// 计算从2开始、到upper的所有质数
outer: for (int i = 2; i <= upper; i++) {
// 用i处于从2开始、到i的平方根的所有数
for (int j = 2; j <= Math.sqrt(i); j++) {
// 如果可以整除,表明这个数不是质数
if (i != 2 && i % j == 0) {
continue outer;
}
}
nums.add(i);
}
// 使用Toast显示统计出来的所有质数
Toast.makeText(CalPrime.this, nums.toString(),
Toast.LENGTH_LONG).show();
}
}
};
Looper.loop();
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
etNum = (EditText) findViewById(R.id.etNum);
calThread = new CalThread();
// 启动新线程
calThread.start();
}
// 为按钮的点击事件提供事件处理函数
public void cal(View source) {
// 创建消息
Message msg = new Message();
msg.what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt(UPPER_NUM, Integer.parseInt(etNum.getText().toString()));
msg.setData(bundle);
// 向新线程中的Handler发送消息
calThread.mHandler.sendMessage(msg);
}
}
如果想在主线程类中回调另一个线程类中的Handler(发送消息),只需将另一个线程类中的Handler设为public
如果想在另一个线程类中向主线程类中的Handler发送消息,只需将主线程类中的Handler对象用参数传递给另一个线程中的构造方法
例子程序如下:
主线程类:
package org.crazyit.net;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MultiThreadClient extends Activity {
// 定义界面上的两个文本框
EditText input;
TextView show;
// 定义界面上的一个按钮
Button send;
Handler handler;
// 定义与服务器通信的子线程
ClientThread clientThread;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
input = (EditText) findViewById(R.id.input);
send = (Button) findViewById(R.id.send);
show = (TextView) findViewById(R.id.show);
handler = new Handler() // ①
{
@Override
public void handleMessage(Message msg) {
// 如果消息来自于子线程
if (msg.what == 0x123) {
// 将读取的内容追加显示在文本框中
show.append("\n" + msg.obj.toString());
}
}
};
clientThread = new ClientThread(handler);
// 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据
new Thread(clientThread).start(); // ①
send.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
// 当用户按下发送按钮后,将用户输入的数据封装成Message,
// 然后发送给子线程的Handler
Message msg = new Message();
msg.what = 0x345;
msg.obj = input.getText().toString();
clientThread.revHandler.sendMessage(msg);
// 清空input文本框
input.setText("");
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
另一个线程类:/**
*
*/
package org.crazyit.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
public class ClientThread implements Runnable {
private Socket s;
// 定义向UI线程发送消息的Handler对象
private Handler handler;
// 定义接收UI线程的消息的Handler对象
public Handler revHandler;
// 该线程所处理的Socket所对应的输入流
BufferedReader br = null;
OutputStream os = null;
public ClientThread(Handler handler) {
this.handler = handler;
}
public void run() {
try {
s = new Socket("192.168.1.2", 30000);
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
os = s.getOutputStream();
// 启动一条子线程来读取服务器响应的数据
new Thread() {
@Override
public void run() {
String content = null;
// 不断读取Socket输入流中的内容。
try {
while ((content = br.readLine()) != null) {
// 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据
Message msg = new Message();
msg.what = 0x123;
msg.obj = content;
handler.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
// 为当前线程初始化Looper
Looper.prepare();
// 创建revHandler对象
revHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 接收到UI线程中用户输入的数据
if (msg.what == 0x345) {
// 将用户在文本框内输入的内容写入网络
try {
os.write((msg.obj.toString() + "\r\n")
.getBytes("utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
// 启动Looper
Looper.loop();
} catch (SocketTimeoutException e1) {
System.out.println("网络连接超时!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}