Android消息处理机制
1.Message
消息,理解为线程间通讯的数据单元。例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程。
2.MessageQueue
消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
3.Handler
Handler是Message的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。
4.Looper
循环器,扮演MessageQueue和Handler之间桥梁的角色,循环取出MessageQueue里面的Message,并交付给相应的Handler进行处理。
5.线程
UIthread 通常就是mainthread,而Android启动程序时会替它建立一个MessageQueue。
每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。
运行机理:
每个线程都可以并仅可以拥有一个Looper实例,消息队列MessageQueue在Looper的构造函数中被创建并且作为成员变量被保存,也就是说MessageQueue相对于线程也是唯一的。Android应用在启动的时候会默认会为主线程创建一个Looper实例,并借助相关的Handler和Looper里面的MessageQueue完成对Activities、Services、BroadcaseReceivers等的管理。而在子线程中,Looper需要通过显式调用Looper.Prepare()方法进行创建。Prepare方法通过ThreadLocal来保证Looper在线程内的唯一性,如果Looper在线程内已经被创建并且尝试再度创建"Onlyone Looper may be created per thread"异常将被抛出。
Handler在创建的时候可以指定Looper,这样通过Handler的sendMessage()方法发送出去的消息就会添加到指定Looper里面的MessageQueue里面去。在不指定Looper的情况下,Handler绑定的是创建它的线程的Looper。如果这个线程的Looper不存在,程序将抛出"Can'tcreate handler inside thread that has not called Looper.prepare()"。
:UIthread通常就是mainthread,而Android启动程序时会替它建立一个MessageQueue。
二、举例
1.同线程内不同组件间的消息传递
publicclassActivity1extendsActivityimplementsOnClickListener{
Buttonbutton=null;
TextViewtext=null;
@Override
protectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity1);
button=(Button)findViewById(R.id.btn);
button.setOnClickListener(this);
text=(TextView)findViewById(R.id.content);
}
publicvoid onClick(Viewv) {
switch(v.getId()){
caseR.id.btn:
Looperlooper = Looper.myLooper();//取得当前线程里的looper
MyHandlermHandler =newMyHandler(looper);
//构造一个handler使之可与looper通信//buton等组件可以由mHandler将消息传给looper后,再放入messageQueue中,同时mHandler也可以接受来自looper消息
mHandler.removeMessages(0);
StringmsgStr ="主线程不同组件通信:消息来自button";
Messagem = mHandler.obtainMessage(1, 1, 1, msgStr);//构造要传递的消息
mHandler.sendMessage(m);//发送消息:系统会自动调用handleMessage方法来处理消息
break;
}
}
privateclassMyHandlerextendsHandler{
publicMyHandler(Looperlooper){
super(looper);
}
@Override
publicvoid handleMessage(Messagemsg) {//处理消息
text.setText(msg.obj.toString());
}
}
}
2.子线程传递消息给主线程
publicclassActivity2extendsActivityimplementsOnClickListener{
Buttonbutton=null;
TextViewtext=null;
MyHandlermHandler=null;
Threadthread;
@Override
protectedvoidonCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity1);
button=(Button)findViewById(R.id.btn);
button.setOnClickListener(this);
text=(TextView)findViewById(R.id.content);
}
publicvoidonClick(Viewv) {
switch(v.getId()){
caseR.id.btn:
thread=newMyThread();
thread.start();
break;
}
}
privateclass MyHandlerextendsHandler{
publicMyHandler(Looperlooper){
super(looper);
}
@Override
publicvoidhandleMessage(Messagemsg) {//处理消息
text.setText(msg.obj.toString());
}
}
privateclassMyThreadextendsThread{
@Override
publicvoid run(){
LoopercurLooper = Looper.myLooper();
LoopermainLooper = Looper.getMainLooper();
Stringmsg ;
if(curLooper==null){
mHandler=newMyHandler(mainLooper);
msg="curLooper is null";
}else{
mHandler=newMyHandler(curLooper);
msg="Thisis curLooper";
}
mHandler.removeMessages(0);
Messagem =mHandler.obtainMessage(1,1, 1, msg);
mHandler.sendMessage(m);
}
}
}
说明:
Android会自动替主线程建立MessageQueue。在这个子线程里并没有建立MessageQueue。所以,myLooper值为null,而mainLooper则指向主线程里的Looper。于是,执行到:
mHandler= new MyHandler (mainLooper);
此mHandler属于主线程。
mHandler.sendMessage(m);
就将m消息存入到主线程的MessageQueue里。mainLooper看到MessageQueue里有讯息,就会作出处理,于是由主线程执行到mHandler的handleMessage()来处理消息。
Looper.myLooper();获得当前的Looper
Looper.getMainLooper() 获得UI线程的Lopper。
问题:怎么在子线程里面创建消息队列和消息循环呢?
需要在线程中首先调用Looper.prepare()来创建消息队列,然后调用Looper.loop()进入消息循环。如下例所示:
如果一个线程中调用Looper.prepare(),那么系统就会自动的为该线程建立一个消息队列,然后调用 Looper.loop();之后就进入了消息循环,这个之后就可以发消息、取消息、和处理消息。这个如何发送消息和如何处理消息可以再其他的线程中通过Handle来做,但前提是我们的Hanle知道这个子线程的Looper,但是你如果不是在子线程运行 Looper.myLooper(),一般是得不到子线程的looper的。 class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
所以很多人都是这样做的:我直接在子线程中新建handle,然后在子线程中发送消息,这样的话就失去了我们多线程的意义了。
classmyThread extends Thread{
private EHandler mHandler ;
publicvoid run() {
Looper myLooper, mainLooper;
myLooper =Looper.myLooper ();
mainLooper = Looper.getMainLooper();
String obj;
if (myLooper == null ){
mHandler =new EHandler(mainLooper);
obj = "current thread has nolooper!" ;
}
else {
mHandler = newEHandler(myLooper);
obj = "This is from current thread.";
}
mHandler .removeMessages(0);
Message m =mHandler .obtainMessage(1, 1, 1, obj);
mHandler.sendMessage(m);
}
}
这个函数就是我们的消息处理,如何处理,这里完全取决于你,然后通过obtainMessage和sendMessage等来生成和发送消息,removeMessages(0)来清除消息队列。
我们看看GooleMusicApp的源代码。
在MediaPlaybackActivity.java中,我们可以看一下再OnCreate中的有这样的两句:
mAlbumArtWorker= new Worker("album art worker");
mAlbumArtHandler= newAlbumArtHandler(mAlbumArtWorker.getLooper());
很明显这两句,是构建了一个子线程。并且这个子线程还是Looper的子线程,这里很牛逼的使用了mAlbumArtWorker.getLooper()这个函数,因为我们知道,我们能够得到子线程的Looper的途径只有一个:就是在子线程中调用Looper.myLooper(),并且这个函数还要在我们perpare之后调用才能得到正确的Looper,但是他这里用了一个这样的什么东东getLooper,不知道它是如何实现的?
这里有一个大概的思路,我们在子线程的的prepare之后调用myLooper()这个方法,然后保存在一个成员变量中,这个getLooper就返回这个东西,但是这里会碰到多线程的一个很突出的问题,同步。我们在父线程中调用mAlbumArtWorker.getLooper(),但是想要这个返回正确的looper就必须要求我们的子线程运行了prepare,但是这个东西实在子线程运行的,我们如何保证呢?
我们看Google是如何实现的?
privateclass Worker implements Runnable {
privatefinal Object mLock = new Object();
privateLooper mLooper;
/**
*Creates a worker thread with the given name. The thread
*then runs a [email=%7B@link]{@link[/email] android.os.Looper}.
*@param name A name for the new thread
*/
Worker(Stringname) {
Threadt = new Thread(null, this,name);
t.setPriority(Thread.MIN_PRIORITY);
t.start();
synchronized(mLock) {
while(mLooper == null) {
try{
mLock.wait();
}catch (InterruptedException ex) {
}
}
}
}
publicLooper getLooper() {
returnmLooper;
}
publicvoid run() {
synchronized(mLock) {
Looper.prepare();
mLooper= Looper.myLooper();
mLock.notifyAll();
}
Looper.loop();
}
publicvoid quit() {
mLooper.quit();
}
}
我们知道,一个线程类的构造函数是在主线程中完成的,所以在我们的Worker的构造函数中我们创佳一个线程,然后让这个线程运行,这一这个线程的创建是指定一个Runnabl,这里就是我们的Worker本身,在主线程调用t.start();,这后,我们子线程已经创建,并且开始执行work的run方法。然后下面的代码很艺术:
synchronized(mLock) {
while(mLooper == null) {
try{
mLock.wait();
}catch (InterruptedException ex) {
}
}
}
我们开始等待我们的子线程给mLooper赋值,如果不赋值我们就继续等,然后我们的子线程在运行run方法之后,在给mLooper赋值之后,通知worker够着函数中的wait,然后我们的构造函数才能完成,所以我们说:
mAlbumArtWorker= new Worker("album artworker");
这句本身就是阻塞的,它创建了一个子线程,开启了子线程,并且等待子线程给mLooper赋值,赋值完成之后,这个函数才返回,这样才能保证我们的子线程的Looper的获取绝对是正确的,这个构思很有创意。值得借鉴
http://www.cnblogs.com/qingblog/archive/2012/06/27/2566021.html