1.首先来看一个常规的handler用法:

在主线程中建立一个handler:

private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
mTestTV.setText("This is handleMessage");//更新UI
break;
}
}
};

在子线程中进行耗时操作,结束后发送消息,主线程收到消息后进行更新UI操作。

new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络
mHandler.sendEmptyMessage(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();

2.现在来看看handler.post()的版本:

private Handler mHandler;//全局变量
@Override
protected void onCreate(Bundle savedInstanceState) {
mHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络
mHandler.post(new Runnable() {
@Override
public void run() {
mTestTV.setText("This is post");//更新UI
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();

耗时操作完成之后,直接在handler开启的子线程中进行了更新UI的操作。post和sendMessage原理都是封装成Message,并且最终都调用了enqueueMessage()一个无限循环将消息加入到消息队列中(链表的形式)。翻看MessageQueue的方法,我们找到了next(),代码太长,不赘述,我们知道它是用来把消息取出来的就行了。不过这个方法是在什么地方调用的呢,不是在Handler中,我们找到了Looper这个关键人物,我叫他环形使者,专门负责从消息队列中拿消息。参考博客:http://blog.csdn.net/ly502541243/article/details/52062179/

3.HandlerThread用法

(1)创建HandlerThread并启动

mThread = new HandlerThread("handler_thread");
mThread.start();

(2)创建处理任务的handler和在主线程中更新UI的handler

一句话来总结就是:通过handler与HandlerThread进行绑定相当于开启了一个子线程,在这个子线程中处理任务,处理后的任务形成消息或者其他数据形式。再通过主线程的handler.sendMessage(子线程结果),发送给主线程的handler,进行UI操作。

其中处理任务的mWorkHandler与handlerThread关联起来,那么这个Handler对象就是与HandlerThread这个线程绑定了(这时就不再是与UI线程绑定了,这样它处理耗时操作将不会阻塞UI),它将在handleMessage(Message msg)中处理耗时任务,之后在mUIHandler对UI进行刷新,如果在handleMessage()执行完成后,如果想要更新UI,可以用UI线程的Handler发消息给UI线程来更新。

mWorkHandler = new Handler(mThread.getLooper());
mUIHandler = new Handler();

(3)在合适的时机退出HandlerThread,比如activity中的onDestroy(),方法有quit()和quitSafely()

下面我们看一个比较典型的handlerThread的使用过程:参考http://blog.csdn.net/javazejian/article/details/52426353

public class HandlerThreadActivity extends Activity {
/**
* 图片地址集合
*/
private String url[]={
"http://img.blog.csdn.net/20160903083245762",
"http://img.blog.csdn.net/20160903083252184",
"http://img.blog.csdn.net/20160903083257871",
"http://img.blog.csdn.net/20160903083257871",
"http://img.blog.csdn.net/20160903083311972",
"http://img.blog.csdn.net/20160903083319668",
"http://img.blog.csdn.net/20160903083326871"
};
private ImageView imageView;
private Handler mUIHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
LogUtils.e("次数:"+msg.what);
ImageModel model = (ImageModel) msg.obj;
imageView.setImageBitmap(model.bitmap);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_thread);
imageView= (ImageView) findViewById(R.id.image);
//创建异步HandlerThread
HandlerThread handlerThread = new HandlerThread("downloadImage");
//必须先开启线程
handlerThread.start();
//子线程Handler
Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());
for(int i=0;i<7;i++){
//每个1秒去更新图片
childHandler.sendEmptyMessageDelayed(i,1000*i);
}
}
/**
* 该callback运行于子线程
*/
class ChildCallback implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
//在子线程中进行网络请求
Bitmap bitmap=downloadUrlBitmap(url[msg.what]);
ImageModel imageModel=new ImageModel();
imageModel.bitmap=bitmap;
imageModel.url=url[msg.what];
Message msg1 = new Message();
msg1.what = msg.what;
msg1.obj =imageModel;
//通知主线程去更新UI
mUIHandler.sendMessage(msg1);
return false;
}
}
private Bitmap downloadUrlBitmap(String urlString) {
HttpURLConnection urlConnection = null;
BufferedInputStream in = null;
Bitmap bitmap=null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
bitmap=BitmapFactory.decodeStream(in);
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (in != null) {
in.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
return bitmap;
}
}

在这个案例中,我们创建了两个Handler,一个用于更新UI线程的mUIHandler和一个用于异步下载图片的childHandler。最终的结果是childHandler会每个隔1秒钟通过sendEmptyMessageDelayed方法去通知ChildCallback的回调函数handleMessage方法去下载图片并告诉mUIHandler去更新UI界面,以上便是HandlerThread常规使用。

下面来看另外一个例子,使用post方式通信:

public class MainActivity extends AppCompatActivity {

    private TextView mTvServiceInfo;

    private HandlerThread mCheckMsgThread;
private Handler mCheckMsgHandler;
private boolean isUpdateInfo; private static final int MSG_UPDATE_INFO = 0x110; //与UI线程管理的handler
private Handler mHandler = new Handler(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("~~~~~~~~~~~~~~~now is ",Thread.currentThread().getName());
setContentView(R.layout.activity_main); //创建后台线程
mCheckMsgThread = new HandlerThread("check-message-coming");
mCheckMsgThread.start();
mCheckMsgHandler = new Handler(mCheckMsgThread.getLooper()){
@Override
public void handleMessage(Message msg) {
try
{
//模拟耗时,处理任务(模拟实时获取大盘数据),处理完成之后,通知mHandler完成界面更新
Thread.sleep(1000);
mHandler.post(new Runnable()
{
@Override
public void run()
{
String result = "实时更新中,当前大盘指数:<font color='red'>%d</font>";
result = String.format(result, (int) (Math.random() * 3000 + 1000));
mTvServiceInfo.setText(Html.fromHtml(result));
}
}); } catch (InterruptedException e) {
e.printStackTrace();
}
if (isUpdateInfo)
{
mCheckMsgHandler.sendEmptyMessageDelayed(MSG_UPDATE_INFO, 1000);
}
}
}; }
}

4.handlerThread的一些细节补充学习:

HandlerThread本质上是一个线程类,继承自Thread类,但是HandlerThread有自己的Looper对象,可以进行looper循环,不断从MessageQueue中取消息。

HandlerThread的特点

  • HandlerThread将loop转到子线程中处理,说白了就是将分担MainLooper的工作量,降低了主线程的压力,使主界面更流畅。
  • 开启一个线程起到多个线程的作用。处理任务是串行执行,按消息发送顺序进行处理。
    相比多次使用new Thread(){…}.start()这样的方式节省系统资源。
    但是由于每一个任务都将以队列的方式逐个被执行到,一旦队列中有某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。
  • HandlerThread拥有自己的消息队列,它不会干扰或阻塞UI线程。
  • 通过设置优先级就可以同步工作顺序的执行,而又不影响UI的初始化;
  • HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper

总结

HandlerThread比较适用于单线程+异步队列的场景,比如IO读写操作,耗时不多而且也不会产生较大的阻塞。对于网络IO操作,HandlerThread并不适合,因为它只有一个线程,还得排队一个一个等着

201709013工作日记--Android消息机制HandlerThread的更多相关文章

  1. 201709012工作日记--Android消息机制

    1. android的消息机制——Handler机制 参考:http://www.jianshu.com/p/9e4d1fab0f36. Android异步消息处理机制完全解析,带你从源码的角度理解: ...

  2. 201709013工作日记--Android异步通信AsyncTask

    参考相关博客:http://blog.csdn.net/liuhe688/article/details/6532519 在Android中实现异步任务机制有两种方式,Handler和AsyncTas ...

  3. Android消息机制

    每一个Android应用在启动的时候都会创建一个线程,这个线程被称为主线程或者UI线程,Android应用的所有操作默认都会运行在这个线程中. 但是当我们想要进行数据请求,图片下载,或者其他耗时操作时 ...

  4. Android消息机制不完全解析(上)

        Handler和Message是Android开发者常用的两个API,我一直对于它的内部实现比较好奇,所以用空闲的时间,阅读了一下他们的源码.    相关的Java Class: androi ...

  5. Android 消息机制 (Handler、Message、Looper)

    综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidne ...

  6. Android开发之漫漫长途 ⅥI——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  7. Android开发之漫漫长途 Ⅶ——Android消息机制(Looper Handler MessageQueue Message)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  8. Android消息机制探索(Handler,Looper,Message,MessageQueue)

    概览 Android消息机制是Android操作系统中比较重要的一块.具体使用方法在这里不再阐述,可以参考Android的官方开发文档. 消息机制的主要用途有两方面: 1.线程之间的通信.比如在子线程 ...

  9. Android消息机制1-Handler(Java层)(转)

    转自:http://gityuan.com/2015/12/26/handler-message-framework/ 相关源码 framework/base/core/java/andorid/os ...

随机推荐

  1. SpringBoot 监控管理模块actuator没有权限的问题

    SpringBoot 1.5.9 版本加入actuator依赖后, 访问/beans 等敏感的信息时候报错,如下 Tue Mar 07 21:18:57 GMT+08:00 2017 There wa ...

  2. Object-c 创建按钮

    @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //动态创建我们自己的按钮 //1.创建按钮(UIB ...

  3. sql查询job

    use msdb go --if object_id('tempdb..#SqlAgentJob') is not null -- drop table #SqlAgentJob --go decla ...

  4. Android图形动画

    一.动画基础 本质 每帧绘制不同的内容. 基本过程 开始动画后,调用View的invalidate触发重绘.重绘后检查动画是否停止,若未停止则继续调用invalidate触发下一帧(下一次重绘),直到 ...

  5. Flexvolume

    https://kubernetes.io/docs/concepts/storage/volumes/ https://github.com/kubernetes/community/blob/ma ...

  6. hdoj1114 Piggy-Bank(DP 完全背包)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1114 思路: 题目看着有些绕,其实就是完全背包的变形,需要注意的是这里求最小值,所以需要将dp数组初始 ...

  7. 第八章 高级搜索树 (a2)伸展树:双层伸展

  8. [leetcode]314. Binary Tree Vertical Order Traversal二叉树垂直遍历

    Given a binary tree, return the vertical order traversal of its nodes' values. (ie, from top to bott ...

  9. AIDL--------应用之间的通信接口

    在下面例子中04Service中添加aidl包包里定义好接口 接口文件名后缀为.aidl package com.example.aidl; interface IRemoteService{ voi ...

  10. 使用jmeter工具测试上传接口

    1.方法选择post:上传都是post上传. 2.路径输入正确的上传接口路径,并勾选Use multipart/form-data for POST 3.添加文件,文件路径尽量不要有中文,防止编码问题 ...