Android Handle原理(Looper,Handler和Message)三者关系案例详解

作者:安辉就是我 时间:2023-08-25 22:51:47 

介绍

前面的内容对Handler做了介绍,也讲解了如何使用handler,但是我们并不知道他的实现原理。本文从源码的角度来分析如何实现的。

首先我们得知道Handler,Looper,Message Queue三者之间的关系

- Handler封装了消息的发送,也负责接收消。内部会跟Looper关联。
- Looper 消息封装的载,内部包含了MessageQueue,负责从MessageQueue取出消息,然后交给Handler处理
- MessageQueue 就是一个消息队列,负责存储消息,有消息过来就存储起来,Looper会循环的从MessageQueue读取消息。

源码分析

当我们new一个Handler对象的时候,看看他的构造方法里面做了什么.


public Handler(Callback callback, boolean async) {
   if (FIND_POTENTIAL_LEAKS) {
       final Class<? extends Handler> klass = getClass();
       if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
               (klass.getModifiers() & Modifier.STATIC) == 0) {
           Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
               klass.getCanonicalName());
       }
   }

mLooper = Looper.myLooper();
   if (mLooper == null) {
       throw new RuntimeException(
           "Can't create handler inside thread that has not called Looper.prepare()");
   }
   mQueue = mLooper.mQueue;
   mCallback = callback;
   mAsynchronous = async;
}

从源码中我们看到他会调用Looper.myLooper方法获取一个Looper对象,然后从Looper对象获取到MessageQueue对象。

Looper myLooper()

跟进去看看Looper.myLooper()方法做了什么。这是一个静态方法,可以类名.方法名直接调用。


public static @Nullable Looper myLooper() {
   return sThreadLocal.get();
}

这个方法里面就一行代码,从sThreadLocal中获取一个Looper对象,sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。底层是ThreadLocalMap,既然是Map类型那肯定得先set一个Looper对象,然后我们才能从sThreadLocal对象里面get一个Looper对象。

ActivityThread main()

说到这里得给大家介绍一个新的类ActivityThread,ActivityThread类是Android APP进程的初始类,它的main函数是这个APP进程的入口。我们看看这个main函数干了什么事情。


public static final void main(String[] args) {
       ------
       Looper.prepareMainLooper();
       if (sMainThreadHandler == null) {
           sMainThreadHandler = new Handler();
       }

ActivityThread thread = new ActivityThread();
       thread.attach(false);

if (false) {
           Looper.myLooper().setMessageLogging(new
                   LogPrinter(Log.DEBUG, "ActivityThread"));
       }

Looper.loop();
       -----
}

在第二行代码调用Looper.prepareMainLooper()方法,第13行调用了Looper.loop()方法。

Looper prepareMainLooper()

继续跟进Looper.prepareMainLooper()方法,在这个方法中第一行代码调用了内部的prepare方法。prepareMainLooper有点像单例模式中的getInstance方法,只不过getInstance会当时返回一个对象,而prepareMainLooper会新建一个Looper对象,存储在sThreadLocal中。


public static void prepareMainLooper() {
   prepare(false);
   synchronized (Looper.class) {
       if (sMainLooper != null) {
           throw new IllegalStateException("The main Looper has already been prepared.");
       }
       sMainLooper = myLooper();
   }
}

Looper prepare()

继续跟进prepare方法,看第5行代码,新建了一个Looper对象,调用sThreadLocal.set方法把Looper对象保存起来。看到这里我想聪明的你们肯定明白了为什么new Handler对象的时候调用Looper.myLooper()方法能从sThreadLocal对象中取到Looper对象。


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));
}

Looper 构造方法

文章开头我们就讲到Looper内部包含了MessageQueue,其实就是在new Looper对象的时候就new了一个MessageQueue对象。


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

Looper loop()

ActivityThread类main方法中调用了Looper的两个方法,前面我们解释了prepareMainLooper(),现在来看第二个方法loop()。


public static void loop() {
   final Looper me = myLooper();//获取Looper对象
   if (me == null) {
       throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
   }
   final MessageQueue queue = me.mQueue;//从Looper对象获取MessageQueue对象

// Make sure the identity of this thread is that of the local process,
   // and keep track of what that identity token actually is.
   Binder.clearCallingIdentity();
   final long ident = Binder.clearCallingIdentity();

for (;;) {//死循环  一直从MessageQueue中遍历消息
       Message msg = queue.next(); // might block
       if (msg == null) {
           return;
       }

// This must be in a local variable, in case a UI event sets the logger
       final Printer logging = me.mLogging;
       if (logging != null) {
           logging.println(">>>>> Dispatching to " + msg.target + " " +
                   msg.callback + ": " + msg.what);
       }

final long traceTag = me.mTraceTag;
       if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
           Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
       }
       try {
           //调用handler的dispatchMessage方法,把消息交给handler处理
           msg.target.dispatchMessage(msg);
       } finally {
           if (traceTag != 0) {
               Trace.traceEnd(traceTag);
           }
       }

if (logging != null) {
           logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
       }

// Make sure that during the course of dispatching the
       // identity of the thread wasn't corrupted.
       final long newIdent = Binder.clearCallingIdentity();
       if (ident != newIdent) {
           Log.wtf(TAG, "Thread identity changed from 0x"
                   + Long.toHexString(ident) + " to 0x"
                   + Long.toHexString(newIdent) + " while dispatching to "
                   + msg.target.getClass().getName() + " "
                   + msg.callback + " what=" + msg.what);
       }

msg.recycleUnchecked();
   }
}

这个方法的代码呢比较多。我都给代码加上了注释。其实就是一个死循环,一直会从MessageQueue中取消息,如果取到了消息呢,会执行msg.target.dispatchMessage(msg)这行代码,msg.target就是handler,其实就是调用handler的dispatchMessage方法,然后把从MessageQueue中取到的message传入进去。

Handler dispatchMessage()


public void dispatchMessage(Message msg) {
   //如果callback不为空,说明发送消息的时候是post一个Runnable对象
   if (msg.callback != null) {
       handleCallback(msg);
   } else {
       if (mCallback != null) {//这个是用来拦截消息的
           if (mCallback.handleMessage(msg)) {
               return;
           }
       }
       handleMessage(msg);//最终调用我们重写的handleMessage方法
   }
}

这个方法对消息做最后处理,如果是post类型调用handleCallback方法处理,如果是sendMessage发送的消息。看我们有没有拦截消息,如果没有最终调用handleMessage方法处理。

Handler handleCallback()

看到这里我们知道为什么post一个Runnable对象,run方法执行的代码在主线程了吧,因为底层根本就没有开启线程,就只是调用了run方法而已。


private static void handleCallback(Message message) {
   message.callback.run();
}

前面我们从创建handler对象开始,以及创建Looper,创建MessageQueue的整个流程,现在来分析下,当我们调用post以及sendMessage方法时,怎么把Message添加到MessageQueue。

Handler post()

调用了getPostMessage方法,把Runnable传递进去。


public final boolean post(Runnable r)
{
  return  sendMessageDelayed(getPostMessage(r), 0);
}

Handler getPostMessage()

首先调用Message.obtain()方法,取出一个Message对象,这个方法之前有讲过,然后把Runnable对象赋值了Message对象的callback属性。看到这里我们也明白了dispatchMessage方法为什么要先判断callback是否为空了吧。


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

Handler enqueueMessage()

在post方法里面调用了sendMessageDelayed方法,其实最终调用的是enqueueMessage方法,所以我这里就直接看enqueueMessage方法源码。第一行代码就把handler自己赋值给messgae对象的target属性。然后调用MessageQueue的enqueueMessage方法把当前的Messgae添加进去。


private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
   msg.target = this;
   if (mAsynchronous) {
       msg.setAsynchronous(true);
   }
   return queue.enqueueMessage(msg, uptimeMillis);
}

来源:https://blog.csdn.net/lowprofile_coding/article/details/72580044

标签:Android,Handle
0
投稿

猜你喜欢

  • Java实例讲解多态数组的使用

    2021-08-30 19:34:46
  • 微服务通过Feign调用进行密码安全认证操作

    2023-07-30 02:43:38
  • Java map存放数组并取出值代码详解

    2023-06-27 23:32:18
  • C# WORD操作实现代码

    2023-11-20 05:16:25
  • Java编程Webservice指定超时时间代码详解

    2023-11-02 23:17:12
  • SpringBoot-RestTemplate实现调用第三方API的方式

    2022-12-29 09:49:56
  • JVM中的flag设置详解

    2022-08-11 01:37:20
  • java 一个类实现两个接口的案例

    2023-08-09 12:24:35
  • Java上传视频实例代码

    2023-06-24 04:17:45
  • 详解java中jvm虚拟机栈的作用

    2022-06-06 14:08:44
  • Java反射机制在Spring IOC中的应用详解

    2023-11-10 14:09:32
  • C# SMTP发送邮件的示例

    2021-06-20 12:36:10
  • 获取Android手机中所有短信的实现代码

    2023-08-04 16:55:30
  • Android自定义ListView实现下拉刷新上拉加载更多

    2021-08-20 21:17:04
  • Java的外部类为什么不能使用private和protected进行修饰的讲解

    2023-06-29 00:45:57
  • 浅谈Android View滑动冲突的解决方法

    2021-12-17 06:47:09
  • 浅谈Java中Map和Set之间的关系(及Map.Entry)

    2023-08-25 02:23:48
  • Java利用反射对list对象做过滤

    2023-12-23 07:39:17
  • 基于Class.forName()用法及说明

    2021-06-20 19:09:30
  • EditText监听方法,实时的判断输入多少字符

    2021-08-16 11:32:36
  • asp之家 软件编程 m.aspxhome.com