与多线程结合使用的消息处理类Handler、Message
1. 消息处理类——Handler
消息处理类(Handler)允许发送和处理Message或Runnable对象到其所在线程的MessageQueue中。Handerl有以下两个主要作用:
- 将Message或Runnable应用post()方法或sendMessage()方法发送到MessageQueue中,在发送时可以指定延迟时间、发送时间或者要携带的Bundle数据。当MessageQueue循环到该Message时,调用相应的Handler对象的handlerMessage()方法对其进行处理。
- 在子线程中与主线程进行通信,也就是在工作线程中与UI线程进行通信。
说明:在一个线程中,只能有一个Looper和MessageQueue,但是,可以有多个Handler,而且这些Handler可以共享同一个Looper和MessageQueue。
Handler类提供的常用方法
方法 | 描述 |
handleMessage(Message msg) | 处理消息的方法。通常重写该方法来处理消息,在发送消息时,该方法会自动回调。 |
post(Runnable r) | 立即发送Runnable对象,该Runnable对象最后将被封装成Message对象 |
postAtTime(Runnable r, long uptimeMillis) | 定时发送Runnable对象,该Runnable对象最后将被封装成Message对象 |
postDelayed(Runnable r, long delayMillis) |
延迟多少毫秒发送Runnable对象,该Runnable对象最后将被封装成Message对象 |
sendEmptyMessage(int what) | 发送空消息 |
sendMessage(Message msg) | 立即发送消息 |
sendMessageAtTime(Message msg, long uptimeMillis) | 定时发送消息 |
sendMessageDelayed(Message msg, long delayMillis) | 延迟多少毫秒发送消息 |
2. 消息类——Message
消息类(Message)被存放在MessageQueue中,一个MessageQueue中可以包含多个Message对象。每个Message对象可以通过Message.obtain()方法或者Handler.obtainMessage()方法获得。一个Message对象具有下表所示的5个属性。
Message类的属性
属性 | 类型 | 描述 |
arg1 | int | 用来存放整型数据 |
arg2 | int | 用来存放整型数据 |
obj | Object | 用来存放发送给接收器的Object类型的任意对象 |
replyTo | Messenger | 用来指定此Message发送到何处的可选Messager对象 |
what | int | 用于指定用户自定义的消息代码,这样接收者可以了解这个消息的信息 |
说明:使用Message类的属性可以携带int型的数据,如果要携带其他类型的数据,可以先将要携带的数据保存到Bundle对象中,然后通过Message类的setDate()方法将其添加到Message中。
综上所述,Message类的使用方法比较简单,只要在使用它时,注意以下3点即可:
- 尽管Message有public的默认构造方法,但是通常情况下,需要使用Message.obtain()方法或Handler.obtainMessage()方法来从消息池中获得空消息对象,以节省资源。
- 如果一个Message只需要携带简单的int型信息,应优先使用Message.arg1和Message.arg2属性来传递信息,这比用Bundle更省内存。
- 尽可能使用Message.what来标识信息,以便用不同的方式处理Message。
3. 用Handler和Message实现一个每隔1秒更新时间的程序
1. 布局文件activity_main.xml内容如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.tiaoshi.MainActivity" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="每秒变化的文本" /> <Button android:id= "@+id/btn1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/start" /> <Button android:id="@+id/btn2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/stop" /> </LinearLayout>
2. MainActivity内容如下
package com.example.tiaoshi; import java.util.Date; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends ActionBarActivity implements Runnable { private Button startBtn = null; private Button stopBtn = null; private Thread thread = null; private Handler handler = null; private TextView text = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); this.text = (TextView) this.findViewById(R.id.text); //处理消息 handler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what==0x101){ text.setText("当前时间为:"+new Date()); } super.handleMessage(msg); } }; this.startBtn = (Button) this.findViewById(R.id.btn1); startBtn.setOnClickListener(new OnClickListener(){ @Override public void onClick(View arg0) { thread = new Thread(MainActivity.this); thread.start(); text.setText("当前时间为:"+new Date()); } }); this.stopBtn = (Button) this.findViewById(R.id.btn2); stopBtn.setOnClickListener(new OnClickListener(){ @Override public void onClick(View arg0) { if(thread!=null){ thread.interrupt(); thread = null; } Log.i("提示", "中断线程"); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override public void run() { try { while(!Thread.currentThread().isInterrupted()){ Message m = handler.obtainMessage(); //获取一个空的message m.what = 0x101; //设置消息标识 handler.sendMessage(m); //发送消息 Thread.sleep(1000); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override protected void onDestroy() { if(thread!=null){ thread.interrupt(); thread = null; } super.onDestroy(); } }
4. 创建Handler对象发送并处理消息
在Eclipse中创建Android项目,创建一个继承了Thread类的LooperThread,并在重写的run()方法中,创建一个Handler对象并处理消息。
在Android中,一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue(消息队列)。MessageQueue用于存放Message(消息),在MessageQueue中,存放的消息按照FIFO(先进先出)原则执行,由于MessageQueue被封装到Looper里面了,所以这里不对MessageQueue进行过多介绍。
Looper对象用来为一个线程开启一个消息循环,用来操作MessageQueue。默认情况下Android中新创建的线程是没有开启消息循环的。但是主线程除外,系统自动为主线程创建Looper对象,开启消息循环。所以,当我们在主线程中,应用下面的代码创建Handler对象时,就不会出错,而如果在创建的非主线程中,应用下面的代码创建Handler对象时,将产生异常信息。
Handler handler = new Handler();
如果想要在非主线程中,创建Handler对象,首先需要使用Looper类的prepare()方法来初始化一个Looper对象,然后创建这个Handler对象,再使用Looper类的loop()方法启动Looper,从消息队列里获取和处理消息。
Looper类提供的常用方法如下表所示。
方法 | 描述 |
prepare() | 用于初始化Looper |
loop() | 调用loop()方法后,Looper线程就开始真正工作了,它会从消息队列里获取消息和处理消息 |
myLooper() | 可以获取当前线程的Looper对象 |
getThread() | 用于获取Looper对象所属的线程 |
quit() | 用于结束Looper循环 |
注意:写在Looper.loop()之后的代码不会被执行,这个函数内部是一个循环,当调用Handler.getLooper().quit()方法后,loop()方法才会终止,其后面的代码才能得以执行。
1. 创建一个继承了Thread类的LooperThread,并在重写的run()方法中,创建一个Handler对象发送并处理消息,关键代码如下:
package com.example.tiaoshi; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; public class LooperThread extends Thread { public Handler handler; //声明一个Handler对象 @Override public void run() { super.run(); Looper.prepare(); //初始化Looper对象 //实例化一个Handler对象 handler = new Handler(){ @Override public void handleMessage(Message msg) { Log.i("Looper", String.valueOf(msg.what)); } }; //获取一个消息 Message m = handler.obtainMessage(); //设置Message的what属性的值 m.what = 0x11; // 发送消息 handler.sendMessage(m); //启动Looper Looper.loop(); } }
2. 在MainActivity的onCreate()方法中,创建一个LooperThread线程,并开启该线程,关键代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); LooperThread thread = new LooperThread(); //创建一个线程 thread.start(); //开启线程 }