Android学习之Handler, Looper,MessageQueue

  1. 云栖社区>
  2. 博客>
  3. 正文

Android学习之Handler, Looper,MessageQueue

一生有你llx 2014-11-27 11:29:57 浏览385
展开阅读全文
在Andorid中其他的线程不能直接的更新UI控件,在获取数据相对较费时间的处理中通常在其他 线程中完成,在该线程加载成功之后再由主线程执行对应的显示操作,这样就减少了对用户体验的影响。

消息循环(Message loop)包含一个线程和一个looper,Looper通常是用来管理当前Thread的消息队列,因此如果是一个消息循环就必然需要一个线程和一个Looper。主线程是一个消息循环,因此包含一个Looper对象,主线程的所有执行操作都是通过Looper管理的(获取对应的消息,并执行对应的操作)。而创建的Backgroun 线程通常不会包含Looper对象。同样可以通过HandlerTask来重载一个包含了Looper的类。

HandlerTask的实现中有关于Looper的处理过程,代码如下所示:
Message包含如下的一些域:
what:主要用于定义消息的类型。
obj: 主要用于保存数据
target:是指处理这个message的Handler对象。
Message的创建和处理都是由一个handler完成,因此通常target是自动填充。

同时消息只能在某个具体的Looper上消耗,因此每个Handler都会绑定一个Looper。但是多个Handler可以绑定同一个Looper,这也是在主线程中能够创建新的Handler的原因。因此各个Handler的消息是共存在同一个消息循环中。

具体的关系图如下所示:

Handler.obtainMessage()用于获取Message的实例,此时实际上就完成了Handler与Message的绑定关系。

在获取到Message以后就可以调用sendtoTarget()发送Message到Handler。而Handler接收到消息以后将对应的消息填充到Looper的消息队列循环的尾部。

HandlerThread.onLooperPrepared()在looper第一次检查队列的过程中调用,因此可以再该接口中创建Handler的实例。通常Handler的实例还需要创建一个重载handleMessage(),在该函数中实现对具体消息的处理。

Handler可以再其他的线程中使用,但是message的处理都是在创建Handler的线程中完成,也就是消耗在对应的Looper中。

在主线程中可以传递一个handler到其他的线程中,其他的线程通过该handler就可以完成将数据更新到主线程或者发生数据到主线程的功能。关于对该数据或者handler的消息的处理可以采用如下的两种方式实现:
(1)可以在主线程中实现handler的handleMessgae,
(2)也可以其他线程中通过传递的handler调用post接口。

方式一:
      主线程中传递参数到其他的线程中,其他的线程保存对应的handler实体。同时主线程中需要实现当前handler的handleMessage接口。在其他线程完成相关的处理或者需要更新时,通过保存到handler实体发送数据到主线程中sendMessage()。这种实现方式要求主线程实现不同handler的handlerMessage函数。

基本的代码可以采用如下的方式:
主线程:
...
Thread background = new BackgroundThread(mHandler );
background .start();
...
mHandler = new Handler() {
   @Override
   public void handleMessage(Message msg) {
    if (msg.what == MESSAGE_DOWNLOAD) {
     Token token = (Token)msg.obj;
     Log.i(TAG, "Got a request for url: " + requestMap.get(token));
     handleRequest(token);
    }
   }
  };

而在BackgroundThread中的处理则比较简单:
class BackgroundThread extends Thread {
    Handler mHandler;
    ...
    Message message = mHandler.Handler.obtainMessage();
    ...
    /* 填充消息,然后发送消息 */
    mHandler.sendMessage(message);
}

方式二:
        在其他线程的构造函数中获取一个主线程的handler,在线程类中保存对应的handler实例,并在线程类中提供一个接口,该接口的目的是让主线程注册对应的接口,并在该接口中完成数据的处理操作,该接口通过设置lister的方式实现。主线程在创建该线程的过程中直接传递一个handler,并设置对应的监听lister,通常是完成UI控制相关的操作。待线程中有数据发送到主线程的时候,可以调用post接口,在post的参数中实现调用主线程设置的监听器,关于post方法的参数为Runnable对象,其中需要实现void run()方法,可以再该方法中调用主线程注册的监听器。

其他线程:
public class ThumbnailDownloader extends HandlerThread {
 ....
/* 分别保存主线程传递的Handler */
 Handler mResponseHandler;
/* 保存主线程设置的监听器对象 */
 Listener mListener;
 
/* 该接口由主线程或者接收数据的线程定义 */
 public interface Listener {
  void onThumbnailDownloaded(Token token, Bitmap thumbnail);
 }
 
/* 该监听器就是对应的实现过程 */
 public void setListener(Listener listener) {
  mListener = listener;
 }
 
 public ThumbnailDownloader(Handler responseHandler) {
  super(TAG);
  mResponseHandler = responseHandler;
 }
 
...
 handleRequest(token);
...
 
 private void handleRequest(final Token token) {
   ...   
   mResponseHandler.post(new Runnable() {
   
    @Override
    public void run() {
     if (requestMap.get(token) != urlString) {
      return;
     }
     
     requestMap.remove(token);
    /* 通过接口对象更新主线程的UI控件 */
     mListener.onThumbnailDownloaded(token, bitmap);
    }
   }); 
 }
}

主线程中的处理:
 /* 传递参数handler到其他线程中 */
  mThumbnailThread = new ThumbnailDownloader(new Handler());
/* 设置监听器,实际就是实现对具体数据的处理过程 */
  mThumbnailThread.setListener(new ThumbnailDownloader.Listener() {
   @Override
   public void onThumbnailDownloaded(ImageView token, Bitmap thumbnail) {
    /* 实现具体的处理 */
    if (isVisible()) {
     token.setImageBitmap(thumbnail);
    }
   }
 
  });
  mThumbnailThread.start();
...

其实关于post的方法实际上是等价于如下的代码:
Runnable myRunnable = new Runnable() {
public void run() {
    /*实际的处理函数*/
}
};
Message m = mHandler.obtainMessage();
m.callback = myRunnable;

也就是在message中设置了设置了callback后不会执行handler创建过程中定义的handleMessage接口,而是直接执行传递的Runnable中的run()函数。

方法二中的实现方式与Fragment与Activity之间通信的方式一样,提供服务的一方提供接口出去,而需要使用这种服务的一方实现具体的接口实现。在Activity中采用了在Activity类中implements对应接口的方式,而在多个线程之间的通信则采用了设置监听器的方式,监听器的参数中实现了对服务的处理方式。服务提供方直接调用接口对象即可实现线程之间的通信。

因此在Android要是习惯这种接口或者监听器的实现方式,即在其中一个类中定义接口,并通过某种方式获取到这个接口的实例对象,比如在Activity之间通过onAttach(),而在这边采用了设置监听器的方式。该对象时实现了具体接口的具体实体。通过该接口的实体即可实现两个类之间的通信。

A类:定义接口,并提供了获取该接口实例的方法通常采用setLister的方式实现.
B类:实现具体的接口,并通过setLister传递具体的接口实例到对象A中。
A通过B传递过来的接口对象即可调用具体的实现,完成A和B类之间的通信。A可以作为服务提供者,而B作为服务接受者。

网友评论

登录后评论
0/500
评论
一生有你llx
+ 关注