您的位置:首页 > 移动开发 > Android开发

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

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

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

[java] view
plaincopy





httpResponse = httpClient.execute(httpGet);

if(200 == httpResponse.getStatusLine().getStatusCode()){

byte[] data = EntityUtils.toByteArray(httpResponse.getEntity());

// 这里的数据data我们必须发送给UI的主线程,所以我们通过Message的方式来做桥梁。

Message message = Message.obtain();

message.obj = data;

message.what = DOWNLOAD_IMG;

handler.sendMessage(message);

}

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

[java] view
plaincopy





private static final Object sPoolSync = new Object();

private static Message sPool;

private static int sPoolSize = 0;

private static final int MAX_POOL_SIZE = 10;

/**

* Return a new Message instance from the global pool. Allows us to

* avoid allocating new objects in many cases.

*/

public static Message obtain() {

synchronized (sPoolSync) {

if (sPool != null) {

Message m = sPool;

sPool = m.next;

m.next = null;

sPoolSize--;

return m;

}

}

return new Message();

}

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

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

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

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

1.Handler 处理并输出控制台

[java] view
plaincopy





private Handler handler = new Handler(){

@Override

public void handleMessage(android.os.Message msg) {

int arg1 = msg.arg1;

int arg2 = msg.arg2;

int what = msg.what;

Object result = msg.obj;

System.out.println("--> arg1: " + arg1);

System.out.println("--> arg2: " + arg2);

System.out.println("--> what: " + what);

System.out.println("--> result: " + result);

}

};

2. 子线程 MyThread 的处理

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

[java] view
plaincopy





public class MyThread implements Runnable{

@Override

public void run() {

// 使用第一种构造方法

Message message = Message.obtain();

message.what = 1;

message.arg1 = 1;

message.arg2 = 3;

message.obj = "AHuier";

handler.sendMessage(message);

}

}

程序执行输出如下图所示:



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

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

[java] view
plaincopy





/*

* 第二种获取Message对象的方法

* public static Message obtain (Handler h)

* 传递一个关联到消息Handler.

*/

Message message = Message.obtain(handler);

message.what = 1;

message.arg1 = 1;

message.arg2 = 3;

message.obj = "AHuier";

message.sendToTarget(); // 完成发送消息的动作

[分析源码]:

[java] view
plaincopy





/**

* Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned.

* @param h Handler to assign to the returned Message object's <em>target</em> member.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h) {

Message m = obtain();

m.target = h;

return m;

}

先调用obtain()的方式来获取Message对象,然后把Handler的对象给了Message,我们查看一下sendToTarget()的操作:

[java] view
plaincopy





...

/*package*/ Handler target;

...

/**

* Sends this Message to the Handler specified by {@link #getTarget}.

* Throws a null pointer exception if this field has not been set.

*/

public void sendToTarget() {

target.sendMessage(this);

}

它是完成发送消息的动作,所以这种方式不需要在通过sendMessage的方式来处理了。只需要调用messge.sendToTarget();的方式就可以了。

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

[java] view
plaincopy





/*

* 第三种获取Message对象的方法

* public static Message obtain (Handler h, int what)

* 关联一个Handler和传递一个what的属性值

*/

Message message = Message.obtain(handler, 1);

message.arg1 = 1;

message.arg2 = 3;

message.obj = "AHuier";

message.sendToTarget();

[分析源码]:

[java] view
plaincopy





/**

* Same as {@link #obtain()}, but sets the values for both <em>target</em> and

* <em>what</em> members on the Message.

* @param h Value to assign to the <em>target</em> member.

* @param what Value to assign to the <em>what</em> member.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h, int what) {

Message m = obtain();

m.target = h;

m.what = what;

return m;

}

从源码我们可以看出这种方式更为简便,它也是内部将handle和what都赋值给Message对象,从而简化我们的代码。

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

[java] view
plaincopy





/*

* public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj)

* 关联Handler和传递Message的几种常用属性值

*/

Message message = Message.obtain(handler, 1, 1, 3, "AHuier");

message.sendToTarget();

[分析源码]:

[java] view
plaincopy





/**

* Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,

* <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.

*

* @param h The <em>target</em> value to set.

* @param what The <em>what</em> value to set.

* @param arg1 The <em>arg1</em> value to set.

* @param arg2 The <em>arg2</em> value to set.

* @param obj The <em>obj</em> value to set.

* @return A Message object from the global pool.

*/

public static Message obtain(Handler h, int what,

int arg1, int arg2, Object obj) {

Message m = obtain();

m.target = h;

m.what = what;

m.arg1 = arg1;

m.arg2 = arg2;

m.obj = obj;

return m;

}

同理,从源码中我们可以看出它也是在obtain()方法内部将传递过来的参数赋值给Message对象了。

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

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

[java] view
plaincopy





/**

* Same as {@link #obtain()}, but copies the values of an existing

* message (including its target) into the new one.

* @param orig Original message to copy.

* @return A Message object from the global pool.

*/

public static Message obtain(Message orig) {

Message m = obtain();

m.what = orig.what;

m.arg1 = orig.arg1;

m.arg2 = orig.arg2;

m.obj = orig.obj;

m.replyTo = orig.replyTo;

if (orig.data != null) {

m.data = new Bundle(orig.data);

}

m.target = orig.target;

m.callback = orig.callback;

return m;

}

通过源码知道,同样它首先先从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
plaincopy





/*

* public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj)

* 关联Handler和传递Message的几种常用属性值

*/

Message message = Message.obtain(handler, 1, 1, 3, "AHuier");

Bundle data = new Bundle();

data.putStringArray("str", new String[]{"AHui", "AHui1", "AHui2"});

message.setData(data);

message.sendToTarget();

ii. 处理消息,同时从Bundle中取出数据,打印到控制台,输出键"str"的字符串数组长度为3

[java] view
plaincopy





private Handler handler = new Handler(){

@Override

public void handleMessage(android.os.Message msg) {

int arg1 = msg.arg1;

int arg2 = msg.arg2;

int what = msg.what;

Object result = msg.obj;

System.out.println("--> arg1: " + arg1);

System.out.println("--> arg2: " + arg2);

System.out.println("--> what: " + what);

System.out.println("--> result: " + result);

System.out.println("----------------------");

Bundle bundle = msg.getData();

System.out.println("--> bundle: " + bundle.getStringArray("str").length);

}

};

【总结】:这种方式也Android推荐使用的。但是我自己对这一部分的理解是:

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

源码下载:HandlerMessageTest2
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐