线程

Android消息处理机制

1.Message

消息,理解为线程间通讯的数据单元。例如后台线程在处数据完毕后需要更新UI,则可发送一条包含更新信息的MessageUI线程。

2.MessageQueue

消息队列,用来存放通过Handler发布的消息,按照先进先出执行。

3.Handler

HandlerMessage的主要处理者,负责将Message添加到消息队列以及对消息队列中的Message进行处理。

4.Looper

循环器,扮演MessageQueueHandler之间桥梁的角色,循环取出MessageQueue里面的Message,并交付给相应的Handler进行处理。



5.线程

UIthread 通常就是mainthread,而Android启动程序时会替它建立一个MessageQueue

每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。


运行机理:

每个线程都可以并仅可以拥有一个Looper实例,消息队列MessageQueueLooper的构造函数中被创建并且作为成员变量被保存,也就是说MessageQueue相对于线程也是唯一的。Android应用在启动的时候会默认会为主线程创建一个Looper实例,并借助相关的HandlerLooper里面的MessageQueue完成对ActivitiesServicesBroadcaseReceivers等的管理。而在子线程中,Looper需要通过显式调用Looper.Prepare()方法进行创建。Prepare方法通过ThreadLocal来保证Looper在线程内的唯一性,如果Looper在线程内已经被创建并且尝试再度创建"Onlyone Looper may be created per thread"异常将被抛出。



Handler在创建的时候可以指定Looper,这样通过HandlersendMessage()方法发送出去的消息就会添加到指定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里有讯息,就会作出处理,于是由主线程执行到mHandlerhandleMessage()来处理消息。



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);
}
}

这个函数就是我们的消息处理,如何处理,这里完全取决于你,然后通过obtainMessagesendMessage等来生成和发送消息,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();,这后,我们子线程已经创建,并且开始执行workrun方法。然后下面的代码很艺术:
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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值