Android - Handler 、AsyncTask(一)

Android中,更新UI的操作都必须在主线程中进行,不能做阻塞线程的操作。

当我们要执行一个耗时的操作并且最终要去更新UI(比如将计算结果反映到UI上)时,我们会考虑新开一个线程,去执行这个耗时的操作,执行完毕之后,再在主线程中更新UI

为了解决这种问题,android为我们提供了很多办法。

 

                                                                   一、handlermessage机制


下边这个小Demo演示了Handler最简单常见的用法,在新开的线程里模拟一个耗时的操作timeConsumingOperation(),操作完成后利用handler.sendEmptyMessage(0)发送消息,然后在主线程中mProgressDialog.dismiss()更新UI:

[java]  view plain  copy
  1. public class MainActivity extends Activity implements OnClickListener{    
  2.     private Button mButton;  
  3.     private ProgressDialog mProgressDialog;       
  4.     private Handler handler = new Handler(){   
  5.         public void handleMessage(Message msg){    
  6.             super.handleMessage(msg);    
  7.             mProgressDialog.dismiss();    
  8.         }    
  9.     };   
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);  
  14.         mButton= (Button)findViewById(R.id.button1);    
  15.         mButton.setOnClickListener(this);   
  16.     }  
  17.     @Override  
  18.     public void onClick(View v) {  
  19.         newThread();  
  20.     }  
  21.     private void newThread(){    
  22.         mProgressDialog= ProgressDialog.show(this"提示""耗时操作中,请稍后 ……");    
  23.         new Thread(){    
  24.             public void run(){    
  25.                 timeConsumingOperation();    
  26.                 handler.sendEmptyMessage(0);    
  27.             }    
  28.         }.start();    
  29.     }   
  30.     private void timeConsumingOperation(){    
  31.         try {    
  32.             Thread.sleep(5000);    
  33.         } catch (InterruptedException e) {    
  34.             e.printStackTrace();    
  35.         }    
  36.     }  
  37. }  

在上述代码中,发送消息的 sendEmptyMessage() 方法是由Handler的子类对象调用的,更新UI的方法mProgressDialog.dismiss()也是在Handler的子类复写的handleMessage()方法中定义的,所以我们先从创建Handler的子类对象new Handler()讲起

Handler类概要:
[java]  view plain  copy
  1. /** 
  2.  * A Handler allows you to send and process {@link Message} and Runnable 
  3.  * objects associated with a thread's {@link MessageQueue}.  Each Handler 
  4.  * instance is associated with a single thread and that thread's message 
  5.  * queue.  When you create a new Handler, it is bound to the thread / 
  6.  * message queue of the thread that is creating it -- from that point on, 
  7.  * it will deliver messages and runnables to that message queue and execute 
  8.  * them as they come out of the message queue. 
  9.  * <p>There are two main uses for a Handler: (1) to schedule messages and 
  10.  * runnables to be executed as some point in the future; and (2) to enqueue 
  11.  * an action to be performed on a different thread than your own ... ...   
  12.  */  
  13. //一个Handler允许你发送和处理和一个线程的Message Queue相关联的Message和Runnable对象,  
  14. //每一个Handler和一个线程及线程的Message Queue相关联... ...  
  15. public class Handler {  ......  }  

(注释太多不全列出了)通常我们执行new Handler()时,以下构造函数会被调用:

[java]  view plain  copy
  1. public Handler(Callback callback, boolean async) {   
  2.     ... ...    
  3.     final MessageQueue mQueue;  
  4.     mLooper = Looper.myLooper();  
  5.     if (mLooper == null) {  
  6.         throw new RuntimeException(  
  7.             "Can't create handler inside thread that has not called Looper.prepare()");  
  8.     }  
  9.     mQueue = mLooper.mQueue;  
  10.     mCallback = callback;  
  11.     ... ...   
  12. }  

其中mLooper是Handler的成员变量final Looper mLooper, Looper类的作用是管理此线程里的Message Queue先来看看Looper类的继承关系及类概要:


[java]  view plain  copy
  1. /** 
  2.  * Class used to run a message loop for a thread.  Threads by default do 
  3.  * not have a message loop associated with them; to create one, call 
  4.  * {@link #prepare} in the thread that is to run the loop, and then 
  5.  * {@link #loop} to have it process messages until the loop is stopped. 
  6.  *  
  7.  * <p>Most interaction with a message loop is through the 
  8.  * {@link Handler} class. 
  9.  */  
  10. public final class Looper {}  

此类用来为一个线程run一个message loop,一个线程默认是没有和其关联的message loop的,如果想要创建,则调用prepare方法,然后调用loop方法来处理消息......

而myLooper()方法的注释如下:

[java]  view plain  copy
  1. /** 
  2.  * Return the Looper object associated with the current thread.  Returns 
  3.  * null if the calling thread is not associated with a Looper. 
  4.  */  
  5. //该方法用于获取当前线程的Looper对象,如果没有则返回null  
  6. public static Looper myLooper() {  
  7.     return sThreadLocal.get();  
  8. }  

该方法用于获取当前线程的Looper对象,如果没有则返回null

需要注意的是:系统默认为主线程而没有为子线程创建looper对象,所以在子线程中直接new Handler()会报如下错误(参考public Handler(Callback callback, boolean async)方法):                                                          

"Can't create handler inside thread that has not called Looper.prepare()");

我们来看看系统在主线程中创建looper对象的相关代码,从ActivityThread.Java中我们可以看到:

[java]  view plain  copy
  1. public static void main(String[] args) {  
  2.     ... ...  
  3.     Looper.prepareMainLooper();  
  4.     ... ...  
  5.     Looper.loop();  
  6.     ... ...  
  7. }  

其中,prepareMainLooper()方法的注释如下:

[java]  view plain  copy
  1. /** 
  2.  * Initialize the current thread as a looper, marking it as an 
  3.  * application's main looper. The main looper for your application 
  4.  * is created by the Android environment, so you should never need 
  5.  * to call this function yourself.  See also: {@link #prepare()} 
  6.  */  
  7. //初始化当前的线程为一个looper线程,标记它为一个应用的main looper,应用的main looper为  
  8. //系统自动创建,你不需要自己调用该方法  
  9. public static void prepareMainLooper() {  
  10.     prepare(false);  
  11.     ... ...  
  12. }  
prepare()方法的注释如下:

[java]  view plain  copy
  1. /** Initialize the current thread as a looper. 
  2.  * This gives you a chance to create handlers that then reference 
  3.  * this looper, before actually starting the loop. Be sure to call 
  4.  * {@link #loop()} after calling this method, and end it by calling 
  5.  * {@link #quit()}. 
  6.  */  
  7. //初始化当前线程为一个looper线程,这使得你有机会去创建一个handler,在调用这个方法之后,  
  8. //在正式开始loop之前,记得调用loop方法,最后调用quit方法end it  
  9. public static void prepare() {  
  10.     prepare(true);  
  11. }  

loop()方法的注释如下:

[java]  view plain  copy
  1. /** 
  2.  * Run the message queue in this thread. Be sure to call 
  3.  * {@link #quit()} to end the loop. 
  4.  */  
  5. //让这个线程里的message queue run起来,确保调用quit方法来end the loop  
  6. public static void loop() { ... ... }  
同样,我们如果需要在子线程中new Handler,也需要调用Looper.prepare()和Looper.loop()两个方法,正如API中对looper类的介绍一样:
[java]  view plain  copy
  1. /** 
  2.  * <p>This is a typical example of the implementation of a Looper thread, 
  3.  * using the separation of {@link #prepare} and {@link #loop} to create an 
  4.  * initial Handler to communicate with the Looper. 
  5.  * <pre> 
  6.  *  class LooperThread extends Thread { 
  7.  *      public Handler mHandler; 
  8.  * 
  9.  *      public void run() { 
  10.  *          Looper.prepare(); 
  11.  * 
  12.  *          mHandler = new Handler() { 
  13.  *              public void handleMessage(Message msg) { 
  14.  *                  // process incoming messages here 
  15.  *              } 
  16.  *          }; 
  17.  * 
  18.  *          Looper.loop(); 
  19.  *      } 
  20.  *  }</pre> 
  21.  */  
  22. public final class Looper {}  

接下来,我们看prepare()方法的主要逻辑:

[java]  view plain  copy
  1. public final class Looper {  
  2.     final MessageQueue mQueue;  
  3.     public static void prepare() {  
  4.         prepare(true);  
  5.     }  
  6.     private static void prepare(boolean quitAllowed) {  
  7.         ... ...  
  8.         sThreadLocal.set(new Looper(quitAllowed));  
  9.         ... ...   
  10.     }  
  11.     private Looper(boolean quitAllowed) {  
  12.         mQueue = new MessageQueue(quitAllowed);  
  13.         ... ...  
  14.     }  
  15. }  

可以看到,无论是在主线程的prepareMainLooper()方法中还是我们手动调用prepare()方法,都会在创建Looper对象的同时创建一个Message Queue对象赋给Looper的成员变量final MessageQueue mQueue
现在来看用来存放消息的Message Queue的继承关系及类概要:

[java]  view plain  copy
  1. /** 
  2.  * Low-level class holding the list of messages to be dispatched by a 
  3.  * {@link Looper}.  Messages are not added directly to a MessageQueue, 
  4.  * but rather through {@link Handler} objects associated with the Looper. 
  5.  *  
  6.  * <p>You can retrieve the MessageQueue for the current thread with 
  7.  * {@link Looper#myQueue() Looper.myQueue()}. 
  8.  */  
  9. public final class MessageQueue { }  
而我们new Handler 时,在public Handler(Callback callback, boolean async)方法中,在通过Looper.myLooper()方法得到线程对应的Looper对象后,将该Looper对象的成员变量mQueue再赋给Handler的final MessageQueue mQueue,所以,一个Handler对象对应一个线程对应一个Looper对象对应一个MessageQueue 。

至此,我们 从Handler对象的创建开始,大致将Handler、Looper和MessageQueue 的相互关系理清了。接下来,我们分析在子线程中发送消息和在主线程中处理消息的过程。

在新开的线程中完成耗时的操作后,需要发送消息,那我们就 从消息的创建开始

在Handler机制中,创建一个消息可以用以下两种方法:
第一种,Message mMessage = new Message();
第二种,Message mMessage = mHandler.obtainMessage();

这两种方法的区别,我认为主要有以下两方面:
一、效率上的区别。obtainMessage()方法直接从message pool中返回一个Message对象,省去了新创建对象的开销,提高了效率。我们来看Handler类中的注释:
[java]  view plain  copy
  1. /** 
  2.  * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than 
  3.  * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). 
  4.  *  If you don't want that facility, just call Message.obtain() instead. 
  5.  */  
  6. //直接从message pool中返回一个message,这将比直接创建一个message更有效率,返回的message已经有了  
  7. //和它关联的handler(这是在Message类中的obtain(Handler h)方法中通过m.target = h实现的),  
  8. //你也可以直接调用Message.obtain()方法来创建消息  
  9. public final Message obtainMessage(){  
  10.     return Message.obtain(this);  
  11. }  
二、灵活性上的区别
通过查看Message和Handler的源码,我们发现,Handler类中obtainMessage()方法最终会调用Message 类中的obtain()方法,并且这两个方法都有若干个不同参数的一 一对应的方法可供选择, 我们就以Message类中参数最多的obtain(Handler h, int what, int arg1, int arg2, Object obj)方法为例来讲吧,直接上源码:
[java]  view plain  copy
  1. /** 
  2.  * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,  
  3.  * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members. 
  4.  */  
  5. public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {  
  6.     Message m = obtain();  
  7.     m.target = h;  
  8.     m.what = what;  
  9.     m.arg1 = arg1;  
  10.     m.arg2 = arg2;  
  11.     m.obj = obj;  
  12.     return m;  
  13. }  
第一句,Message m = obtain();我们来看 obtain()方法的注释:
[java]  view plain  copy
  1. /** 
  2.  * Return a new Message instance from the global pool. Allows us to 
  3.  * avoid allocating new objects in many cases. 
  4.  */  
  5. //从global pool中返回一个新的message对象,能避免我们在很多情况下去新建对象  
  6. public static Message obtain() {  
  7.     ... ...  
  8. }  
在获得一个Message对象之后,接着为它的成员变量赋值:m.what = what 、m.arg1 = arg1 ,等等。
接下来我们看看,Message类都有哪些成员变量,它们的作用都是什么。依然是源码:
[java]  view plain  copy
  1. public final class Message implements Parcelable {  
  2.     /** 
  3.      * User-defined message code so that the recipient can identify  
  4.      * what this message is about. Each {@link Handler} has its own name-space 
  5.      * for message codes, so you do not need to worry about yours conflicting 
  6.      * with other handlers. 
  7.      */  
  8.     //可以理解为一个用于区分消息的标示符  
  9.     public int what;  
  10.     /** 
  11.      * arg1 and arg2 are lower-cost alternatives to using 
  12.      * {@link #setData(Bundle) setData()} if you only need to store a 
  13.      * few integer values. 
  14.      */  
  15.     //如果你只是想携带一些比较简单的integer数据的话,相对于setData(Bundle)方法来讲  
  16.     //arg1和arg2是一种比较低耗的选择  
  17.     public int arg1;   
  18.     /** 
  19.      * arg1 and arg2 are lower-cost alternatives to using 
  20.      * {@link #setData(Bundle) setData()} if you only need to store a 
  21.      * few integer values. 
  22.      */  
  23.     public int arg2;  
  24.     Bundle data;  
  25.     Handler target;  
  26.     Runnable callback;  ... ...  
  27. }  
1、最常用的what,可以理解为是一个对message进行区分的标示符。
2、arg1和arg2可以让message携带简单的整数,它相对于setData(Bundle data)使用Bundle来传送数据,更简单轻便。
3、data变量在需要传送的数据比较复杂的时候使用,比如:
[java]  view plain  copy
  1. Bundle mBundle = new Bundle();  
  2. mBundle.putBoolean("myKey1"true);  
  3. mBundle.putString("myKey2""myValue");  
  4. Message mMessage = Message.obtain(handler, 5111112, mBundle);  
  5. //或者:  
  6. Bundle mBundle = new Bundle();  
  7. mBundle.putBoolean("myKey1"true);  
  8. mBundle.putString("myKey2""myValue");  
  9. Message mMessage = new Message();  
  10. mMessage.setData(mBundle);  
  11. //在接收到message时,可以通过Message的getData()方法获取mBundle对象  
在这里,我们也可以感受到 为获得Message对象的obtain()方法的参数中 加入Object obj的作用,它 让message的创建有了更好的扩展性
4、Handler target ,Message的成员变量
5、Runnable callback ,这个放到后边来讲

通过上边的讲解,我们可以看到,在创建一个Message对象时,我们需要考虑 :
为它的哪些成员变量赋值,需要让它携带什么样的数据,有没有对应的obtain()方法可供使用,是使用obtain()方法呢,还是直接new一个Message再为它的成员变量赋值?
—— 这就是创建一个Message对象的两种方法在灵活性上的区别

消息创建完成了,接着讲发送消息
Handler类中发送消息的主要是send和post两类方法,先看send相关方法
比较常用的有:sendMessage和sendEmptyMessage,它们的注释如下:
[java]  view plain  copy
  1. /** 
  2.  * Pushes a message onto the end of the message queue after all pending messages 
  3.  * before the current time. It will be received in {@link #handleMessage}, 
  4.  * in the thread attached to this handler. 
  5.  *   
  6.  * @return Returns true if the message was successfully placed in to the  
  7.  *         message queue.  Returns false on failure, usually because the 
  8.  *         looper processing the message queue is exiting. 
  9.  */  
  10. public final boolean sendMessage(Message msg){  
  11.     return sendMessageDelayed(msg, 0);  
  12. }  
[java]  view plain  copy
  1. /** 
  2.  * Sends a Message containing only the what value. 
  3.  *   
  4.  * @return Returns true if the message was successfully placed in to the  
  5.  *         message queue.  Returns false on failure, usually because the 
  6.  *         looper processing the message queue is exiting. 
  7.  */  
  8.  //发送一条仅仅含有what值得消息,返回true(false)如果(没有)成功将消息发送到消息队列  
  9. public final boolean sendEmptyMessage(int what){  
  10.        return sendEmptyMessageDelayed(what, 0);  
  11. }  
sendMessage()方法是将一个(不管通过什么方式)已经构建好的Message对象放到消息队列中,而sendEmptyMessage(int what)方法,则是先通过Message.obtain()获得一个Message,将what值赋给Message的what变量:msg.what = what,再将消息发送到消息队列。
所以,如果需要发送一个仅仅包含what值的消息,那么Handler类的sendEmptyMessage(int what)方法是首选(优化效率)。

然后看post相关的方法,以post(Runnable r)为例,先看源码:
[java]  view plain  copy
  1. /** 
  2.  * Causes the Runnable r to be added to the message queue. 
  3.  * The runnable will be run on the thread to which this handler is  
  4.  * attached.  
  5.  * @param r The Runnable that will be executed. 
  6.  * @return Returns true if the Runnable was successfully placed in to the  
  7.  *         message queue.  Returns false on failure, usually because the 
  8.  *         looper processing the message queue is exiting. 
  9.  */  
  10. public final boolean post(Runnable r){  
  11.       return  sendMessageDelayed(getPostMessage(r), 0);  
  12. }  
需要说明的是,post(Runnable r)方法和sendMessage(Message msg)方法都调用了sendMessageDelayed(Message msg, long delayMillis)方法,只不过 post(Runnable r)方法中,先 通过 getPostMessage(r)获取了  一个 成员变量Runnable callback的值为Runnable r 的 Message对象 ,下面是 getPostMessage(Runnable r)的源码:
[java]  view plain  copy
  1. private static Message getPostMessage(Runnable r) {  
  2.     Message m = Message.obtain();  
  3.     m.callback = r;  
  4.     return m;  
  5. }  
在讲消息处理之前,还需要提到的是, 我们可以从Handler.java中看到,无论调用哪个send和post方法来发送message,最终都会调用 enqueueMessage()方法,我们来看源码:
[java]  view plain  copy
  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  2.     msg.target = this;  
  3.     if (mAsynchronous) {  
  4.         msg.setAsynchronous(true);  
  5.     }  
  6.     return queue.enqueueMessage(msg, uptimeMillis);  
  7. }  

在enqueueMessage()方法的第一行,先执行msg.target = this,将发送消息的这个handler赋值给Message的成员变量Handler target,然后再将消息发送到消息队列

现在,来看处理消息的逻辑:
在Looper类的概要中,我们提到过,调用loop方法之后,就能循环从消息队列中取出并处理消息了
loop()方法:
[java]  view plain  copy
  1. public static void loop() {  
  2.     for (;;) {  
  3.         Message msg = queue.next();   
  4.         msg.target.dispatchMessage(msg);  
  5.      }  
  6. }  

loop()方法中,循环从MessageQueue中获取Message对象(Message msg = queue.next();,然后调用 msg.target.dispatchMessage(msg)方法进行处理,哪个handler发送的消息就由哪个handler来处理,而我们在讲发送消息时也做过铺垫了。来看dispatchMessage()方法的定义:

[java]  view plain  copy
  1. public void dispatchMessage(Message msg) {  
  2.     if (msg.callback != null) {  
  3.         handleCallback(msg);  
  4.     } else {  
  5.         if (mCallback != null) {  
  6.             if (mCallback.handleMessage(msg)) {  
  7.                 return;  
  8.             }  
  9.         }  
  10.         handleMessage(msg);  
  11.     }  
  12. }  
首先判断,如果(msg.callback != null),则执行handleCallback(msg),所以由post发送过来的Runnable对象,在处理的时候执行的就是 handleCallback(msg)方法,来看该方法的定义:
[java]  view plain  copy
  1. private static void handleCallback(Message message) {  
  2.     message.callback.run();  
  3. }  
如果(msg.callback == null),则执行 handleMessage(msg)方法,来看该方法在Handler类中的定义:
[java]  view plain  copy
  1. /** 
  2.  * Subclasses must implement this to receive messages. 
  3.  */  
  4.   //子类必须实现该方法来处理消息  
  5. public void handleMessage(Message msg) {  
  6.   //空的  
  7. }  

我们从一个简单常见的例子入手,先以Handler对象的创建为出发点,大致将Handler、Looper和MessageQueue 的相互关系理清,接下来,分析在子线程中创建消息、发送消息和在主线程中处理消息的过程及在这些过程中的常用方法。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值