频道栏目
首页 > 程序开发 > 移动开发 > Android > 正文
Android消息处理机制之Handler
2017-04-20 10:39:00         来源:莹波&微步  
收藏   我要投稿

Android消息处理机制之Handler。Android进程内线程之间的通信广泛使用到了Handler,handler也是Android独有的消息处理机制。最常见的莫过于使用handler更新ui了。现在就来分析Handler机制。

Android进程内线程之间的通信 如下图所示:

\

Android中当一个app运行之后,至少有一个主线程,也就是通常说的UI线程。如果还有其他线程要更新UI,那么试图更新UI的线程,就要给UI主线程发送消息,告诉UI线程如何更改UI等。

当然也可以向其他线程发送消息。Android 可以处理消息的线程都有且仅有一个消息队列,用来暂存发送给他的消息,处理线程的消息也有且仅有一个处理消息的对象,用来从消息对象中取出消息进行处理。

这里就要搞清楚,当有多个消息处理线程的时候,如何发送给某个具体的消息处理线程。消息处理线程又是如何处理消息的。

Android中与消息机制相关的类主要是 Looper,Handler,Message,MessageQueue.其中Looper充当消息处理的角色,MessageQueue充当消息队列,Message充当消息,Handler可以暂时理解为充当消息的发送者,实际上还Handler还定义了如何处理消息,只是处理消息是由消息处理线程完成的。

Handler机制即可用于异步通信,也可用于同步通信。大多数时候使用的是异步通信。

Looper

源码路径:

Android-6/frameworks/base/core/java/android/os/Looper.java

主要成员如下图所示:

\

Looper充当消息处理的角色,每个线程只能有一个looper对象,那么是如何做到的呢?

如何创建线程唯一的looper

创建looper对象只能使用其提供的静态方法prepare(),因为其构造方法是私有的。

public static void prepare() {

prepare(true);

}

private static void prepare(boolean quitAllowed) {

if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");

}

sThreadLocal.set(new Looper(quitAllowed));

}

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

静态变量sThreadLocal的类型是 ThreadLocal,它通过将需要保存的对象和线程id关联在一起的方式实现了线程本地存储的功能。这样放入sThreadLocal对象中的Looper对象就和创建它的线程关联在一起了。

消息处理循环

创建好Looper对象之后,调用他的loop方法即可进入消息处理循环:

public static void loop() {

// 拿到与当前线程关联的looper对象

final Looper me = myLooper();

if (me == null) {

throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");

}

final MessageQueue queue = me.mQueue;

..............................................

for (;;) {

Message msg = queue.next(); // might block

...................................

msg.target.dispatchMessage(msg);

..........................................

msg.recycleUnchecked();// 回收Message对象

}

}

loop()方法内部是一个for无线循环,所以肯定调用loop方法也是有讲究的:一般来说对其的调用是放在线程的run方法中的。

class mtThread extends Thread{

public void run(){

Looper.prepare();

Looper.loop();

}

}

loop()方法会循环从MessageQueue队列中取出消息,然后吧消息分发出去。消息的分发是通过Message对象的Target变量完成的。这变量的类型是Handler类型。一个Looper对象可以和多个Handler对象关联.

Message是消息的载体,发送者吧需要传递的消息放在Message对象中,Message创建的时候需要指定他的处理对象。Handler主要用来发送和处理消息。只不过处理消息这个过程发生在消息处理线程中。

消息的载体Messenger

Android-6/frameworks/base/core/java/android/os/Message.java

关键数据成员:

public final class Message implements Parcelable {

// 标识消息的类型,消息分发的时候需要使用

public int what;

// 此消息可以携带的一个int型数据

public int arg1;

// 此消息可以携带的第二个int型数据

public int arg2;

// 此消息可以携带的一个对象

public Object obj;

public Messenger replyTo;

/*package*/ static final int FLAG_IN_USE = 1 << 0;

/** If set message is asynchronous */

/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;

/** Flags to clear in the copyFrom method */

/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;

/*package*/ int flags;

/*package*/ long when;

/*package*/ Bundle data;

/*package*/ Handler target; // 此消息绑定的Handler,也就是该Handler发送和处理该消息

/*package*/ Runnable callback; // 处理消息的回调方法,执行是在消息处理线程

其中target表示哪个Handler处理该消息。要知道在哪里设置了该字段的值。

如何创建一个Message

创建Message对象,不建议直接new,而是调用Message提供的一个静态方法obtain():

推荐:

public static Message obtain(Handler h) {

Message m = obtain();

m.target = h;

return m;

}

Message设计时,实现了Recyle机制。

public static Message obtain() {

synchronized (sPoolSync) {

if (sPool != null) {

Message m = sPool;

sPool = m.next;

m.next = null;

m.flags = 0; // clear in-use flag

sPoolSize--;

return m;

}

}

return new Message();

}

其他obtain方法:

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

public static Message obtain(Handler h, Runnable callback);//同事指定了处理该消息的逻辑

public static Message obtain(Handler h, int what, Object obj);

public static Message obtain(Handler h, int what, int arg1, int arg2);

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

Recycle机制

Android源码中大量使用了一个设计机制:当一个对象不再使用时把它储藏起来,不让虚拟机回收,需要的时候再从仓库里拿出来重新使用,这就避免了对象被回收后再重分配的过程。对于在应用的生命周期内(或者在循环中)需要频繁创建的对象来说这个机制特别实用,可以显著减少对象创建的次数,从而减少 GC 的运行时间。运用得当便可改善应用的性能

如何实现?

首先,我们需要一个仓库用于存放暂时不用的对象;需要新对象的时候我们不能使用 new 来分配一个新对象,所以还需要一个方法 obtain 来从仓库里获取对象;最后,便是 recycle 方法了,用于回收不再使用的对象。

Message类中与Recycle机制相关的成员,要注意除了next外,其他都为static类型。

// 同步

private static final Object sPoolSync = new Object();

// 仓库中的某个Message

private static Message sPool;

// 指向仓库中的下一个可用Message对象,当 next 为 null 时表示仓库为空

Message next;

// 仓库中的 Message 对象 数量

private static int sPoolSize = 0;

// 回收仓库中的Message对象数量最大为50个

private static final int MAX_POOL_SIZE = 50;

private static boolean gCheckRecycle = true;

创建对象的时候,不建议直接new,而是调用obtain()来尝试从仓库中获取。

第一次调用Obtain时,仓库为空,那么就调用构造方法创建Message对象。当Message不在使用的时候,调用recycle()方法可以把该对象放在仓库中。

public void recycle() {

if (isInUse()) {

if (gCheckRecycle) {

throw new IllegalStateException("This message cannot be recycled because it "

+ "is still in use.");

}

return;

}

recycleUnchecked();

}

void recycleUnchecked() {

// Mark the message as in use while it remains in the recycled object pool.

// Clear out all other details.

flags = FLAG_IN_USE;

what = 0;

arg1 = 0;

arg2 = 0;

obj = null;

replyTo = null;

sendingUid = -1;

when = 0;

target = null;

callback = null;

data = null;

synchronized (sPoolSync) {

if (sPoolSize < MAX_POOL_SIZE) {

// 初次sPool为null

next = sPool;

sPool = this;

sPoolSize++;

}

}

放在仓库中,实际上就是为该对象增加了一个引用而已,这样虚拟机就不会回收它了。等下次使用在使用obtain方法尝试获取Message对象的时候,就可以省去分配对象的过程了,直接从仓库中获取。

可能会有疑问,平时在使用过程中,并没有手动调用过recycle()方法啊,其实Looper在处理消息时,会调用Message的recyle()方法!!!

handler

在创建Message对象时,需要关联一个Handler,那么现在看看这个Handler是什么。

源码位置:

Android-6/frameworks/base/core/java/android/os/Handler.java

Handler充当了消息发送者和处理者的角色(定义了如何处理消息,但是是由Looper调用的,执行发生在消息处理线程),与Handler关联的Message可以有多个,那么Handler就要提供一个分发的功能,因为不同的消息,处理肯定也不一样。在一个线程中,可以只使用一个Handler对象来处理所有消息,也可以有多个。

如何创建Handler

Handler的创建必须关联一个Looper,因为Handler负责发送消息,那么自然要指定谁来接收这个消息了。如果创建Handler没有关联Looper,那么默认关联当前线程中的Looper对象。

Handler也要负责定义消息的实际处理逻辑,所以创建Handler时,可以传递一个callback,负责这个Handler所有消息的处理。但这不是必须的,因为Message类中也有callback变量,而且可以使用下面的方法创建Message并指定消息处理逻辑:

public static Message obtain(Handler h, Runnable callback);

创建Handler的构造方法如下,构造Handler时会将与之关联的Looper中的消息队列也通过一个引用保存在Handler对象中,方便直接操作。

普通的消息的Handler:

// 关联当前线程的Looper

// 也指定了处理该Handler中所有Message的回调方法

public Handler(Callback callback) {

this(callback, false);

}

// 指定关联的Looper

public Handler(Looper looper) {

this(looper, null, false);

}

// 指定关联的Looper

// 也指定了处理该Handler中所有Message的回调方法

public Handler(Looper looper, Callback callback) {

this(looper, callback, false);

}

异步消息的Handler:

// 下面三个构造方法中有一个相同的参数:boolean类型的 async

// 为true时,表明异步消息

public Handler(boolean async) {

this(null, async);

}

public Handler(Callback callback, boolean async);

public Handler(Looper looper, Callback callback, boolean async){

mLooper = looper;

// 与这个Handler关联的Looper中的消息队列

mQueue = looper.mQueue;

mCallback = callback;

//在将该消息放入消息队列的enqueueMessage方法中,该变量为true时,会设置该消息为异步消息

mAsynchronous = async;

}

普通消息和异步消息的区别在于是否调用Message的setAsynchronous将其设置为异步消息了。默认创建的消息的均为普通消息,需要显示调用setAsynchronous将其变为异步消息。

public void setAsynchronous(boolean async) {

if (async) {

flags |= FLAG_ASYNCHRONOUS;

} else {

flags &= ~FLAG_ASYNCHRONOUS;

}

}

根据创建Handler方式的不同,在Handler发送消息到消息队列的时候设置是否为异步消息。异步消息和普通消息的处理之后当消息队列中存在一个target为null的message的时候,才会不同。当消息队列中没有这样的message的时候,处理是一样的。

Handler如何发送Message

有两大类发送消息的方法。

send类,该类方法发送的消息一般用于发送传统的带有消息id的消息,也可以携带一些其他信息。消息的处理由Handler设定callback方法处理。当然Message也是可以指定消息处理的callback的。

// 发送一个Message,希望马上处理

public final boolean sendMessage(Message msg)

// 创建一个Message,初始化what,然后发送,希望马上处理

public final boolean sendEmptyMessage(int what)

// 创建一个Message,初始化what,然后在uptimeMillis毫秒时 发送,希望在指定的时间处理

public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)

// 延时delayMillis毫秒,在发送Message,希望延时一段时间处理

public final boolean sendMessageDelayed(Message msg, long delayMillis)

// 在uptimeMillis毫秒时,发送Message,希望在指定的时间处理

public boolean sendMessageAtTime(Message msg, long uptimeMillis)

上述send方法最终都会调用

public final boolean sendMessageDelayed(Message msg, long delayMillis)

{

if (delayMillis < 0) {

delayMillis = 0;

}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

MessageQueue queue = mQueue;

if (queue == null) {

RuntimeException e = new RuntimeException(

this + " sendMessageAtTime() called with no mQueue");

Log.w("Looper", e.getMessage(), e);

return false;

}

return enqueueMessage(queue, msg, uptimeMillis);

}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;//在这里设置了Message的target字段

// 如果是异步Handler的时候,mAsynchronous为true

// 此时就会把它发送的消息设置为异步消息,然后放入消息队列中

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}

所谓的send消息,只是把消息插入到了消息队列中,同时指定消息处理的时间。MesssageQueue中的消息是按照时间排序的,后面在细说。

点击复制链接 与好友分享!回本站首页
上一篇:[RK3288][Android6.0] 调试笔记 --- 开机默认选择24小时制时间格式
下一篇:Android6.0之AMS前奏
相关文章
图文推荐
点击排行

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站