Handler机制分析之一

1, 基本概念

Handler主要做什么?机制是怎样的?有其他相同或者类似的机制吗?

我们知道,Binder主要用于跨进程通信,Handler呢?主要用于进程内部进行通信,或者说进程内部不同线程之间进行通信,即是不同线程之间互相发送消息。

 

额外知识:

libcore/libart/src/main/java/java/lang/Thread.java

libcore/luni/src/main/java/java/lang/ThreadLocal.java

Thread类有一个变量专门用于存储线程的ThreadLocal的数据。

ThreadLocal是做啥的呢?  啥都不说,直接上例子:

private ThreadLocalmBooleanThreadLocal = new ThreadLocal(Boolean);

mBooleanThreadLocal.set(true);
Log.d(TAG, [Thread#main]mBooleanThreadLocal= + mBooleanThreadLocal.get());

new Thread(Thread#1) {
	@Override
	public void run() {
		mBooleanThreadLocal.set(false);
		Log.d(TAG, [Thread#1]mBooleanThreadLocal= + mBooleanThreadLocal.get());
	};
}.start();

new Thread(Thread#2) {
	@Override
	public void run() {
		Log.d(TAG, [Thread#2]mBooleanThreadLocal= + mBooleanThreadLocal.get());
	};
}.start();

输出结果:

D/TestActivity(8676):[Thread#main]mBooleanThreadLocal=true
D/TestActivity(8676):[Thread#1]mBooleanThreadLocal=false
D/TestActivity(8676):[Thread#2]mBooleanThreadLocal=null

很神奇吧,可以这样理解, ThreadLocal虽然作为主线程的变量,但是在不同的子线程中都有各自一个独立的副本,彼此之间互不干扰。也就是说ThreadLocal是基于线程的。

那么Handler相关消息概念中为啥要使用ThreadLocal呢?

对于Handler来说,它需要获取当前线程的Looper,通过ThreadLocal就可以轻松实现Looper在线程中的存取.如果不采用ThreadLocal,那么系统就必须提供一个全局的哈希表供Handler查找指定线程的Looper,这样一来就必须提供一个类似于LooperManager的类了,这就是ThreadLocal的好处。

所以, 当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。比如比如Looper、ActivityThread以及AMS等都用到了ThreadLocal。

handler主要做两件事情,1,(延时)发送消息;2,(延时)运行一个线程,最后都是以消息的形式封装处理。

主要包括三个类:handler, Looper, MessageQueue,还有Message

消息的表示:Message

消息队列:MessageQueue

消息循环,用于循环取出消息进行处理:Looper

消息处理,消息循环从消息队列中取出消息后要对消息进行处理:Handler

一个进程仅有一个looper,looper中包含一个消息链表(按照时间排序).

同一个handler可以对应(处理)不同的消息(Message)

2, 流程图

在apk启动时,会创建一个主线程,在ActivityThread的main函数中

public static void main(String[] args) {
•••
Looper.prepareMainLooper();
•••
Looper.loop();
}

流程如下:



2.1 Looper初始化

private static void prepare(boolean quitAllowed) {
        •••
        sThreadLocal.set(new Looper(quitAllowed));
// 构造当前线程的Looper并且保存在sThreadLocal中
}
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);// 构造消息队列 MessageQueue
        mThread = Thread.currentThread();
    }
MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();// 该函数是一个C/C++层函数
    }

可以看看mQuitAllowed的定义:

// True if the message queue can be quit.
    private final boolean mQuitAllowed;

该变量为false时表示不可以退出,只有主线程在调用prepare函数时传入false。也就是说仅有主线程不允许退出。

2.2 获取当前线程的Looper

直接看myLooper函数:

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();// 如前所述,可以直接得到当前线程的Looper  一般指主线程
    }

获取消息队列

public static @NonNull MessageQueue myQueue() {
        return myLooper().mQueue;
    }

3, 消息入队列

普通用法:

1,(延时)发送消息

protected static final int MSG_send = 0; // 标记
private static int DELAY_TIME = 700; // 延迟时间 单位: ms

Message msga = mHandler.obtainMessage();
msga.what = MSG_send;
mHandler.sendMessageDelayed(msga, DELAY_TIME);

private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_send:
                 •••
                 break;
			 }
            }

        };

2, (延时)运行一个线程

private Handler mHandler = new Handler();
private static int AUTO_TEST_DELAY_TIME = 700;

mHandler.postDelayed(mRunnable, AUTO_TEST_DELAY_TIME);

private Runnable mRunnable = new Runnable() {
		@Override
		public void run() {
		•••
		}
	};

这2种方法的机制,原理是完全相同的。

还有一些其他的用法,以及下面类的构造函数和方法,便于不偏离主线,只讲紧要部分。

3.1 Handler

public Handler(Callback callback, boolean async) { // 通常callback=null, async=false
        mLooper = Looper.myLooper();
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

由Handler的构造函数以及上面分析可知,1个线程仅有1个Looper对象,并且一个Looper仅有一个消息队列MessageQueue。

3.2 Message

首先看Message类:  实现Parcelable说明是可以封装和打包的

public final class Message implements Parcelable {

重要变量:

public int what;  // 不同消息的区分标志
Handler target;
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
long when;  // 消息延迟的时间
Message next; // 消息的下一条消息,这样就可以组成一个单向链表

Message的获取(初始化)过程:


public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h; // Message 的目标就是获取Message的Handler
        return m;
}
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();  // 新建一个Message对象
    }

3.3消息队列MessageQueue

重要变量和函数:

Message mMessages; // 该变量表示第一个消息,根据next可以遍历所有消息,也可以说它是一条消息链表
Runnable callback;
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);

所谓的消息入队列就是将新建的Message插入mMessages中,在mMessages找到合适的位置。那么mMessages中消息排序的依据是什么呢?  延迟时间


从使用方法上可以看到,延迟运行线程多了一个步骤: postDelayed

public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
}

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

主要是为callback 变量赋值,当发送普通消息时, callback=null ,仅运行线程时才有值,这在消息的处理时有一点差别。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

关键是这句  SystemClock.uptimeMillis() + delayMillis以系统的绝对时间为基准,对消息进行插入操作。

boolean enqueueMessage(Message msg, long when) {
        synchronized (this) {
            if (mQuitting) {
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p; // 还未有消息,作为链表的表头
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;   // 找到合适的位置并插入链表
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

4,消息循环


Looper只是个简单的类而已,它虽然提供了循环处理方面的成员函数loop(),却不能自己凭空地运行起来,而只能寄身于某个真实的线程。

在线程中,调用loop方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

public static void loop() {
        final Looper me = myLooper(); // 获取当前线程的Looper
        final MessageQueue queue = me.mQueue; // 获取消息链表

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        for (;;) {
            Message msg = queue.next(); // 直接获取下一条消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            // This must be in a local variable, in case a UI event sets the logger
            
            msg.target.dispatchMessage(msg);// 分发处理消息
            msg.recycleUnchecked(); // 消息本身的处理,进行回收
        }
    }

loop方法主要做3件事情:

1,从消息链表中取出下一条消息。

2,将消息分发给Handler去处理(msg.target 的值就是handler)

3,处理完消息之后,消息本身也需要做后续处理。

 

分发消息比较简单,上面流程图中的步骤22到25.

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg); //  运行线程时调用
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//  发送消息时调用
        }
    }
private static void handleCallback(Message message) {
        message.callback.run();
    }

分别调用自写的handleMessage 方法和Runnable 的run方法

private class H extends Handler {
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值