安卓handler源码(androidstudio源码)

Android多线程还有HandleThread,看名字就可以能感觉到得到,会是handler和Thread的综合使用。那到底什么怎么样的呢,就跟随Android的源码来看看他的工作原理是什么样的。我们先看看他的类注解:先看看官方对他的介绍:【Handyclassforstartinganewthreadthathasalooper.Theloopercanth…

大家好,又见面了,我是你们的朋友全栈君。

前言

Android 多线程还有HandleThread,看名字就可以能感觉到得到,会是handler和Thread的综合使用。那到底是怎么样的呢,现在就跟随Android的源码来看看他的工作原理是什么样的。

我们先看看他的类注解:先看看官方对他的介绍:【 Handy class for starting a new thread that has a looper. The looper can then be  used to create handler classes. Note that start() must still be called.】简单的翻译就是这是一个很方便启动一个拥有looper的的类,这个Looper在这以后是用来创建handler类的,注意:thread的start()方法一定要被调用。

理解HandleThread

看到介绍可能觉得HandleThread有点厉害,有looper还能创建handler。我们抱着这样的心情去看HandleThread比较难受,所以,我们要换一下思路,这个HandleThread就是个Thread,这个Thread执行工作任务,我们通过handler给这个Thread安排工作。【从使用角度就相当于我们自己起一个工作线程,然后在线程中自己prepareLooper一样。只不过HandleThread已经把这些做好了,还封装了优先级设置,安全退出等一些辅助功能,让我们开发人员使用起来更加方便

handler我们知道可以用来线程间通信,之前在Handler工作流程梳理里面分析过【具体在本篇就不细说】,我们这里要知道,Handler可以把Message发送到MessageQueue中,通过Looper.loop()循环将Message取出并分派到到相应的Handler去处理。同一个线程中只有一个MessageQueue和Looper, Message的处理是串行的。当有Thread使用了Handler,那么通过Handler给Thread安排任务也是串行执行的,就是一个执行完才执行下一个,所以这个HandleThread不适多耗时任务,这样任务的执行相互收影响比较严重。对于任务量小,使用频繁的任务来说就比较友好,可以使用一个线程来实现线程池的效果,节省了资源。

源码分析

看了上面的理解描述后,我们就可以猜到HandleThread代码逻辑应该不复杂,代码量也不会很大。事实就是这样,加上注解,HandleThread的代码在160多行。

/frameworks/base/core/java/android/os/HandlerThread.java

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;
    private @Nullable Handler mHandler;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    ...
}

HandleThread继承自Thread,首先我们就看到了该有的Looper对象,然后从它的构造函数看,可以设置线程优先级和给线程命名。在使用时如果不设置优先级,会默认设置为Process.THREAD_PRIORITY_DEFAULT;

HandleThread主要的逻辑就在run()方法里面

1.run()

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

在run()里。Looper.prepare()来初始化当前线程的一个Looper。然后拿到一个looper对象 赋值给mLooper,呃呃呃。这里为啥要notifyAll(),在Java中wait()和notify/notifyAll是一起使用的,主要用在多线程中,,用在这里又是为什么?我们在HandleThread搜索一下wait()果然有:

1.2 getLooper()

public Looper getLooper() {
    ...
    // If the thread has been started, wait until the looper has been created.
    synchronized (this) {
        while (isAlive() && mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

在getLooper()方法里,当looper还没有创建时间,需要当前线程保持wait,这是为什么呢?我估计是当线程启动,在没有获取到looper之前,当前线程是空闲的,也没任务做,就先释放资源,让其他线程先执行run()方法来获取Looper对象,然后将looper赋值给mLooper,【当我看到这里时是很疑惑的,这个在文章后面来介绍】;

再回到run()方法中;获mLooper赋值后,随后调用notifyAll(),【此时其他线程的getLooper()方法也会被唤醒,就可以利用looper去创建handler。然后再handleMessage()中实现处理任务的业务代码】;然后接下里就主要是进入loop循环了。这就之前分析handler看到的线程最后的状态是一毛一样,那就是随后线程都会进入到loop循环中。

以上就是HandleThread的主要原理了。

———————————————————-华丽丽的分割线————————————————————————–

疑惑

现在要来说说我在分析那个mlooper被赋值时遇到的疑惑,还记得我们在Handler工作流程梳理事看到Looper.getLooper()最后是从ThreadLoacl中取出的,在Android_ThreadLoacl原理一篇中,我们知道threadLocal保存的变量是与线程相关联的,所以不同线程get到的Looper是不一样的。

疑点来了啊,注意听。假如线程A运行到HandleThread的getLooper()方法,如果此时mlooper为空。那么线程A就会wait,阻塞在这里;那么后来线程B运行到HandleThread的run()方法,从线程B中获取到线程B的looper,然后赋值给mLooper;之后线程A就可以被唤醒,返回mLooper;那么这时候线程A返回的looper其实是在线程B中创建的looper,我就很乱了,,,不应该looper和线程相关的吗?在这里怎么可以这么玩?

反正我是很乱的,问了同事也还没问个明白。有一点肯定的,源码中这么玩肯定是没错的,我需要一个可以让自己信服的理由。

既然没问道直接的答案,按我自己试试,然后,我写了个demo,在主线程中获取主线程的MainLooper,然后在子线程创建Handler时直接传入MainLooper。最后发现, 子线程是可以使用主线程的looper。恩恩,终于,用事实说明了,多线是可以公用looper的。好了,HandleThread中的疑惑解决了;

接下来,我就有了新疑惑,我需要继续搞明白,为什么多线程可以共用looper。这和我之前的理解不一样,我之前的理解是线程中的looper和线程相关,当前线程只能使用当前线程的Looper。

解惑

答案还是得要在源码里面找,经过观察发现HandleThread和我们一般的在子线程中创建Handler的方式是不同的;

  • 子线程中直接我们一般是这样的:
/ * 
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler() {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  * /
  • 使用HandelrThread时是这样的
private HandlerThread mMyhandleThread = new HandlerThread("MyHandleThread");
Handler mHandler;
mMyhandleThread.start();
mHandler = new Handler(mMyhandleThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
            // TODO
            }
};

我感觉问题还是在Handler上。那我们就去看看Handler 的构造函数:

/frameworks/base/core/java/android/os/Handler.java

// 1.我们一般手动在子线程就是这样创建Handler的
public Handler() {
    this(null, false);
}
...
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 " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
//------------------------华丽丽的分割线------------------------

// 2.我们一般使用HandlerThread时是这样创建Handler的
public Handler(Looper looper) {
    this(looper, null, false);
}
...
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

通过对不同方式创建Handler时调用的构造方法,我们发现 ,handler的如果在构造函数中指定了Looper,就直接使用传进来的looper对象,如果在构造事没有传入looper对象,则此时就需要使用本线程自己的looper;至于messageQueue只是和Looper相关,MessageQueue和handler并没有直接联系。

结论

所以我们得出结论,多线程可以使用同一个Looper,相应的也就是使用了相同的MessageQueue。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126973.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • MongoDB和MySQL和Redis的区别

    MongoDB和MySQL和Redis的区别MongoDB和MySQL和Redis的区别MySQL1、在不同的引擎上有不同的存储方式。2、查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高。3、开源数据库的份额在不断增加,mysql的份额页在持续增长。4、缺点就是在海量数据处理的时候效率会显著变慢。MongoDBMongodb是非关系型数据库(nosql),属于文档型数据库。文档是mongoDB中数据的基本单元,类似关系数据库的行,多个键值对有序地放置在一起便是文档,语法有点类似javascript面向对象的查询语言,

  • Git的下载安装 (图文教程)

    Git的下载安装 (图文教程)git的下载安装目录git的下载git的安装步骤git的下载如果你还没有下载Git,可直接到git官网进行下载。如图直接按下Downloads选项这是页面会跳转到相应的页面按下Windows或DownloadforWindows选项,页面会自动跳转下载页面,当然其它系统类型根据需要下载相应的版本即可。下载完成双击运行点击运行…

  • 函数类型_C语言函数类型

    函数类型_C语言函数类型函数类型在ECMAScript中有三种函数类型:函数声明,函数表达式和函数构造器创建的函数。每一种都有自己的特点。1.函数声明这种函数类型的主要特点在于它们仅仅影响变量对象。该特点也解释了第二

  • 图集谷-写真集-爬虫-1.0[通俗易懂]

    图集谷-写真集-爬虫-1.0[通俗易懂]图集谷写真集爬虫

  • Spring AOP四种实现方式Demo详解与相关知识探究[通俗易懂]

    Spring AOP四种实现方式Demo详解与相关知识探究[通俗易懂]一、前言在网络上看到一篇博客Spring实现AOP的4种方式,博主写的很通俗易懂,但排版实在抓狂,对于我这么一个对排版、代码格式有强迫症的人来说,实在是不能忍受~~~~(>_我亲手实现了一遍,重新整理,加上了一些不易关注到的细节、漏掉的知识,以及自己对AOP的一些理解,写成这篇博客。二、AOP相关概念(1)AOP是什么?AOP与拦截器的区别?太抽象的不说,如果你知道St

  • linux命令大全网址https://www.linuxcool.com/

    linux命令大全网址https://www.linuxcool.com/linux命令大全网址https://www.linuxcool.com/

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号