HandlerThread 简单介绍:

我们知道Thread线程是一次性消费品,当Thread线程运行完一个耗时的任务之后。线程就会被自己主动销毁了。假设此时我又有一

个耗时任务须要运行,我们不得不又一次创建线程去运行该耗时任务。然而。这样就存在一个性能问题:多次创建和销毁线程是非常耗

系统资源的。为了解这样的问题,我们能够自己构建一个循环线程Looper Thread。当有耗时任务投放到该循环线程中时。线程运行耗

时任务,运行完之后循环线程处于等待状态,直到下一个新的耗时任务被投放进来。这样一来就避免了多次创建Thread线程导致的

性能问题了。或许你能够自己去构建一个循环线程,但我能够告诉你一个好消息。Aandroid SDK中事实上已经有一个循环线程的框架

了。此时你仅仅须要掌握其怎么使用的就ok啦!当然就是我们今天的主角HandlerThread啦!

接下来请HandlerThread上场,鼓掌~~

HandlerThread的父类是Thread,因此HandlerThread事实上是一个线程,仅仅只是其内部帮你实现了一个Looper的循环而已。

那么我们

先来了解一下Handler是怎么使用的吧。

【转载请注明出处:Android HandlerThread 源代码分析 CSDN 废墟的树】

HandlerThread使用步骤:

1.创建实例对象

HandlerThread handlerThread = new HandlerThread("handlerThread");

以上參数能够随意字符串。參数的作用主要是标记当前线程的名字。

2.启动HandlerThread线程

handlerThread.start();

到此,我们就构建完一个循环线程了。

那么你可能会怀疑。那我怎么将一个耗时的异步任务投放到HandlerThread线程中去运行呢?当然是有办法的,接下来看第三部。

3.构建循环消息处理机制

Handler subHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//实现自己的消息处理
return true;
}
});

第三步创建一个Handler对象。将上面HandlerThread中的looper对象最为Handler的參数。然后重写Handler的Callback接口类中的

handlerMessage方法来处理耗时任务。

总结:以上三步顺序不能乱,必须严格依照步骤来。到此,我们就能够调用subHandler以发送消息的形式发送耗时任务到线程

HandlerThread中去运行。言外之意就是subHandler中Callback接口类中的handlerMessage方法事实上是在工作线程中运行的。

HandlerThread实例:

package com.example.handlerthread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends Activity { private Handler mSubHandler;
private TextView textView;
private Button button; private Handler.Callback mSubCallback = new Handler.Callback() {
//该接口的实现就是处理异步耗时任务的,因此该方法运行在子线程中
@Override
public boolean handleMessage(Message msg) { switch (msg.what) {
case 0:
Message msg1 = new Message();
msg1.what = 0;
msg1.obj = java.lang.System.currentTimeMillis();
mUIHandler.sendMessage(msg1);
break; default:
break;
} return false;
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView);
button = (Button) findViewById(R.id.button); HandlerThread workHandle = new HandlerThread("workHandleThread");
workHandle.start();
mSubHandler = new Handler(workHandle.getLooper(), mSubCallback); button.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
//投放异步耗时任务到HandlerThread中
mSubHandler.sendEmptyMessage(0);
}
}); }
}

HandlerThread源代码分析

HandlerThread构造方法

/**
* 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.
*/
public class HandlerThread extends Thread {
//线程优先级
int mPriority;
//当前线程id
int mTid = -1;
//当前线程持有的Looper对象
Looper mLooper; //构造方法
public HandlerThread(String name) {
//调用父类默认的方法创建线程
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
//带优先级參数的构造方法
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
} ............... }

分析:该类开头就给出了一个描写叙述:该类用于创建一个带Looper循环的线程,Looper对象用于创建Handler对象,值得注意的是在创建Handler

对象之前须要调用start()方法启动线程。

这里可能有些人会有疑问?为啥须要先调用start()方法之后才干创建Handler呢?后面我们会解答。

上面的代码凝视已经非常清楚了。HandlerThread类有两个构造方法。不同之处就是设置当前线程的优先级參数。你能够依据自己的情况来设置优先

级,也能够使用默认优先级。

HandlerThrad的run方法

public class HandlerThread extends Thread {
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
} @Override
public void run() {
//获得当前线程的id
mTid = Process.myTid();
//准备循环条件
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功。这里主要是通知getLooper方法中的wait
notifyAll();
}
//设置当前线程的优先级
Process.setThreadPriority(mPriority);
//该方法实现体是空的,子类能够实现该方法,作用就是在线程循环之前做一些准备工作。当然子类也能够不实现。
onLooperPrepared();
//启动loop
Looper.loop();
mTid = -1;
}
}

分析:以上代码中的凝视已经写得非常清楚了,以上run方法主要作用就是调用了Looper.prepare和Looper.loop构建了一个循环线程。

值得一提的

是。run方法中在启动loop循环之前调用了onLooperPrepared方法,该方法的实现是一个空的。用户能够在子类中实现该方法。该方法的作用是

在线程loop之前做一些初始化工作,当然你也能够不实现该方法,详细看需求。

由此也能够看出,Googleproject师在编写代码时也考虑到代码的可扩展性。牛B!

HandlerThread的其它方法

getLooper获得当前线程的Looper对象

 /**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
//假设线程不是存活的。则直接返回null
if (!isAlive()) {
return null;
} // If the thread has been started, wait until the looper has been created.
//假设线程已经启动,可是Looper还未创建的话,就等待,知道Looper创建成功
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}

分析:事实上方法开头的英文凝视已经解释的非常清楚了:该方法主要作用是获得当前HandlerThread线程中的mLooper对象。

首先推断当前线程是否存活,假设不是存活的,这直接返回null。

其次假设当前线程存活的。在推断线程的成员变量mLooper是否为null。假设为

null,说明当前线程已经创建成功,可是还没来得及创建Looper对象,因此,这里会调用wait方法去等待。当run方法中的notifyAll方法调用之后

通知当前线程的wait方法等待结束,跳出循环。获得mLooper对象的值。

总结:在获得mLooper对象的时候存在一个同步的问题。仅仅有当线程创建成功而且Looper对象也创建成功之后才干获得mLooper的值。这里等待方法wait和run方法中的notifyAll方法共同完毕同步问题。

quit结束当前线程的循环

 /**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
//安全退出循环
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}

分析:以上有两种让当前线程退出循环的方法,一种是安全的,一中是不安全的。

至于两者有什么差别? quitSafely方法效率比quit方法标率低一点。可是安全。详细选择哪种就要看详细项目了。

总结:

1.HandlerThread适用于构建循环线程。

2.在创建Handler作为HandlerThread线程消息运行者的时候必须调用start方法之后。由于创建Handler须要的Looper參数是从HandlerThread类中获得,而Looper对象的赋值又是在HandlerThread的run方法中创建。

3.关于HandlerThread和Service的结合使用请參考还有一篇博客:Android IntentService 源代码分析

【转载请注明出处:Android HandlerThread 源代码分析 CSDN 废墟的树】

Android HandlerThread 源代码分析的更多相关文章

  1. Android 消息处理源代码分析(1)

    Android 消息处理源代码分析(1) 在Android中,通常被使用的消息队列的代码在文件夹\sources\android-22\android\os下,涉及到下面几个类文件 Handler.j ...

  2. Android HttpURLConnection源代码分析

    Android HttpURLConnection源代码分析 之前写过HttpURLConnection与HttpClient的差别及选择.后来又分析了Volley的源代码. 近期又遇到了问题,想在V ...

  3. Android KLog源代码分析

    Android KLog源代码分析 Android KLog源代码分析 代码结构 详细分析 BaseLog FileLog JsonLog XmlLog 核心文件KLogjava分析 遇到的问题 一直 ...

  4. android开发源代码分析--多个activity调用多个jni库的方法

    android开发源代码分析--多个activity调用多个jni库的方法 有时候,我们在开发android项目时会遇到须要调用多个native c/jni库文件,下面是本人以前实现过的方法,假设有知 ...

  5. Android 消息处理源代码分析(2)

    Android 消息处理源代码分析(1)点击打开链接 继续接着分析剩下的类文件 Looper.java public final class Looper { final MessageQueue m ...

  6. Appium Android Bootstrap源代码分析之启动执行

    通过前面的两篇文章<Appium Android Bootstrap源代码分析之控件AndroidElement>和<Appium Android Bootstrap源代码分析之命令 ...

  7. Android AsyncTask 源代码分析

    AsyncTask源代码分析 public abstract class AsyncTask<Params, Progress, Result> { //日志TAG private sta ...

  8. Appium Android Bootstrap源代码分析之简单介绍

    在上一个系列中我们分析了UiAutomator的核心源代码,对UiAutomator是怎么执行的原理有了根本的了解.今天我们会開始另外一个在安卓平台上基于UiAutomator的新起之秀--Appiu ...

  9. [Android] Volley源代码分析(五岁以下儿童)Q \\ u0026一个

    Volley源代码分析系列那里一段时间,告诉我,有许多私人留言,同时一些问题抛出.对于一些简单的问题,我们跳,这两天被连接到朋友@smali提出的问题.告诉我你不得不赞叹查看源代码时的详细程度,大家一 ...

随机推荐

  1. ThinkPHP中如何获取指定日期后工作日的具体日期

    思路: 1.获取到查询年份内所有工作日数据数组2.获取到查询开始日期在工作日的索引3.计算需查询日期索引4.获得查询日期 /*创建日期类型记录表格*/ CREATE TABLE `tb_workday ...

  2. MySQL5.6 GTID Replication

    MySQL 5.6 的新特性之一,是加入了全局事务 ID (Global Transaction ID) 来强化数据库的主备一致性,故障恢复,以及容错能力.官方文档:http://dev.mysql. ...

  3. 2018-2019-2 20165333 《网络对抗技术》 Exp5:MSF基础应用

    2018-2019-2 20165333 <网络对抗技术> Exp5:MSF基础应用 实践内容(3.5分) 本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路 ...

  4. Hibernate api 之常见的类(配置类,会话工厂类,会话类)

    1:Configuration :配置管理类对象 1.1:config.configure(): 加载主配置文件的方法(hibernate.cfg.xml) ,默认加载src/hibernate.cf ...

  5. SpringBank 开发日志 Mybatis 使用redis 作为二级缓存时,无法通过cacheEnabled=false 将其关闭

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC ...

  6. 【AtCoder】CODE FESTIVAL 2017 qual A

    A - Snuke's favorite YAKINIKU -- #include <bits/stdc++.h> #define fi first #define se second # ...

  7. 连连看 (BFS)

    难点在于判断转弯小于两次  这个还好 主要是   走过的路还能再走 但是去掉标记数组会超时 *******所以用     v.step<=f[v.x][v.y]即可!!!  这个思想非常重用!! ...

  8. HDU1532 网络流最大流【EK算法】(模板题)

    <题目链接> 题目大意: 一个农夫他家的农田每次下雨都会被淹,所以这个农夫就修建了排水系统,还聪明的给每个排水管道设置了最大流量:首先输入两个数n,m ;n为排水管道的数量,m为节点的数量 ...

  9. TypeReference -- 让Jackson Json在List/Map中识别自己的Object

    private Map<String, Object> buildHeaders(Object params) { ObjectMapper objectMapper = JacksonH ...

  10. 用js来实现那些数据结构01(数组篇01-数组的增删)

    在开始正式的内容之前,不得不说说js中的数据类型和数据结构,以及一些比较容易让人混淆的概念.那么为什么要从数组说起?数组在js中是最常见的内存数据结构,数组数据结构在js中拥有很多的方法,很多初学者记 ...