
本文共 6060 字,大约阅读时间需要 20 分钟。
【1】handler在Android 开发中到底有什么用?
首先我们要先搞懂handler到底被设计出来有什么用。——一种东西被设计出来肯定就有它存在的意义,而Handler的意义就是切换线程。(线程间通信) 常用的场景就是:网络交互后切换到主线程进行UI更新。
(1) 为什么不直接在子线程更新UI?
Android的UI是线程不安全的,肯定不能同时多个线程操作UI线程。如果加锁又会降低UI的效率,所以通常不能在子线程更新UI。
【2】handler的简单使用如下:
public class MainActivity extends AppCompatActivity { //传递的数据 Bundle bundle = new Bundle(); bundle.putString("msg", "传递我这个消息"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //数据的接收 final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 0x11) { //这里模拟就获取到闯过来的数据就行了 Bundle bundle = msg.obj; String date = bundle.getString("msg"); } } }; new Thread(new Runnable() { @Override public void run() { //发送数据 Message message = Message.obtain(); message.obj=bundle; message.what = 0x11; handler.sendMessage(message); } }).start(); }
在主线程中创建handler对象,并且重写handleMessage方法,在子线程中发送message对象。
- 注意:不建议直接new Message,Message内部保存了一个缓存的消息池,我们可以用obtain从缓存池获得一个消息,Message使用完后系统会调用recycle回收,如果自己new很多Message,每次使用完后系统放入缓存池,会占用很多内存的。
【3】handler 原理分析:
使用Handler方式进行异步消息处理主要由Message,Handler,MessageQueue,Looper四部分组成:
-
(1)Message,线程之间传递的消息,用于不同线程之间的数据交互。Message中的what字段用来标记区分多个消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。
-
(2)Handler,用于发送和处理消息。其中的sendMessage()用来发送消息,handleMessage()用于消息处理,进行相应的UI操作。
-
(3)MessageQueue,消息队列(先进先出),用于存放Handler发送的消息,一个线程只有一个消息队列。
-
(4)Looper,可以理解为消息队列的管理者,当发现MessageQueue中存在消息,Looper就会将消息传递到handleMessage()方法中,同样,一个线程只有一个Looper。
Handler实现原理如下图:



【4】源码分析
-
(1)handler 源码分析:
send
和post
开头函数,都是发送消息的,最终都会调用到sendMessageAtTime
这个函数,而这个函数又会调用到handler里面的enqueueMessage
函数,这个函数又会调用到MessageQueue里面的enqueueMessage
函数。然后我们来分析MessageQueue。 -
(2)MessageQueue 源码分析:
MessageQueue主要作用就两个:1.存放消息进入MessageQueue。2.从这个消息队列中取出一个Message。
所以来到存放函数:enqueueMessage
boolean enqueueMessage(Message msg, long when) { ...... 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; }........}
直接看到else里面的for死循环,大概意思就是如果要存放的这个Message的when要比当前的这个Message的when要小的话就直接将要存放的Message放到当前这个Message的前一个,如果不是那么久依次向下找。
说完入队,我们来看看Message如何取出:next函数
Message next() { ... ... ... if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } ... ... ... }
就从当前的MessageQueue的头部取出就可以了。
-
(3)Looper源码分析:
首先我们要知道每个线程都只有一个Looper实例,这个是怎么实现的呢?
要想清楚这个问题,我们就必须知道ThreadLocal
下面就具体说说ThreadLocal运行机制。 //ThreadLocal.javapublic T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue();}public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value);}
从ThreadLocal
类中的get
和set
方法可以大致看出来,有一个ThreadLocalMap
变量,这个变量存储着键值对形式的数据。
-key
为this
,也就是当前ThreadLocal
变量。
value
为T
,也就是要存储的值。
然后继续看看ThreadLocalMap哪来的,也就是getMap方法:
//ThreadLocal.javaThreadLocalMap getMap(Thread t) { return t.threadLocals;}//Thread.javaThreadLocal.ThreadLocalMap threadLocals = null;
原来这个ThreadLocalMap
变量是存储在线程类Thread
中的。
所以ThreadLocal
的基本机制就搞清楚了。
在每个线程中都有一个threadLocals
变量,这个变量存储着ThreadLocal
和对应的需要保存的对象
。
这样带来的好处就是,在不同的线程,访问同一个ThreadLocal
对象,但是能获取到的值却不一样。
挺神奇的是不是,其实就是其内部获取到的Map
不同,Map
和Thread
绑定,所以虽然访问的是同一个ThreadLocal
对象,但是访问的Map
却不是同一个,所以取得值也不一样。
ThreadLocal原理见下图(个人理解,如果有误,欢迎指正):
static final ThreadLocalsThreadLocal = new ThreadLocal ();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));}public static @Nullable Looper myLooper() { return sThreadLocal.get();}
发表评论
最新留言
关于作者
