Android 开发 HandlerThread详解 转载
转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/75073307
对于Handler不太懂的可以参考我的这两篇文章:
Android Handler的基本使用
深入理解Handler、Looper、Messagequeue
这篇主要说一下HandlerThread的使用方法,及分析下其源码。
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.
HandlerThread是Android API提供的一个方便、便捷的类,使用它我们可以快速的创建一个带有Looper的线程。Looper可以用来创建Handler实例。注意:start()仍然必须被调用。
如下是HandlerThread使用的demo。
package com.zpengyong.hand; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends Activity {
private final static String TAG = "MainActivity"; private Button mGet;
private TextView mResult; protected final int MSG_GET = 1;
protected final int MSG_RESULT = 2; private HandlerThread mHandlerThread;
//子线程中的Handler实例。
private Handler mSubThreadHandler;
//与Ui线程绑定的Handler实例。
private Handler mUiHandler = new Handler(){
public void handleMessage(Message msg) {
Log.i(TAG, "mUiHandler handleMessage thread:"+Thread.currentThread());
switch (msg.what) {
case MSG_RESULT:
mResult.setText((String)msg.obj);
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "onCreate thread:"+Thread.currentThread());
mGet = (Button) findViewById(R.id.get);
mGet.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mSubThreadHandler.sendEmptyMessage(MSG_GET);
}
});
mResult = (TextView) findViewById(R.id.result); initHandlerThraed();
} private void initHandlerThraed() {
//创建HandlerThread实例
mHandlerThread = new HandlerThread("handler_thread");
//开始运行线程
mHandlerThread.start();
//获取HandlerThread线程中的Looper实例
Looper loop = mHandlerThread.getLooper();
//创建Handler与该线程绑定。
mSubThreadHandler = new Handler(loop){
public void handleMessage(Message msg) {
Log.i(TAG, "mSubThreadHandler handleMessage thread:"+Thread.currentThread());
switch(msg.what){
case MSG_GET:
try { //模拟延时处理
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
double number = Math.random();
String result = "number:"+number;
//向ui线程发送消息,更新ui。
Message message = new Message();
message.what = MSG_RESULT;
message.obj = result;
mUiHandler.sendMessage(message);
break;
default:
break;
}
};
};
} @Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
//退出HandlerThread的Looper循环。
mHandlerThread.quit();
}
}
上述代码比较简单,功能也比较简单,可以在此基础上进行扩展。
在Actvitiy创建的时候调用initHandlerThraed()函数:
- 创建HandlerThread线程
- 运行线程
- 获取HandlerThread线程中的Looper实例
- 通过Looper实例创建Handler实例,从而使mSubThreadHandler与该线程连接到一起。
多次点击按钮,打印信息如下所示:
07-13 05:15:07.662: I/MainActivity(1472): onCreate thread:Thread[main,5,main]
07-13 05:15:45.382: I/MainActivity(1472): mSubThreadHandler handleMessage thread:Thread[handler_thread,5,main]
07-13 05:15:46.402: I/MainActivity(1472): mUiHandler handleMessage thread:Thread[main,5,main]
07-13 05:15:46.412: I/MainActivity(1472): mSubThreadHandler handleMessage thread:Thread[handler_thread,5,main]
07-13 05:15:47.412: I/MainActivity(1472): mUiHandler handleMessage thread:Thread[main,5,main]
.....
点击按钮,向mSubThreadHandler发送消息,mSubThreadHandler中接收到消息进行处理,由打印可知mSubThreadHandler的handleMessage方法运行在子线程中。
模拟耗时操作,生成随机数,然后向主线程中(mUiHandler)发送消息(Message)。
mUiHandler的handleMessage方法运行在主线程,可以用来更新Ui界面。
Activity销毁的时候,调用mHandlerThread.quit(),退出HandlerThread的Looper循环。
效果图如下:
HandlerThread源码分析
源码路径路径:frameworks/base/core/java/android/os/HandlerThread.java
先看下HandlerThread的构造方法。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
//@param name 线程名
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;
}
。。。。
HandlerThread是Thread(线程)的子类。创建一个HandlerThread实例,也就是创建了一个特殊的线程实例。
HandlerThread提供了两个构造方法:
- HandlerThread(String name) 参数为线程名称,线程优先级为Process.THREAD_PRIORITY_DEFAULT。
- HandlerThread(String name, int priority),name为线程名称,priority为设置的线程优先级。
我们知道线程需要通过start()方法来运行线程,HandlerThread也是这样的。接着看下线程运行的run()方法。
/**
* 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实例
Looper.prepare();
synchronized (this) {
//获取当前线程的Looper实例
mLooper = Looper.myLooper();
notifyAll();
}
//设置线程优先级
Process.setThreadPriority(mPriority);
onLooperPrepared();
//开始循环
Looper.loop();
mTid = -1;
}
由run方法可知HandlerThrea线程运行创建了Looper实例,并开启了Looper循环,循环从消息队列中获取消息并给Handler进行处理。对于Looper不太明白的可以参考这篇深入理解Handler、Looper、Messagequeue
onLooperPrepared()在Looper循环之前调用,如果需要在Looper循环之前执行一些设置,可以显式覆盖此方法。
接着看获取Looper实例
//获取HandlerThread线程中的Looper实例
Looper loop = mHandlerThread.getLooper();
对应源码:
//此方法返回与此线程关联的Looper。 如果此线程未启动或由于任何原因isAlive()返回false,此方法将返回null。
public Looper getLooper() {
if (!isAlive()) {
return null;
} // 如果这个线程已经启动,将会被阻塞,直到mLooper被初始化为止。
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
mHandlerThread.getLooper()获取与该线程绑定的Looper实例。mLooper是在HandlerThread的run()方法中赋值的(也就是在子线程中),getLooper是我们在主线程中调用,该方法会阻塞直到mLooper赋值。
然后demo中通过该looper实例创建Handler
//创建Handler与该线程绑定。
mSubThreadHandler = new Handler(loop)
你可能会好奇为什么要这样长久Handler而不是“new Handler()“这样呢?因为我们要创建的Handler要与子线程绑定到一起,要处理子线程中的消息,所以要通过子线程中的looper(有线程对应的消息队列)实例创建Handler。这样通过mSubThreadHandler发送的消息会添加到子线程中的消息队列中,然后Looper实例消息进行分发,交给mSubThreadHandler进行处理。
HandlerThread提供的线程退出方法:
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;
}
quit和quitSafely都是退出HandlerThread的消息循环。其分别调用Looper的quit和quitSafely方法。
quit方法会将消息队列中的所有消息移除(延迟消息和非延迟消息)。
quitSafely会将消息队列所有的延迟消息移除,非延迟消息派发出去让Handler去处理。quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息
HandlerThread适合处理本地IO读写操作(数据库,文件),因为本地IO操作大多数的耗时属于毫秒级别,对于单线程 + 异步队列的形式 不会产生较大的阻塞。而网络操作相对比较耗时,容易阻塞后面的请求,因此在这个HandlerThread中不适合加入网络操作。
至此HandlerThread就说完了。有什么问题欢迎大家指正、交流。
Android 开发 HandlerThread详解 转载的更多相关文章
- Android开发实例详解之IMF(Android SDK Sample—SoftKeyboard)
本博前面的文章介绍了Android开发环境的搭建和模拟器的常用操作.本次,将以Android Sample中经典的SoftKeyboard项目为例,详细解析Android上一个小型项目的开发过程和注意 ...
- Android开发 Context详解与类型 转载
转载地址:https://blog.csdn.net/guolin_blog/article/details/47028975 个人总结: Context分为 activity : activity其 ...
- Android开发——AsyncTask详解
android提供AsynvTask,目的是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的. Android为了降低开发难度,提供了AsyncTask.Asyn ...
- Android开发之详解五大布局
http://bbs.chinaunix.net/thread-3654213-1-1.html 为了适应各式各样的界面风格,Android系统提供了5种布局,这5种布局分别是: LinearLayo ...
- Android 开发 ConstraintLayout详解
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3' app:layout_constraintHorizo ...
- Android开发 StateListDrawable详解
前言 StateListDrawable是与xml中的selector属性对应代码实现类,它需要配合GradientDrawable的使用,如果你还不了解GradientDrawable可以参考我的另 ...
- Android开发 GradientDrawable详解
前言 GradientDrawable类似与Xml布局里的shape,常用在一些自己封装的对话框控件的背景或者其他View中,优势是不需要你在带着xml布局文件一起封包.. 画线 GradientDr ...
- Android开发 layer-list详解
参考:https://blog.csdn.net/speverriver/article/details/80925686 挖坑,以后填坑
- Android开发 LevelListDrawable详解
前言 此篇博客正在施工中... 作者其实就是想挖个坑备忘一下... 十分抱歉, 可以参考https://www.jianshu.com/p/f9ec65241b6b
随机推荐
- iOS 在工程内部创建一个静态库target
当你在开发项目的时候需要把公用的东西打包出来,其他项目方便使用的时候,打包成静态库是你的最优选择,在工程内部开发的时候新建一个target进行静态库的开发可以使你的开发调试更加方便而不是单独新建一个工 ...
- ADO.NET 中的五个主要对象
Connection:主要用来开启程序和数据库的连接 Command:主要是用来对数据库发出一些指令,. DataAdapter;主要在数据源以及DataSet之间执行数据库的传输工作 DataSet ...
- Jmeter的察看结果树 出现乱码的解决方法
在apache-jmeter-3.0\bin目录下, 用Notepad工具打开jmeter.properties文件 在文件中搜索encoding,可以搜素到1030行: #sampleresult. ...
- CHECK约束表达式
1.性别:只能填写'男'和'女' (字段名='男'or 字段名='女') 注意:单引号里的值可以为其他任何字 2.规定密码长度不能小于6位数 (len([字段名])>(5)) 3.例:成绩在 ...
- 【转载】 强化学习(八)价值函数的近似表示与Deep Q-Learning
原文地址: https://www.cnblogs.com/pinard/p/9714655.html ------------------------------------------------ ...
- 获取真实ip三个方法
方法一: /** * 获取客户端IP地址 * * @return string */function get_client_ip(){ if (getenv("HTTP_CLIENT_IP& ...
- arduino 522样本中文注释
#accesscontrol访问控制#change uid更改UID#dump info转储信息#firmware_check固件#Fix Bricked UID修复变砖的UID#Mifare Cla ...
- 《DSP using MATLAB》Problem 7.12
阻带衰减50dB,我们选Hamming窗 代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ...
- oracle存储结构
数据库的物理存储结构 select * from v$datafile; 数据库的逻辑存储结构,从表空间开始查起一个数据库对象的逻辑存储结构如下表空间-段-区-块 select * from dba_ ...
- oracle-pl/sql之三
集合与记录 set serveroutput on create or replace package my_types authid definer is type my_rec is record ...