Android异步消息处理机制实现原理详解

消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable)

Looper不断从MessageQueue消息队列中取出一个Message,然后传给Handle,如此循环往复,如果队列为空,那么它会进入休眠。

这些类的主要变量

Looper.java

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
  private static Looper sMainLooper; // guarded by Looper.class

  final MessageQueue mQueue;
  final Thread mThread;

Handler.java

final MessageQueue mQueue;
  final Looper mLooper;
  final Callback mCallback;
  final boolean mAsynchronous;
  IMessenger mMessenger;

Message.java

Handler target;每个消息只能对应一个handler
Runnable callback;回调接口

MessageQueue.java

Message mMessages;

Runnable是一个空接口类,没有变量

上一个书上的图:

Handler和Thread没有直接关系,但对应关系可以推理得到

每个Thread只对应一个Looper;

每个Looper只对应一个MessageQueue;

每个MessageQueue对应N个Message,每个Message只对应一个Handler

==》每个Thread对应N个Handler。

Handler是”真正处理事情“的地方,作用:处理消息,将Message压入MessageQueue中

带着一个问题看源码:创建handler对象的线程(ui/主线程除外)为什么,必须先调用Looper.prepare() ?

public Handler() {
    this(null, false);
  }
public Handler(Callback callback) {
    this(callback, false);
  }
public Handler(Looper looper) {
    this(looper, null, false);
  }
 public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
  }
 public Handler(boolean async) {
    this(null, async);
  }
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;
  }
 public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
  }

初始化handler对象时(构造方法是Handler(),Handler(Callback callback))都间接调用Handler(Callback callback, boolean async)构造方法

主要代码是Looper.myLooper();

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//这是在Looper类中的定义
public static Looper myLooper() {
    return sThreadLocal.get();//从当前线程中获得looper对象
  }
public static void prepare() {
prepare(true);
}

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.prepare()方法为当前线程设置looper对象才可以通过Looper.myLooper()方法返回looper对象,这样在非UI线程创建handler对象时才不会报错。"Can't create handler inside thread that has not called Looper.prepare()"

ps:prepare(boolean quitAllowed)(这个不用我们关心,略过。。)

这个quitAlowed参数是定义消息队列用了,看的源代码是android4.4

Looper.javaprivate Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mRun = true;
    mThread = Thread.currentThread();
  }MessageQueue.java
// True if the message queue can be quit.  private final boolean mQuitAllowed;//true消息队列可以被quit,false消息队列不能被quit。

主线程/UI线程的MessageQueue不能被销毁掉。看源码(销毁调用Looper.quit())

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

偏离太远了

所以得出结论:创建handler对象的线程(ui/主线程除外),必须先调用Looper.prepare()

Handler作用1:处理消息

在Looper类中处理消息是通过msg.target.dispatchMessage(msg);target就是handler对象(Message类的内部变量Handler target)将消息转发到处理消息的对应的handler对象上,然后这个target即handler对象会在处理消息前做一个检查

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {//如果msg有绑定callback回调接口Runaable不为空,则执行Runnable的run方法
      handleCallback(msg);
    } else {
      if (mCallback != null) {//如果handler的内置接口类Callback不为空,则执行boolean handleMessage(Message msg)这个方法
        if (mCallback.handleMessage(msg)) {执行完成则return
          return;
        }
      }
      handleMessage(msg);//最后才执行handler本身的方法
    }
  }
 private static void handleCallback(Message message) {    message.callback.run();  }
public interface Callback {//handler的内置接口类Callback
public boolean handleMessage(Message msg);
  }

Handler作用2:将Message压入MessageQueue中

handler中提供的很多发送message的方法,除了sendMessageAtFrontOfQueue()方法(直接调用enqueueMessage(queue, msg, 0);)之外,其它的发送消息方法最终都会辗转调用到sendMessageAtTime()方法

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
      RuntimeException e = new RuntimeException(
          this + " sendMessageAtTime() called with no mQueue");
      Log.w("Looper", e.getMessage(), e);
      return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
  }

sendMessageAtTime()方法也是调用Handler中的enqueueMessage(queue, msg, uptimeMillis)方法

和sendMessageAtFrontOfQueue()方法两者最后都会调用enqueueMessage(queue, msg, uptimeMillis)方法

区别是需要延迟uptimeMillis时间后才将Message压入MessageQueue中

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//给msg的target赋值为handler自身然后加入MessageQueue中
    if (mAsynchronous) {
      msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
  }

最终所有的方法都是调用MessageQueue中的enqueueMessage(msg, uptimeMillis);方法,是不是感觉两个方法差不多啊,注意参数!!

MessageQueue的使用是在Looper中

Handler的作用整理完毕(好像我现在已经可以把Handler源码完整默写下来了。哈哈^.^记忆力真不行)

Looper类

作用:与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。

对于Looper主要是prepare()和loop()两个方法

prepare()将普通线程转化为looper线程,

loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
      throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    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.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
      Message msg = queue.next(); // might block
      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
      Printer logging = me.mLogging;
      if (logging != null) {
        logging.println(">>>>> Dispatching to " + msg.target + " " +
            msg.callback + ": " + msg.what);
      }

      msg.target.dispatchMessage(msg);

      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.recycle();
    }
  }

27行就是上面提到了,handler进行消息处理的关键代码了

看着上面的分析很复杂,总结下

1、首先Looper.prepare()为在当前线程中保存一个Looper实例(sThreadLocal.set()),然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android编程实现异步消息处理机制的几种方法总结

    本文实例讲述了Android编程实现异步消息处理机制的几种方法.分享给大家供大家参考,具体如下: 1.概述 Android需要更新ui的话就必须在ui线程上进行操作.否则就会抛异常. 假如有耗时操作,比如:在子线程中下载文件,通知ui线程下载进度,ui线程去更新进度等,这个时候我们就需要用到异步消息处理. 一.什么是Handler Handler是Android提供用来异步更新UI的一套机制,也是一套消息处理机制,可以用它来发送消息,也可以用它来接收消息. 二.为什么使用Handler Andr

  • Android图片异步加载框架Android-Universal-Image-Loader

    Android-Universal-Image-Loader是一个图片异步加载,缓存和显示的框架.这个框架已经被很多开发者所使用,是最常用的几个Android开源项目之一,主流的应用,随便反编译几个,都可以见到它的身影.淘宝,天猫,Facebook,京东商城等都用到了这个项目. 该项目的Github地址链接 运行流程: 每一个图片的加载和显示任务都运行在独立的线程中,除非这个图片缓存在内存中,这种情况下图片会立即显示.如果需要的图片缓存在本地,它会开启一个独立的线程队列.如果在缓存中没有正确的图

  • Android开发实现ListView异步加载数据的方法详解

    本文实例讲述了Android开发实现ListView异步加载数据的方法.分享给大家供大家参考,具体如下: 1.主Activity public class MainActivity extends Activity { private ListView listView; private ArrayList<Person> persons; private ListAdapter adapter; private Handler handler=null; //xml文件的网络地址 final

  • Android开发之HttpClient异步请求数据的方法详解【附demo源码下载】

    本文实例讲述了Android开发之HttpClient异步请求数据的方法.分享给大家供大家参考,具体如下: 前面一篇Android开发笔记之:AsyncTask的应用较为详细的讲述了Asynctask的原理与应用,这里来结合使用一下HttpClient与Asynctask~ 代码编写如下: 服务器代码我就不写出来了,就是一个用户名和密码~ 1.写一个类HttpClientUtil,来实现HttpClient对象的创建并且返回HttpResponse对象 public class HttpClie

  • Android上传多张图片的实例代码(RxJava异步分发)

    学习RxJava有一段时间了,一直在考虑怎么使用,如何在项目中合理运用它.在android很多项目中,都会存在图片上传,下面我介绍如何用Rxjava异步上传多张图片. 一,用到的框架 compile 'top.zibin:Luban:1.0.9'//图片压缩 compile 'org.xutils:xutils:3.3.34'//网络请求 compile 'io.reactivex:rxandroid:1.1.0'//rxandroid compile 'io.reactivex:rxjava:

  • Android 异步任务 设置 超时使用handler更新通知功能

    Android 使用AsyncTask设置请求超时的注意事项 final AsyncTaskTools task = new AsyncTaskTools(dialog, doTask, result, context); mTask.execute("蓝牙读卡"); new Thread() { public void run() { try { /** * 在这里你可以设置超时时间, * 切记:这段代码必须放到线程中执行,因为不放单独的线程中执行的话该方法会冻结UI线程 * 直接导

  • Android使用OkHttp进行网络同步异步操作

    OkHttp是一个Java和Android的HTTP和HTTP/2的客户端,负责发送HTTP请求以及接受HTTP响应. 一.使用OkHttp OkHttp发送请求后,可以通过同步或异步地方式获取响应.下面就同步和异步两种方式进行介绍. 1.1.同步方式 发送请求后,就会进入阻塞状态,知道收到响应.下面看一个下载百度首页的例子: OkHttpClient client = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).bui

  • Android异步方法以同步方式实现

    Android强制异步转同步方法,供大家参考,具体内容如下 Android系统中规定耗时任务需要在异步线程中进行,特别是网络请求必须在异步线程中进行否则会抛出NetworkOnMainThreadException,但是在一些特殊的情况我们需要保证在获得到网络请求结果之后在进行余下操作,这时候便需要在UiThread中进行相关操作. 这样就需要用到线程阻塞的原理,这里可以借助Callable或FutureTask来实现. public static String getSyncBusiness(

  • Android异步消息处理机制实现原理详解

    消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable) Looper不断从MessageQueue消息队列中取出一个Message,然后传给Handle,如此循环往复,如果队列为空,那么它会进入休眠. 这些类的主要变量 Looper.java static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static

  • Android中的LeakCanary的原理详解

    场景:最新的leakCanary2.8.1: debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' 原理:首先就是我们在引入最新的依赖包,什么都不用干了,因为他的初始化在清单文件中注册了contentProvider(),把初始化放到了这里面的onCreate()去初始化了,在初始化的过程中,他会用application监听观察对象activity.fragment等对象的生命周期的变化,当执行销毁的生命周期

  • android线程消息机制之Handler详解

    android线程消息机制主要由Handler,Looper,Message和MessageQuene四个部分组成.平常在开发中,我们常用来在子线程中通知主线程来更新,其实整个安卓生命周期的驱动都是通过Handler(ActivityThread.H)来实现的. 首先我们先介绍这四个类的作用: Handler:消息的发送者.负责将Message消息发送到MessageQueue中.以及通过Runnable,Callback或者handleMessage()来实现消息的回调处理 Looper:是消

  • Android动态加载Activity原理详解

    activity的启动流程 加载一个Activity肯定不会像加载一般的类那样,因为activity作为系统的组件有自己的生命周期,有系统的很多回调控制,所以自定义一个DexClassLoader类加载器来加载插件中的Activity肯定是不可以的. 首先不得不了解一下activity的启动流程,当然只是简单的看一下,太详细的话很难研究清楚. 通过startActivity启动后,最终通过AMS进行跨进程回调到ApplicationThread的scheduleLaunchActivity,这时

  • Javascript模块化机制实现原理详解

    1. 概述 js发展初期暴露了其缺陷:缺乏模块,后来提出了commonJS规范来规范其模块的规范.作为JavaScript新手,发现对于其JavaScript的模块机制,不是很理解.我查阅了一些资料整理了JavaScript CommonJS的原理和机制. 2. JavaScript 2.1 无后端的项目 这类项目不能使用CommonJS的模块规范,这是我起初所犯的错误.在没有es6被大多数浏览器支持的时候,js文件充斥着var和function,由此引来了命名冲突和污染,使得JavaScrip

  • Android HandlerThread的使用及原理详解

    一.HandlerThread的含义 HandlerThread能够新建拥有Looper的线程.这个Looper能够用来新建其他的Handler.(线程中的Looper)需要注意的是,新建的时候需要被回调. 二.HandlerThread的用法 一般情况下,我们会经常用Handler在子线程中更新UI线程,那是因为在主线程中有Looper循环,而HandlerThread新建拥有Looper的子线程又有什么用呢? 必然是执行耗时操作.举个例子,数据实时更新,我们每10秒需要切换一下显示的数据,如

  • Android UI绘制流程及原理详解

    一.绘制流程源码路径 1.Activity加载ViewRootImpl ActivityThread.handleResumeActivity() --> WindowManagerImpl.addView(decorView, layoutParams) --> WindowManagerGlobal.addView() 2.ViewRootImpl启动View树的遍历 ViewRootImpl.setView(decorView, layoutParams, parentView) --&

  • Android IPC机制Messenger实例详解

    Android IPC机制Messenger实例详解 前言: Messenger可以翻译成信使,通过它可以在不同进程间传递Message对象有了它就可以轻松实现进程间的数据传递了. Messenger使用的方法相对AIDL比较简单,它对AIDL做了一层封装是的我们不需要像采用AIDL那样去实现进程通信那么麻烦,可以看看他的源码有AIDL的迹象. public final class Messenger implements Parcelable { private final IMessenge

  • Android AsyncTask实现异步处理任务的方法详解

    Android AsyncTask实现异步处理任务的方法详解 在开发Android应用时必须遵守单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行. Android 单线程模型概念详解:http://www.jb51.net/article/112165.htm 在单线程模型中始终要记住两条法则: 不要阻塞UI线程 确保只在UI线程中访问Android UI工具包 当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),

随机推荐