上一节我们讲解了Handler的基本使用方法,也是平时大家用到的最多的使用方式。那么本节让我们来学习一下Handler的工作原理吧!!!

  我们知道Android中我们只能在ui线程(主线程)更新ui信息,那么你们知道为什么只能通过Handler机制更新ui吗?其实最根本的目的就是解决多线程并发的问题。

  假设在一个Activity中有多个线程去更新ui,并且都没有加锁,那么会是什么样子?

    导致的结果就是更新界面错乱。

  如果对更新ui的操作都进行加锁处理的话又产生什么问题哪?

    性能下降。

  处于对以上问题的考虑,Android给我们提供了一套更新ui的机制,我们只需要遵守这样的机制就可以了。根本不用去关心多线程问题,因为所有更新ui的操作,都是在主线程的消息队列当中通过轮训处理的。

<一>Handler机制的角色和职责

    1  MessageQueue  消息队列

        存储消息的容器,可以向其中添加、取出消息。遵循先进先出的原则。

    2  Handler

        负责将消息发向消息容器即MessageQueue中。

    3  Looper 轮训器

        通过调用自身的loop方法,不断的从消息队列当中取出消息并发送给target(即handler)处理消息。当消息队列当中没有轮训消息时,它就处于堵塞状态。

  来个实际图来看一下Handler的工作原理:

    

<二>Handler机制工作原理分析

  Handler机制要想起作用有三个步骤:

    1  创建Looper

    2  创建Handler

    3  调用Looper的loop方法,循环消息

    下面让我们来看看android中,如何去遵循这三点的,在那之前,先普及一下一个知识:

      默认整个应用程序,都是通过ActivityThread类启动的,在ActivityThread类当中,负责创建我们所有的Activity,并回调每个Activity中的生命周期方法。在ActivityThread类中,默认会去创建一个线程,这个线程叫做main线程(主线程)。所有的应用程序,更新ui的操作,都是在这个main线程中进行的。

  创建Looper和调用loop方法的工作,Android SDK 已经为我们做好了,所以我们在平时使用的时候,只需要创建Handler并发送消息即可。下面我们跟随Android源码看看它是怎么做的。入口是ActivityThread的main方法。

跟进Looper的prepareMainLooper方法

跟进prepare方法

  这里我们需要对ThreadLocal类进行一下解释,ThreadLocal在我们的线程当中用于去保存一些变量信息,默认情况下,创建一个与线程相关的一个对象,是通过threadLocal存储的,threadLocal有set和get方法,set是把变量设置到threadLocal当中 ,get方法是获取出来。因为当前线程是ui线程,默认情况下threadLocal是没有存储的,所以为null,所以不走if而是new Looper对象之后在存储,下面我们在看看初始化Looper的时候做了哪些事情!

我们看到,在创建Looper轮训器的时候,自动的创建了消息队列MessageQuene。也就是说默认的情况下,android为我们自动创建了主线程的Looper和MessageQuene。

那么Handler怎么和我们的MessageQuene消息队列联系在一起的那?因为之前不是说handler发出的消息是发送到消息队列中了吗?

原因还要看我们在创建Handler的时候做了那些事情,跟进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方法

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

看到了什么?sThreadLocal是不是很熟悉,没错它就是ThreadLocal对象。默认情况下android为我们创建了主线程Looper对象并存储在sThreadLocal中,所以此处返回的就是主线程的Looper对象,也就是说我们在创建Handler的时候,它就和消息队列关联起来了。

那么当我们使用handler发送消息的时候,不管使用哪一种方法,一步一步跟进源码发现最终调用的都是Handler的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);
}

看代码可知,在发送消息的时候消息队列不能为null,继续跟进enqueueMessage方法

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

可以看到,消息最终发送到了消息队列当中。那么消息是怎么轮训的那?前面已经提过,是通过Looper的loop方法,那么来看看吧!!!

可以看到loop方法里面的机制就是一个死循环,不断的从消息队列中取出消息,然后发送给target(handler对象)的dispatchMessage方法,跟进去!!!

一般情况下我们发送消息的时候没有给Message的callback赋值,所以第一个if条件不满足。下面的mCallback是在我们初始化Handler的时候才被初始化,Handler初始化有一种方法Handler(Callback callback),此处的参数就是给mCallback赋值的。我们一般初始化Handler的时候使用的是空参数的构造器,所以导致mCallback未被初始化,所以会直接走handleMessage(msg)方法,也就是我们初始化Handler时重写的handleMessage方法。至此,Handler工作的机制就开始工作了,你、了解了吗?

下面让我们看看如果我们选择的是带Callback参数的初始化方式逻辑又会是什么样那,请看初始化代码:

    Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.d(TAG,"callback参数-------handleMessage");
return true;//此处的返回值会影响下面的handleMessage方法是否调用
//false 调用
//true 不调用
} }){
@Override
public void handleMessage(Message msg) {
Log.d(TAG,"重写handler的-------handleMessage方法");
super.handleMessage(msg); }
};

根据上面的源码分析我们知道此处Callback参数中的handleMessage方法的返回值会影响到下面第二个handleMessage方法是否调用。经过验证,return true  则不调用 ,return false则调用。

  最会通过一张图,看一下Handler的原理:

    

   更形象一点,可以看下图:

    

  好了,Android中的Handler机制工作原理我已经介绍完毕!!!参考了幕课网中《Android面试常客Handler详解》,大家如果没有明白可以去该网站自行学习。下篇我将介绍如何在子线程创建Handler。

Handler系列之原理分析的更多相关文章

  1. 【转载】Android 的 Handler 机制实现原理分析

    handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段.使用起来很简单,就两个步骤,在主线程重写handler的handleMessage( )方法,在工作线程发送消息.但是,有没有 ...

  2. [转]Handler MessageQueue Looper消息循环原理分析

    Handler MessageQueue Looper消息循环原理分析   Handler概述 Handler在Android开发中非常重要,最常见的使用场景就是在子线程需要更新UI,用Handler ...

  3. Handler 原理分析和使用(二)

    在上篇 Handler 原理分析和使用(一)中,介绍了一个使用Handler的一个简单而又常见的例子,这里还有一个例子,当然和上一篇的例子截然不同,也是比较常见的,实例如下. import andro ...

  4. Handler 原理分析和使用(一)

    我为什么写Handler,原因主要还在于它在整个 Android 应用层面非常之关键,他是线程间相互通信的主要手段.最为常用的是其他线程通过Handler向主线程发送消息,更新主线程UI. 下面是一个 ...

  5. Handler 原理分析和使用之HandlerThread

    前面已经提到过Handler的原理以及Handler的三种用法.这里做一个非常简单的一个总结: Handler 是跨线程的Message处理.负责把Message推送到MessageQueue和处理. ...

  6. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  7. java基础解析系列(七)---ThreadLocal原理分析

    java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...

  8. Eureka 系列(05)消息广播(上):消息广播原理分析

    Eureka 系列(05)消息广播(上):消息广播原理分析 [TOC] 0. Spring Cloud 系列目录 - Eureka 篇 首先回顾一下客户端服务发现的流程,在上一篇 Eureka 系列( ...

  9. Java入门系列之线程池ThreadPoolExecutor原理分析思考(十五)

    前言 关于线程池原理分析请参看<http://objcoding.com/2019/04/25/threadpool-running/>,建议对原理不太了解的童鞋先看下此文然后再来看本文, ...

随机推荐

  1. 06.SQLServer性能优化之---数据库级日记监控

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 之前说了一下数据库怎么发邮件:http://www.cnblogs.com/duniti ...

  2. SQLServer地址搜索性能优化例子

    这是一个很久以前的例子,现在在整理资料时无意发现,就拿出来再改写分享. 1.需求 1.1 基本需求: 根据输入的地址关键字,搜索出完整的地址路径,耗时要控制在几十毫秒内. 1.2 数据库地址表结构和数 ...

  3. windows环境下sublime的nodejs插件详细安装图解

    前面的话   搜索了好多文档后,才成功地安装了sublime text3的nodejs插件.为了存档,也为了方便有同样需求的朋友,将其安装过程详细记录如下 安装nodejs 虽然nodejs官网提供了 ...

  4. 前端学HTTP之报文首部

    前面的话 首部和方法配合工作,共同决定了客户端和服务器能做什么事情.在请求和响应报文中都可以用首部来提供信息,有些首部是某种报文专用的,有些首部则更通用一些.本文将详细介绍HTTP报文中的首部 结构 ...

  5. 谱聚类(spectral clustering)原理总结

    谱聚类(spectral clustering)是广泛使用的聚类算法,比起传统的K-Means算法,谱聚类对数据分布的适应性更强,聚类效果也很优秀,同时聚类的计算量也小很多,更加难能可贵的是实现起来也 ...

  6. OpenLiveWriter代码插件

    1.OpenLiveWriter安装 Windows Live Writer在2012年就停止了更新,Open Live Writer(以下简称OLW)是由Windows Live WriterWri ...

  7. .NET中AOP方便之神SheepAspect

    SheepAspect 简介以及代码示列: SheepAspect是一个AOP框架为.NET平台,深受AspectJ.它静织目标组件作为一个编译后的任务(编译时把AOP代码植入). 多有特性时,可根据 ...

  8. Android之Dedug--Circular dependencies cannot exist in AnimatorSet

    今日,在学习AnimatorSet时,使用play.with.after.before时,代码书写如下: ObjectAnimator animator1 = ObjectAnimator.ofFlo ...

  9. 自建git node pm2 (不赘述,就说遇见的问题)

    //======================[git]部分 主题部分还是按照网上的办法进行安装. 安装的话  分为两个办法(一个是yum (contos办法)  或者sudo(ubuntu办法) ...

  10. linux下 lvm 磁盘扩容

    打算给系统装一个oracle,发现磁盘空间不足.在安装系统的时候我选择的是自动分区,系统就会自动以LVM的方式分区.为了保证系统后期的可用性,建议所有新系统安装都采用LVM,之后生产上的设备我也打算这 ...