Android Handler

Love The Way You Lie 2022-12-28 01:46 212阅读 0赞

关联文章:
Android多线程(Handler篇)
Android多线程(AsyncTask篇)
Android多线程(HandlerThread篇)
Android多线程(IntentService篇)

先放流程图:
这里写图片描述
由于Android中的耗时操作不能放入主线程中,所以实现多线程是必须的。今天的主角是Handler,本文将从使用及源码来分析探索其奥秘。

使用

步骤

  1. 创建Handler对象,实现handlMessage()方法
  2. 创建Runnable线程
  3. 此时产生一个Looper,并自动创建一个消息队列MessageQueue()
  4. Looper轮询MessageQueue交给Handler
  5. Handler做处理

其中1、2、5为使用步骤,其他在后面分析源码时会讲到
使用方法

  1. public class MainActivity extends AppCompatActivity {
  2. @SuppressLint("HandlerLeak")
  3. Handler handler = new Handler(){
  4. @Override
  5. public void handleMessage(Message msg) {
  6. super.handleMessage(msg);
  7. switch (msg.what){
  8. case 1:
  9. // 处理事件
  10. break;
  11. }
  12. }
  13. };
  14. @Override
  15. protected void onCreate(Bundle savedInstanceState) {
  16. super.onCreate(savedInstanceState);
  17. setContentView(R.layout.activity_main);
  18. new Thread(new Runnable() {
  19. @Override
  20. public void run() {
  21. // 耗时操作
  22. Message message = new Message();
  23. message.what = 1;
  24. handler.sendMessage(message);
  25. }
  26. }).start();
  27. }
  28. }

使用就写这么多,接下来重点分析Handler原理。

原理分析

首先列一下将会讲到的几个对象

  1. ThreadLocal
  2. Looper
  3. MessageQueue
  4. Handler
  5. Message

ThreadLocal

关于他我看了几篇博客,很大一部分写的是错的,所以
注意

  • 他并不是解决共享对象的多线程访问问题的!!!
  • 他并没有创建对象的拷贝或副本!!!

目的:他只是为了保证每个线程都拥有同一个类的不同对象
实质:每个线程里都new了同一个类的对象
作用:你或许觉得这样做很傻,但是如果使用全局变量就要考虑线程安全问题,而线程安全是以消耗性能为前提的,所以这种设计可以说很巧妙
场景:每个线程都需要相同类型的对象,又各自独立,并且与他相关的操作不少时,如,Looper,ActivityThread,AMS 等

  1. private static final ThreadLocal tSession = new ThreadLocal();
  2. public static Session getSession() throws Exception{
  3. Session s = tSession.get();
  4. try{
  5. if(s==null){
  6. s = getSessionFactory.openSession();
  7. tSession.set(s);
  8. }
  9. }catch(Exception e){
  10. }
  11. return s;
  12. }

我们看一下ThreadLocal的源码,主要分析get()和set()

  1. public T get() {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null) {
  5. ThreadLocalMap.Entry e = map.getEntry(this);
  6. if (e != null) {
  7. @SuppressWarnings("unchecked")
  8. T result = (T)e.value;
  9. return result;
  10. }
  11. }
  12. return setInitialValue();
  13. }
  14. public void set(T value) {
  15. Thread t = Thread.currentThread();
  16. ThreadLocalMap map = getMap(t);
  17. if (map != null)
  18. map.set(this, value);
  19. else
  20. createMap(t, value);
  21. }
  22. ThreadLocalMap getMap(Thread t) {
  23. return t.threadLocals;
  24. }

set、get操作的都是ThreadLocalMap,key=当前线程,value=线程局部变量缓存值。可以看到get()实际上都是调用getMap(传入当前线程)来取得当前线程的ThreadLocalMap对象

  • set(),实际上是调用了ThreadLocalMap的set(),ThreadLocalMap的set()涉及到哈希散列算法,我会在之后博客里详细分析这里先不提算法的事。
  • get(),从当前线程中获取ThreadLocalMap,查询当前ThreadLocal变量实例对应的Entry,如果不为null,获取value,如果map为null,走初始化方法

由此看出不是如很多博主写的各线程用了同一个对象又相互独立那么神奇,只不过是用线程当做键在其中维护了一个私有变量而已。得到ThreadLocalMap后如何得到维护的变量呢,在这一句

ThreadLocalMap.Entry e = map.getEntry(this);
//this指代的ThreadLocal对象

所以过程就很清晰了,我们来总结一下:

  1. 声明一个全局公用的ThreadLocal实例作为key
  2. 在线程中new一个或取出已经存了的对象作为value
  3. 将此key-value放入ThreadLocalMap中
  4. 将当前线程作为key,ThreadLocalMap作为值放入ThreadLocal中

比较绕多读几遍就能明白了,再放一张图加深理解
这里写图片描述

Looper

两件事

  1. 创建消息队列
  2. 在队列中循环取消息

两个方法

  1. prepare()
  2. loop()

两个作用

  1. 保证当前线程只有一个looper和一个MessageQueue
  2. 从MessageQueue中取消息交给target的dispatchMessage

下面我们分析源码

#####构造方法

  1. private Looper(boolean quitAllowed) {
  2. mQueue = new MessageQueue(quitAllowed);
  3. mThread = Thread.currentThread();
  4. }

很容易看出当Looper构造时创建了一个消息队列

#####prepare()方法

  1. private static void prepare(boolean quitAllowed) {
  2. if (sThreadLocal.get() != null) {
  3. throw new RuntimeException("Only one Looper may be created per thread");
  4. }
  5. sThreadLocal.set(new Looper(quitAllowed));
  6. }

通过前面ThreadLocal的分析可知sThreadLocal.get()得到了Looper对象,当Looper存在是报错,不存在是创建一个存入ThreadLocal中,保证线程里有且只有一个Looper对象。

#####loop()

  1. /**
  2. * Run the message queue in this thread. Be sure to call
  3. * {@link #quit()} to end the loop.
  4. */
  5. public static void loop() {
  6. final Looper me = myLooper();
  7. if (me == null) {
  8. throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
  9. }
  10. final MessageQueue queue = me.mQueue;
  11. // Make sure the identity of this thread is that of the local process,
  12. // and keep track of what that identity token actually is.
  13. Binder.clearCallingIdentity();
  14. final long ident = Binder.clearCallingIdentity();
  15. for (;;) {
  16. Message msg = queue.next(); // might block
  17. if (msg == null) {
  18. // No message indicates that the message queue is quitting.
  19. return;
  20. }
  21. // This must be in a local variable, in case a UI event sets the logger
  22. final Printer logging = me.mLogging;
  23. if (logging != null) {
  24. logging.println(">>>>> Dispatching to " + msg.target + " " +
  25. msg.callback + ": " + msg.what);
  26. }
  27. final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
  28. final long traceTag = me.mTraceTag;
  29. if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
  30. Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
  31. }
  32. final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
  33. final long end;
  34. try {
  35. msg.target.dispatchMessage(msg);
  36. end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
  37. } finally {
  38. if (traceTag != 0) {
  39. Trace.traceEnd(traceTag);
  40. }
  41. }
  42. if (slowDispatchThresholdMs > 0) {
  43. final long time = end - start;
  44. if (time > slowDispatchThresholdMs) {
  45. Slog.w(TAG, "Dispatch took " + time + "ms on "
  46. + Thread.currentThread().getName() + ", h=" +
  47. msg.target + " cb=" + msg.callback + " msg=" + msg.what);
  48. }
  49. }
  50. if (logging != null) {
  51. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
  52. }
  53. // Make sure that during the course of dispatching the
  54. // identity of the thread wasn't corrupted.
  55. final long newIdent = Binder.clearCallingIdentity();
  56. if (ident != newIdent) {
  57. Log.wtf(TAG, "Thread identity changed from 0x"
  58. + Long.toHexString(ident) + " to 0x"
  59. + Long.toHexString(newIdent) + " while dispatching to "
  60. + msg.target.getClass().getName() + " "
  61. + msg.callback + " what=" + msg.what);
  62. }
  63. msg.recycleUnchecked();
  64. }
  65. }

1)拿到ThreadLocal中的Looper,若没有则prepare()
2)拿到Looper的MessageQueue
3)进入死循环,调用msg.target.dispatchMessage(msg),发给Handler
4)释放资源

MessageQueue

一个单链表结构的消息队列

Handler

#####构造函数

  1. public Handler(Callback callback, boolean async) {
  2. if (FIND_POTENTIAL_LEAKS) {
  3. final Class<? extends Handler> klass = getClass();
  4. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
  5. (klass.getModifiers() & Modifier.STATIC) == 0) {
  6. Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
  7. klass.getCanonicalName());
  8. }
  9. }
  10. mLooper = Looper.myLooper();
  11. if (mLooper == null) {
  12. throw new RuntimeException(
  13. "Can't create handler inside thread that has not called Looper.prepare()");
  14. }
  15. mQueue = mLooper.mQueue;
  16. mCallback = callback;
  17. mAsynchronous = async;
  18. }

构造时通过Looper.myLooper获取当前线程保存的Looper实例,再获取这个Looper的MessageQueue

#####发送消息

  1. public final boolean sendMessage(Message msg)
  2. {
  3. return sendMessageDelayed(msg, 0);
  4. }
  5. /**
  6. * Sends a Message containing only the what value.
  7. *
  8. * @return Returns true if the message was successfully placed in to the
  9. * message queue. Returns false on failure, usually because the
  10. * looper processing the message queue is exiting.
  11. */
  12. public final boolean sendEmptyMessage(int what)
  13. {
  14. return sendEmptyMessageDelayed(what, 0);
  15. }
  16. /**
  17. * Sends a Message containing only the what value, to be delivered
  18. * after the specified amount of time elapses.
  19. * @see #sendMessageDelayed(android.os.Message, long)
  20. *
  21. * @return Returns true if the message was successfully placed in to the
  22. * message queue. Returns false on failure, usually because the
  23. * looper processing the message queue is exiting.
  24. */
  25. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
  26. Message msg = Message.obtain();
  27. msg.what = what;
  28. return sendMessageDelayed(msg, delayMillis);
  29. }

发送消息时所有方法都实际调用了sendMessageAtTime

  1. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  2. MessageQueue queue = mQueue;
  3. if (queue == null) {
  4. RuntimeException e = new RuntimeException(
  5. this + " sendMessageAtTime() called with no mQueue");
  6. Log.w("Looper", e.getMessage(), e);
  7. return false;
  8. }
  9. return enqueueMessage(queue, msg, uptimeMillis);
  10. }

是获取MessageQueue调用enqueueMessage();
#####接下来看enqueueMessage中

  1. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  2. msg.target = this;
  3. if (mAsynchronous) {
  4. msg.setAsynchronous(true);
  5. }
  6. return queue.enqueueMessage(msg, uptimeMillis);
  7. }

将msg的target属性赋值为Handler自己,实现了Message与Handler的绑定,并调用了MessageQueue中的enqueueMessage()方法

  1. boolean enqueueMessage(Message msg, long when) {
  2. if (msg.target == null) {
  3. throw new IllegalArgumentException("Message must have a target.");
  4. }
  5. if (msg.isInUse()) {
  6. throw new IllegalStateException(msg + " This message is already in use.");
  7. }
  8. synchronized (this) {
  9. if (mQuitting) {
  10. IllegalStateException e = new IllegalStateException(
  11. msg.target + " sending message to a Handler on a dead thread");
  12. Log.w(TAG, e.getMessage(), e);
  13. msg.recycle();
  14. return false;
  15. }
  16. msg.markInUse();
  17. msg.when = when;
  18. Message p = mMessages;
  19. boolean needWake;
  20. if (p == null || when == 0 || when < p.when) {
  21. // New head, wake up the event queue if blocked.
  22. msg.next = p;
  23. mMessages = msg;
  24. needWake = mBlocked;
  25. } else {
  26. // Inserted within the middle of the queue. Usually we don't have to wake
  27. // up the event queue unless there is a barrier at the head of the queue
  28. // and the message is the earliest asynchronous message in the queue.
  29. needWake = mBlocked && p.target == null && msg.isAsynchronous();
  30. Message prev;
  31. for (;;) {
  32. prev = p;
  33. p = p.next;
  34. if (p == null || when < p.when) {
  35. break;
  36. }
  37. if (needWake && p.isAsynchronous()) {
  38. needWake = false;
  39. }
  40. }
  41. msg.next = p; // invariant: p == prev.next
  42. prev.next = msg;
  43. }
  44. // We can assume mPtr != 0 because mQuitting is false.
  45. if (needWake) {
  46. nativeWake(mPtr);
  47. }
  48. }
  49. return true;
  50. }

对前一个方法进行补充,把msg放入MessageQueue中,这时候轮询取消息(在前面Looper已经分析),调用dispatchMessage()

  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null) {
  3. handleCallback(msg);
  4. } else {
  5. if (mCallback != null) {
  6. if (mCallback.handleMessage(msg)) {
  7. return;
  8. }
  9. }
  10. handleMessage(msg);
  11. }
  12. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

最终调用handlerMessage()方法

  1. /**
  2. * Subclasses must implement this to receive messages.
  3. */
  4. public void handleMessage(Message msg) {
  5. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

一个空方法,在这里处理操作,end

##总结

  1. Looper.prepare()在本线程中存入ThreadLocalMap中一个Looper并创建一个MessageQueue对象
  2. Looper.loop()循环取Message中的消息,回调msg.target.dispatchMessage(msg)
  3. Handler构造得到Looper并与MessageQueue关联
  4. Handler.sendMessage会给msg.target赋值为自己并加入MessageQueue中
  5. 重写handlMessage()处理

发表评论

表情:
评论列表 (有 0 条评论,212人围观)

还没有评论,来说两句吧...

相关阅读

    相关 AndroidHandler 总结

    <一> Handler的定义: 主要接受子线程发送的数据, 并用此数据配合主线程更新UI. 解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程

    相关 androidhandler

    Android程序中一些操作是不能放在activity中的,因为非常耗时,如下载过程。这时,需要使用handler,即重新启动一个与activity并行的线程,下面是使用han