Handler类的作用主要有两种:
1.在新启动的线程中发送消息。
2.在主线程(UI线程)中获取,处理消息。
注:主线程已经封装有Loop的消息队列处理机制,无需再创建。
Handler类包含如下方法用于消息发送,处理:
1.void handleMessage(Message msg):处理消息的方法。
2.final boolean hasMessages(int what):检查消息队列是否包含what的值。
3.final boolean hasMessages(int what, Object object):检查消息队列是否包含what的值且object为指定对象。
4.Message obtainMessage():获取消息。
5.sendEmptyMessage(int what):发送空消息。
6.final boolean sendemptyMessageDelayed(int what, long delayMillis):指定多少毫秒之后发送空消息。
7.final boolean sendMessage(Message msg):立即发送消息。
8.final boolean sendMessageDelayed(Message msg, long delayMillis):指定多少毫秒之后发送空消息。
demo:自动播放动画
下面代码实现是创建一个新线程来隔一定时间之后周期性的修改ImageView所显示的图片,实现一个动画效果。
-
public
class HandlerActivity extends Activity {
-
//定义周期性显示的图片ID
-
int[] imageIds =
new
int[]
-
{
-
R.drawable.1,
-
R.drawable.2,
-
R.drawable.3,
-
R.drawable.4,
-
R.drawable.5
-
};
-
int currentImageId =
0;
-
@Override
-
public void onCreate(Bundle savedInstanceState)
-
{
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main_activity);
-
final ImageView show = (ImageView) findViewById(R.id.main_bt);
-
-
final Handler myhHandler =
new Handler()
-
{
-
@Override
-
public void handleMessage(Message msg)
-
{
-
//如果该消息是本程序所发送的
-
if (msg.what ==
0x12345) {
-
//动态的修改所显示的图片
-
show.setImageResource(imageIds[currentImageId++]%imageIds.length);
-
}
-
}
-
};
-
//定义一个计时器,让该计时器周期性的执行指定任务
-
new Timer().schedule(
new TimerTask() {
//TimerTask对象的本质是启动一个新线程
-
-
@Override
-
public void run() {
-
// TODO Auto-generated method stub
-
//发送空消息
-
myhHandler.sendEmptyMessage(
0x12345);
-
}
-
},
0,
1200);
-
}
-
}
说明:当Timertask新线程发送消息时,位于主线程的handleMessage(Message msg)方法自动被回调,动态的修改ImagView组件的属性。效果:由新线程来周期性的修改ImageView的属性,从而实现动画效果。
Handler,Looper,MessageQueue的工作原理:
Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断的从MessageQueue中取出消息,并将消息分发给Handler处理。
MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。
Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
注:
- 在主线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息,处理消息。
- 在自己启动的子线程,必须自己创建一个Looper对象,并启动它(分别调用prepare()和loop()方法),下面讲解这两个方法:
-
public static final void prepare(){
-
if (sThreadLocal.get() !=
null) {
-
throw
new RuntimeException(
"Only one Looper may be created per thread");
-
}
-
sThreadLocal.set(
new Looper());
-
}
说明:prepare方法保证线程最多只有一个Looper对象。
-
public void loop(){
-
for (; ;) {
-
Message msg = queue.next();
//获取消息队列的下一个消息,如果没有消息,将会阻塞
-
if (msg ==
null) {
//如果消息为null,表明消息队列正在退出
-
return;
-
}
-
Printer logging = me.mLogging;
-
if (logging !=
null) {
-
logging.println(
"");
-
}
-
msg.target.dispatchMessage(msg);
-
if (logging !=
null) {
-
logging.println(
"");
-
}
-
//使用final修饰该标识符,保证在分发消息的过程中线程标识符不会被修改
-
final
long newIdent = Binder.clearCallingIdentity();
-
if (iden != newIdent) {
-
logging.println(
"");
-
}
-
msg.recycle();
-
}
-
}
说明:loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。
在非主线程中使用Handler的步骤如下:
- 调用Looper的prepare()方法为当前线程创建Looper对象(创建Looper对象时,它的构造器会自动创建与之配套的MessageQueue)。
- 有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。
- 调用Looper的loop()方法启动Looper。
Demo:使用新线程计算质数
该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将该上限数值发送到新启动的线程,让该线程来计算该范围内的所有质数(之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要响应用户动作,如果在UI线程中执行一个耗时操作,将会导致UI线程被阻塞,引起ANR异常)。
-
/*
-
* 本实例在线程中创建一个Handler对象,然后UI线程的事件处理方法通过Handler向新线程发送消息。
-
*/
-
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_activity);
-
etNum = (EditText)findViewById(R.id.etNum);
-
CalThread 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类的作用主要有两种:
1.在新启动的线程中发送消息。
2.在主线程(UI线程)中获取,处理消息。
注:主线程已经封装有Loop的消息队列处理机制,无需再创建。
Handler类包含如下方法用于消息发送,处理:
1.void handleMessage(Message msg):处理消息的方法。
2.final boolean hasMessages(int what):检查消息队列是否包含what的值。
3.final boolean hasMessages(int what, Object object):检查消息队列是否包含what的值且object为指定对象。
4.Message obtainMessage():获取消息。
5.sendEmptyMessage(int what):发送空消息。
6.final boolean sendemptyMessageDelayed(int what, long delayMillis):指定多少毫秒之后发送空消息。
7.final boolean sendMessage(Message msg):立即发送消息。
8.final boolean sendMessageDelayed(Message msg, long delayMillis):指定多少毫秒之后发送空消息。
demo:自动播放动画
下面代码实现是创建一个新线程来隔一定时间之后周期性的修改ImageView所显示的图片,实现一个动画效果。
-
public
class HandlerActivity extends Activity {
-
//定义周期性显示的图片ID
-
int[] imageIds =
new
int[]
-
{
-
R.drawable.1,
-
R.drawable.2,
-
R.drawable.3,
-
R.drawable.4,
-
R.drawable.5
-
};
-
int currentImageId =
0;
-
@Override
-
public void onCreate(Bundle savedInstanceState)
-
{
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.main_activity);
-
final ImageView show = (ImageView) findViewById(R.id.main_bt);
-
-
final Handler myhHandler =
new Handler()
-
{
-
@Override
-
public void handleMessage(Message msg)
-
{
-
//如果该消息是本程序所发送的
-
if (msg.what ==
0x12345) {
-
//动态的修改所显示的图片
-
show.setImageResource(imageIds[currentImageId++]%imageIds.length);
-
}
-
}
-
};
-
//定义一个计时器,让该计时器周期性的执行指定任务
-
new Timer().schedule(
new TimerTask() {
//TimerTask对象的本质是启动一个新线程
-
-
@Override
-
public void run() {
-
// TODO Auto-generated method stub
-
//发送空消息
-
myhHandler.sendEmptyMessage(
0x12345);
-
}
-
},
0,
1200);
-
}
-
}
说明:当Timertask新线程发送消息时,位于主线程的handleMessage(Message msg)方法自动被回调,动态的修改ImagView组件的属性。效果:由新线程来周期性的修改ImageView的属性,从而实现动画效果。
Handler,Looper,MessageQueue的工作原理:
Looper:每个线程只有一个Looper,它负责管理MessageQueue,会不断的从MessageQueue中取出消息,并将消息分发给Handler处理。
MessageQueue:由Looper负责管理。它采用先进先出的方式来管理Message。
Handler:它能把消息发送给Looper管理的MessageQueue,并负责处理Looper分给它的消息。
注:
- 在主线程中,系统已经初始化了一个Looper对象,因此程序直接创建Handler即可,然后就可通过Handler来发送消息,处理消息。
- 在自己启动的子线程,必须自己创建一个Looper对象,并启动它(分别调用prepare()和loop()方法),下面讲解这两个方法:
-
public static final void prepare(){
-
if (sThreadLocal.get() !=
null) {
-
throw
new RuntimeException(
"Only one Looper may be created per thread");
-
}
-
sThreadLocal.set(
new Looper());
-
}
说明:prepare方法保证线程最多只有一个Looper对象。
-
public void loop(){
-
for (; ;) {
-
Message msg = queue.next();
//获取消息队列的下一个消息,如果没有消息,将会阻塞
-
if (msg ==
null) {
//如果消息为null,表明消息队列正在退出
-
return;
-
}
-
Printer logging = me.mLogging;
-
if (logging !=
null) {
-
logging.println(
"");
-
}
-
msg.target.dispatchMessage(msg);
-
if (logging !=
null) {
-
logging.println(
"");
-
}
-
//使用final修饰该标识符,保证在分发消息的过程中线程标识符不会被修改
-
final
long newIdent = Binder.clearCallingIdentity();
-
if (iden != newIdent) {
-
logging.println(
"");
-
}
-
msg.recycle();
-
}
-
}
说明:loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理。
在非主线程中使用Handler的步骤如下:
- 调用Looper的prepare()方法为当前线程创建Looper对象(创建Looper对象时,它的构造器会自动创建与之配套的MessageQueue)。
- 有了Looper之后,创建Handler子类的实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息。
- 调用Looper的loop()方法启动Looper。
Demo:使用新线程计算质数
该实例允许用户输入一个数值上限,当用户单击“计算”按钮时,该应用会将该上限数值发送到新启动的线程,让该线程来计算该范围内的所有质数(之所以不直接在UI线程中计算该范围的所有质数,是因为UI线程需要响应用户动作,如果在UI线程中执行一个耗时操作,将会导致UI线程被阻塞,引起ANR异常)。
-
/*
-
* 本实例在线程中创建一个Handler对象,然后UI线程的事件处理方法通过Handler向新线程发送消息。
-
*/
-
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_activity);
-
etNum = (EditText)findViewById(R.id.etNum);
-
CalThread 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);
-
-
}