【Android 开发】: Android 消息处理机制之二: Message 中 obtain()源代码剖析

本文详细解析了 Android 中 Message 的多种获取方式及其内部实现原理,包括如何利用 Bundle 传递复杂数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


  阅读本文之前,请先阅读相关链接:

  Android 消息处理机制之一: Handler与Message

  Android 消息处理机制之三: Handler中sendMessage()源代码剖析 

  在上一讲中[Android消息处理机制之Handler与Message],我们学习了Handler类与Message类的大概介绍,同事也遗留了一个问题,在获取Message对象的时候是不能用 "new Message" 的方式来获取,而必须使用 Obtain()的方式来获取Message对象,这是为什么呢?

  我们可以针对上一讲的例子中的代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. httpResponse = httpClient.execute(httpGet);  
  2. if(200 == httpResponse.getStatusLine().getStatusCode()){  
  3.     byte[] data = EntityUtils.toByteArray(httpResponse.getEntity());  
  4.     // 这里的数据data我们必须发送给UI的主线程,所以我们通过Message的方式来做桥梁。  
  5.     Message message = Message.obtain();  
  6.     message.obj = data;  
  7.     message.what = DOWNLOAD_IMG;  
  8.     handler.sendMessage(message);  
  9. }  

1) 跟踪Message.java中的obtain()方法如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static final Object sPoolSync = new Object();  
  2. private static Message sPool;  
  3. private static int sPoolSize = 0;  
  4.   
  5. private static final int MAX_POOL_SIZE = 10;  
  6.   
  7. /** 
  8.  * Return a new Message instance from the global pool. Allows us to 
  9.  * avoid allocating new objects in many cases. 
  10.  */  
  11. public static Message obtain() {  
  12.     synchronized (sPoolSync) {  
  13.         if (sPool != null) {  
  14.             Message m = sPool;  
  15.             sPool = m.next;  
  16.             m.next = null;  
  17.             sPoolSize--;  
  18.             return m;  
  19.         }  
  20.     }  
  21.     return new Message();  
  22. }  

【分析】: 从obtain()的源代码中我们可以知道,它是静态方法,而且只有在spool = null 的情况下才会new出一个Message(),返回一个Message对象,如果在不为空的情况下,Message的对象都是从Message对象池里面拿的实例从而重复使用的,这也为了Android中的Message对象能够更好的回收。

查看Message的源代码,我们可以发现它有多个重载的obtain()方法,这一讲我们就来通过Demo和Message源码来剖析它们之间的不同。

一. 先通过一个程序Demo来进入主题

  通过Thread + Handler + Message的方式在子线程中发送信息,然后在控制台中输出。代码与上一讲中的代码架构类似,我们主要是看一下子线程中run()的处理和Handler的处理。

1.Handler 处理并输出控制台

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private Handler handler = new Handler(){  
  2.     @Override  
  3.     public void handleMessage(android.os.Message msg) {  
  4.         int arg1 = msg.arg1;  
  5.         int arg2 = msg.arg2;  
  6.         int what = msg.what;  
  7.         Object result = msg.obj;  
  8.         System.out.println("--> arg1: " + arg1);  
  9.         System.out.println("--> arg2: " + arg2);  
  10.         System.out.println("--> what: " + what);  
  11.         System.out.println("--> result: " + result);  
  12.     }  
  13. };  

2. 子线程 MyThread 的处理

1) 通过 Message.obtain()方式获取Message对象

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class MyThread implements Runnable{  
  2.   
  3.     @Override  
  4.     public void run() {  
  5.         // 使用第一种构造方法  
  6.         Message message = Message.obtain();  
  7.         message.what = 1;  
  8.         message.arg1 = 1;  
  9.         message.arg2 = 3;  
  10.         message.obj = "AHuier";  
  11.         handler.sendMessage(message);  
  12.     }          
  13. }  
  程序执行输出如下图所示:

[分析源码]: 这种方式博文上述中已经分析过了,主要工作是在Message对象池中获取对象。

2) 通过 Message.obtain(Handler h)的方式获取Message对象

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  *  第二种获取Message对象的方法 
  3.  *  public static Message obtain (Handler h) 
  4.  *  传递一个关联到消息Handler. 
  5.  */  
  6. Message message = Message.obtain(handler);  
  7. message.what = 1;  
  8. message.arg1 = 1;  
  9. message.arg2 = 3;  
  10. message.obj = "AHuier";  
  11. message.sendToTarget(); // 完成发送消息的动作  

[分析源码]:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned. 
  3.  * @param h  Handler to assign to the returned Message object's <em>target</em> member. 
  4.  * @return A Message object from the global pool. 
  5.  */  
  6. public static Message obtain(Handler h) {  
  7.     Message m = obtain();  
  8.     m.target = h;  
  9.   
  10.     return m;  
  11. }  
  先调用obtain()的方式来获取Message对象,然后把Handler的对象给了Message,我们查看一下 sendToTarget()的操作:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ...  
  2. /*package*/ Handler target;    
  3. ...   
  4. /** 
  5.  * Sends this Message to the Handler specified by {@link #getTarget}. 
  6.  * Throws a null pointer exception if this field has not been set. 
  7.  */  
  8. public void sendToTarget() {  
  9.     target.sendMessage(this);  
  10. }  
它是完成发送消息的动作, 所以这种方式不需要在通过sendMessage的方式来处理了。只需要调用messge.sendToTarget();的方式就可以了。

3) 通过 Message.obtain(Handler h)的方式获取Message对象

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 第三种获取Message对象的方法 
  3.  * public static Message obtain (Handler h, int what) 
  4.  * 关联一个Handler和传递一个what的属性值 
  5.  */  
  6. Message message = Message.obtain(handler, 1);  
  7. message.arg1 = 1;  
  8. message.arg2 = 3;  
  9. message.obj = "AHuier";  
  10. message.sendToTarget();  
[分析源码]:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Same as {@link #obtain()}, but sets the values for both <em>target</em> and 
  3.  * <em>what</em> members on the Message. 
  4.  * @param h  Value to assign to the <em>target</em> member. 
  5.  * @param what  Value to assign to the <em>what</em> member. 
  6.  * @return A Message object from the global pool. 
  7.  */  
  8. public static Message obtain(Handler h, int what) {  
  9.     Message m = obtain();  
  10.     m.target = h;  
  11.     m.what = what;  
  12.   
  13.     return m;  
  14. }  
   从源码我们可以看出这种方式更为简便,它也是内部将handle和what都赋值给Message对象,从而简化我们的代码。

4) 通过 Message.obtain(Handler h, int what, int arg1, int arg2, Object obj)的方式获取Message对象

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj) 
  3.  * 关联Handler和传递Message的几种常用属性值 
  4.  */  
  5. Message message = Message.obtain(handler, 113"AHuier");  
  6. message.sendToTarget();  
[分析源码]:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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.  * @param h  The <em>target</em> value to set. 
  6.  * @param what  The <em>what</em> value to set. 
  7.  * @param arg1  The <em>arg1</em> value to set. 
  8.  * @param arg2  The <em>arg2</em> value to set. 
  9.  * @param obj  The <em>obj</em> value to set. 
  10.  * @return  A Message object from the global pool. 
  11.  */  
  12. public static Message obtain(Handler h, int what,   
  13.         int arg1, int arg2, Object obj) {  
  14.     Message m = obtain();  
  15.     m.target = h;  
  16.     m.what = what;  
  17.     m.arg1 = arg1;  
  18.     m.arg2 = arg2;  
  19.     m.obj = obj;  
  20.   
  21.     return m;  
  22. }  
同理,从源码中我们可以看出它也是在obtain()方法内部将传递过来的参数赋值给Message对象了。

5) 通过上述几个例子我们可以知道Message中的obtain()的几种重载方法在底层的实现都是大同小异的,他们都是底层都是首先调用obtain()方法来从消息池中获得一个消息的对象的。然后在通过参数传递来封装指定的Handler和需要携带的数据。如果使用这些重载的方法建议完成数据封装之后调用sendToTarget()方法。这就是几种obtain()重载方法的不同。

6) 这里我们需要特别注意Message中的这个重载方法:Message obtain (Message orig) 它是将原有的消息体作为一个新的消息参数来发送的,我们看一下它的源代码。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Same as {@link #obtain()}, but copies the values of an existing 
  3.  * message (including its target) into the new one. 
  4.  * @param orig Original message to copy. 
  5.  * @return A Message object from the global pool. 
  6.  */  
  7. public static Message obtain(Message orig) {  
  8.     Message m = obtain();  
  9.     m.what = orig.what;  
  10.     m.arg1 = orig.arg1;  
  11.     m.arg2 = orig.arg2;  
  12.     m.obj = orig.obj;  
  13.     m.replyTo = orig.replyTo;  
  14.     if (orig.data != null) {  
  15.         m.data = new Bundle(orig.data);  
  16.     }  
  17.     m.target = orig.target;  
  18.     m.callback = orig.callback;  
  19.   
  20.     return m;  
  21. }  
通过源码知道,同样它首先先从Message对象池中获取Message对象,然后将原有Message中的各种属性值赋予新的信息中的各种属性值,最后返回新的消息对象,再发送出去。

3. 使用Bundle方式来传递复杂的数据类型

1) 在前面的Message的Demo中,我们采用的都是传递Message自带的属性来传递一些轻量级的int类型和Object类型数据,那么如果是复杂一点的数据类型,Message也是可以传递的,传递的方式是就是采用Bundle的方式。

2) 查看Message中的api文档,我们就是采用setData(Bundle data)方法来绑定一个Bundle类型对象,而你可以往Bundle里面填充各种类型。

3) 程序Demo如下

i. 往Bundle中填入数据,同时发送消息

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj) 
  3.  * 关联Handler和传递Message的几种常用属性值 
  4.  */  
  5. Message message = Message.obtain(handler, 113"AHuier");  
  6. Bundle data = new Bundle();  
  7. data.putStringArray("str"new String[]{"AHui""AHui1""AHui2"});  
  8. message.setData(data);  
  9. message.sendToTarget();  
ii. 处理消息,同时从Bundle中取出数据,打印到控制台,输出键"str"的字符串数组长度为3
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private Handler handler = new Handler(){  
  2.     @Override  
  3.     public void handleMessage(android.os.Message msg) {  
  4.         int arg1 = msg.arg1;  
  5.         int arg2 = msg.arg2;  
  6.         int what = msg.what;  
  7.         Object result = msg.obj;  
  8.         System.out.println("--> arg1: " + arg1);  
  9.         System.out.println("--> arg2: " + arg2);  
  10.         System.out.println("--> what: " + what);  
  11.         System.out.println("--> result: " + result);  
  12.         System.out.println("----------------------");  
  13.         Bundle bundle = msg.getData();  
  14.         System.out.println("--> bundle: " + bundle.getStringArray("str").length);  
  15.     }  
  16. };  
【总结】:这种方式也Android推荐使用的。但是我自己对这一部分的理解是:

  如果是携带的是轻量级的int类型的数据或者对象的话,我们就用Message构造方法中自带的属性来传递。如果是需要携带上述两种类型之外的数据类型或者一些比较复杂的数据类型建议使用Bundle的方式来封装好后来传递。


源码下载:HandlerMessageTest2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值