Android Handler分析(一) Handler和Message详解

在《Android Handler机制》中我们对Handler机制的整体流程做了分析,知道了在Android中是怎样通过Handler进行线程切换的了。接下来,我们就看看Handler中的相关类分析。

Message源码分析:

前面说到了Message表示一个消息,可以理解为线程间通讯的数据单元,可以用来传递一些数据。

1. 首先看一下Message类中的一些成员变量

  • public int what:可以用来区分不同的消息,然后在Handler中做不同的处理
  • public int arg1,arg2:可以传递数据,当需要传递的数据仅仅是int类型时,可以用arg1和arg2,而不需要使用复杂的obj和data
  • public Object obj:可以传递数据,可以传递任意的Object对象
  • Bundle data:可以Bundle类型传递数据,但是使用了默认修饰符,不能直接使用Message对象.data的形式设置和获取值,但Message提供了对应的get和set方法用来设置和获取值
  • Handler target:保存发送消息的Handler对象,可以在创建Message时指定,在后面会有用到target这个参数,所以特别说明
  • Runnable callback:Runable对象,可以在创建Message时指定,另外在Handler使用post形式发送消息时的callback参数最终也是保存在 Message的这个参数中
  • Message next:Message对象,用来使Message组成链表的形式
  • private static Message sPool:用于保存回收的Message消息对象
  • private static boolean gCheckRecycle = true:判断Android版本是否大于Android5.0(Build.VERSION_CODES.LOLLIPOP),小于为false

2. 创建(获取)一个Message对象(以下方法都是使用Public修饰的)

Message(){} // 空参数构造方法,直接new Message()创建对象
static Message obtain() // 使用静态的obtain()方法获取,内部会重复使用回收的Message对象,比较常用
static Message obtain(Message orig) // 根据传递的Message对象复制一个新的Message对象
static Message obtain(Handler h) // 给Message指定Handler,会将 h 赋值给 成员变量 target
static Message obtain(Handler h, Runnable callback) // 给Message指定Handler和Runable,会将 h 赋值给 成员变量 target,callback赋值给Message的成员变量 callback
static Message obtain(Handler h, int what) // 给Message指定Handler和what,赋值给Message中对应的成员变量
static Message obtain(Handler h, int what, Object obj) // 给Message指定Handler、what和Object类型的值,赋值给Message中对应的成员变量
static Message obtain(Handler h, int what, int arg1, int arg2) // 指定Message更多的属性
Message obtain(Handler h, int what, int arg1, int arg2, Object obj) // 指定Message更多的属性

3. Message中的部分方法

// 回收消息Message对象
public void recycle() {
    // 判断是否在使用
    if (isInUse()) {
        // 判断版本,在说成员变量时有说明,如果版本大于5.0就会报错
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        // 如果该Message对象在使用就返回
        return;
    }
    // 调用回收Message对象的方法
    recycleUnchecked();
}
// 回收消息
void recycleUnchecked() {
    flags = FLAG_IN_USE; // 将标记设置为没有使用
    what = 0; 
    // ...  清除Message成员变量的值

    // 使用同步代码
    synchronized (sPoolSync) {
        // 判断回收消息队列的长度是否大于最大长度
        if (sPoolSize < MAX_POOL_SIZE) {
            // 将回收的消息添加到回收的链表中
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}
// 给消息指定一个Handler
public void setTarget(Handler target) {
    // 赋值给target
    this.target = target;
}
// 给消息设置data值
public void setData(Bundle data) {
    this.data = data;
}
// 获取data,但是当data为null时会创建一个返回
public Bundle getData() {
    if (data == null) {
        data = new Bundle();
    }
    
    return data;
}
// 和getData()方法一样获取data值,但是不会进行判断和创建
public Bundle peekData() {
    return data;
}
// 将消息发送给指定的Handler
public void sendToTarget() {
    target.sendMessage(this);
}
// 设置是否为异步消息
public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}
// 判断消息是否在使用
/*package*/ boolean isInUse() {
    return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
// 将消息设置为使用状态
/*package*/ void markInUse() {
    flags |= FLAG_IN_USE;
}

Handler源码分析:

1. Handler的创建(构造方法):在Handler中的构造方法比较多,但是最终调用的只有两个

// 两个参数的构造方法
public Handler(Callback callback, boolean async) {
    ...
    // 通过Looper中的方法myLooper()获取与当前线程绑定的Looper,具体实现在Looper部分说明
    mLooper = Looper.myLooper();
    // 当前线程不是Looper线程,也就是没有调用Looper.prepare()给线程创建Looper对象
    if (mLooper == null) {
        // 抛出异常
        throw new RuntimeException(
        "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // 让Handler持有当前线程消息队列(MessageQueue)的引用
    mQueue = mLooper.mQueue;
    // 主要用于Handler的消息发送的回调,优先级是比handlerMessage()方法高,但不常用
    mCallback = callback;
    // 是否异步,如果为true则会调用Message中的setAsynchronous(boolean async)方法,设置参数为true
    mAsynchronous = async;
}
// 三个参数的构造方法
public Handler(Looper looper, Callback callback, boolean async) {
    // 直接赋值,获取Looper中的MessageQueue引用让Handler持有
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

我们常用的Handler handler = new Handler();实际上调用的就是两个参数的构造方法。

2. Handler发送消息:发送消息的方法比较多,下面都列出来了

// 1 sendMessage(Message msg)
// 2 sendEmptyMessage(int what)
// 3 sendEmptyMessageDelayed(int what, long delayMillis)
// 4 sendEmptyMessageAtTime(int what, long uptimeMillis)
// 5 sendMessageDelayed(Message msg, long delayMillis)

// 6 前面5个发送消息的方法最终调用的都是这个(第6个)方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 将Handler中的MessageQueue引用赋值给新的MessageQueue对象
    MessageQueue queue = mQueue;
    // 判断MessageQueue是否为null,如果为null抛出一个 RuntimeException 异常
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    // 调用Handler的enqueueMessage()方法,并将时间传递过去
    return enqueueMessage(queue, msg, uptimeMillis);
}
// 7 这个方法表示将消息对象(Message)插入到消息队列(MessageQueue)的最前面
public final boolean sendMessageAtFrontOfQueue(Message msg) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;   
    }
    // 传的时间值为0
    return enqueueMessage(queue, msg, 0);
}

我们接着看Handler中的enqueueMessage()方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 特别注意:将当前对象(Handler对象)赋值给Message的target变量
    msg.target = this;
    // 根据是否异步调用Message的setAsynchronous()方法
    // 在上面Handler的构造方法中提到过
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 调用MessageQueue的enqueueMessage()方法对Message进行排序
    return queue.enqueueMessage(msg, uptimeMillis);
}

到了这一步,我们看到了在调用Handler中的发送消息方法时,Handler在这里并没有做过多操作,然后就通过参数将Message对象传递到了MessageQueue(消息队列)中,调用的是MessageQueue中的相关方法进行相关操作,在下一篇博客《Android Handler分析 (二) MessageQueue详解》中我们就会说到。

3. Handler发送一个Runable对象

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)

以上就是Handler提供用来发送一个Runnable对象到消息队列的方法,在下面我贴出两个方法的源码:

// 通过getPostMessage()方法获取一个消息对象,然后调用sendMessageDelayed()方法
public final boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}
// 调用sendMessageAtFrontOfQueue()方法将消息添加到消息队列的队首
public final boolean postAtFrontOfQueue(Runnable r){
    return sendMessageAtFrontOfQueue(getPostMessage(r));
}

我们来看一下getPostMessage()方法干了什么事

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

这个方法获取一个消息对象,并将Runnable赋值给Message的callback变量。

我们看到使用post方式发送一个Runnable对象到消息队列其实在内部使用的也是sendXXX的方式,只是系统帮我们把Runnable对象赋值给了Message的callback变量。

4. Handler移除消息

// 移除Runnable这个消息
public final void removeCallbacks(Runnable r){
    mQueue.removeMessages(this, r, null);
}
// 移除指定了token的Runnable的消息,如果token为null,将移除所有的callback消息
public final void removeCallbacks(Runnable r, Object token){
    mQueue.removeMessages(this, r, token);
}
// 移除指定的what值的消息
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}
// 移除指定的what值和object值的消息
public final void removeMessages(int what, Object object) {
    mQueue.removeMessages(this, what, object);
}
// 移除指定的token的消息,如果token为null,将移除所有的消息(callbacks和messages)
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

由以上方法我们可以看到,Handler并没有对消息进行移除操作,只是调用了MessageQueue中的相关方法对消息对象进行操作,在下一篇博客《Android Handler分析 (二) MessageQueue详解》我们主要说这个类。

5. Handler中的dispatchMessage(Message msg)方法

public void dispatchMessage(Message msg) {
    // 判断Message中的callback是否为null,也就是判断是否为Runnable类型的消息
    if (msg.callback != null) {
        // 执行Runnable中的run()方法
        handleCallback(msg);
    } else {
        // 如果不是,判断Handler中的mCallback是否为null
        if (mCallback != null) {
            // 不为null,就执行mCallback中的handleMessage()方法
            // 并且如果该方法返回true,就不会执行Handler中的handleMessage()方法了
            // 在Handler的创建这个部分注释中说到了它的优先级更高
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 执行Handler中的handleMessage()方法,也就是我们在创建Handler对象时候重写的方法
        handleMessage(msg);
    }
}
// 调用Runnable的run()方法
private static void handleCallback(Message message) {
    message.callback.run();
}

说明:前面所说的Handler中的方法(Handler的创建、发送/移除消息等)我们在开发中都调用过,但是对于dispatchMessage()方法我们却并没有调用过,那么它是在哪里调用然后让系统来执行我们的handleMessage()方法呢?不要着急,在这里我们先记住这个方法,在后面的篇博客《Android Handler分析 (三) Looper详解和Handler其他知识》 中我们就会说到,其实它是在Looper中被调用的。

最后来个带CallBack的Handler创建方式:

private Handler handler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // 如果返回true,就不会执行下面的那个handleMessage()方法了
        return false;
    }
}) {
    @Override
    public void handleMessage(Message msg) {
        int what = msg.what; // 获取消息的what
        // 通过what对消息进行判断,不同消息不同的处理
        if (what == HANDLER_TEST_WHAT) {
            // 处理消息
            Toast.makeText(MainActivity.this, "hhh", Toast.LENGTH_SHORT).show();
        }
    }
};

接下一篇博客《Android Handler分析 (二) MessageQueue详解》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容