Android开发之消息处理机制(一)——Handler
/*
* Android开发之消息处理机制(一)——Handler
* 北京Android俱乐部群:167839253
* Created on: 2011-8-29
* Author: blueeagle
* Email: liujiaxiang@gmail.com
*/
对于Android里的消息处理,涉及到Handler,Looper,Message,Message Queue等概念,先捋顺这些概念。
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper:一个线程可以产生一个Looper对象,用来管理MessageQueue,它就像一个消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
下面来先来着重说明Handler类。
Android 里面对于异步消息的处理,提供了一套Handler的实现方案。Handler有很多适宜的应用和微妙之处,使它在和Thread以及Service等一起使用的时候达到很好的效果。
下面先看看手册里对Handler是如何定义的:
A Handler allows you to send and process Message
and Runnable objects associated with a thread'sMessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Handler 允许 你去发送和处理 消息或者是发送处理绑定在线程消息队列中的rannable对象,每一个Handler实例都联系到一个单一的线程和那个线程的消息队列中。当创建一个新的Handler,他将绑定到这个线程或者是创建Hanlder的线程的消息队列。从这一点上来看,它会传递信息和消息队列的rannables并在消息弹出消息队列时执行它们。Handler有两个主要的用途:1是调度消息和runnables在未来执行的时间;2是为不同线程中所进行的活动排队。
云里雾里的,但是好像隐约知道他是什么意思了。继续理解下去:这里就是多线程的问题了,当我们启动一个Android应用程序的时候,Android会首先开启一个主线程,这个主线程的工作主要就是管理界面中的UI控件,进行事件分发,处理消息响应函数等。但是如果处理一个比较耗时的操作时,比如读取本地大文档,读取网络数据等等时,如果依然用主线程的话,就会出现问题,Android系统规定默认5S无反应的话,就会弹出强制关闭对话框。
在这个时候我们就需要另外开一个线程来处理耗时的工作,这与在学习SurfaceView的时候,我们开启了一个线程去处理频繁更新的操作有些类似。但是因为子线程涉及到UI更新,而更新UI只能在主线程中更新(Android主线程不是线程安全的),子线程中操作是危险的。Handler就是用来解决这个复杂问题而出现的。Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据,这个时候,Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递)Message对象(里面包含数据),把这些消息放入主线程队列中,配合主线程进行更新UI。
如下面的例子:
在主Activity中,定义一个TextView,我们来实时的更新这个TextView的内容。
package com.blueeagle; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.TextView; public class SendMessage extends Activity { private TextView textView; private MyHandler myHandler;//定义一个自己的Handle类 private Button button; private MyThread m=new MyThread(); //定义一个自己的线程类 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView=(TextView)findViewById(R.id.text); button=(Button)findViewById(R.id.startButton); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { myHandler=new MyHandler(); new Thread(m).start(); System.out.println("主线程运行ID:"+Thread.currentThread().getId()); } }); }//在对UI进行更新时,执行时所在的线程为主UI线程 class MyHandler extends Handler{//继承Handler类时,必须重写handleMessage方法 public MyHandler(){ } public MyHandler(Looper l){ super(l); } @Override public void handleMessage(Message msg) {//执行接收到的通知,此时执行的顺序是按照队列进行,即先进先出 super.handleMessage(msg); Bundle b=msg.getData(); String textStr1=b.getString("textStr"); SendMessage.this.textView.setText(textStr1);//更改TextView中的值 } }//该线程将会在单独的线程中运行 class MyThread implements Runnable{ int i=1; @Override public void run() { while(i<11){ System.out.println("当前运行线程ID:"+Thread.currentThread().getId()); try { Thread.sleep(1000); } catch(InterruptedException e){ e.printStackTrace(); } Message msg=new Message(); Bundle b=new Bundle(); b.putString("textStr", "线程运行"+i+"次"); i++; msg.setData(b); SendMessage.this.myHandler.sendMessage(msg);//通过sendMessage向Handler发送更新UI的消息 } i=1;//下次启动线程时重新计数。 } } }XML文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <Button android:id="@+id/startButton" android:layout_height="wrap_content" android:text="Button" android:layout_width="wrap_content"></Button> </LinearLayout>小结:
可以看到,屏幕上的TxtView在实时变化,考察线程ID可以得知是由哪一个线程来完成这项工作的。再次点击按钮后,则会触发新的线程去执行实时更新的操作。