软件编程
位置:首页>> 软件编程>> Android编程>> Android实例HandlerThread源码分析

Android实例HandlerThread源码分析

作者:废墟的树  发布时间:2022-03-05 13:35:33 

标签:Android,HandlerThread

HandlerThread 简介:
我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了。如果此时我又有一

个耗时任务需要执行,我们不得不重新创建线程去执行该耗时任务。然而,这样就存在一个性能问题:多次创建和销毁线程是很耗

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

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

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

了。此时你只需要掌握其怎么使用的就ok啦!当然就是我们今天的主角HandlerThread啦!接下来请HandlerThread上场,鼓掌~~

HandlerThread的父类是Thread,因此HandlerThread其实是一个线程,只不过其内部帮你实现了一个Looper的循环而已。那么我们

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

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之前做一些初始化工作,当然你也可以不实现该方法,具体看需求。由此也可以看出,Google工程师在编写代码时也考虑到代码的可扩展性。牛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方法中创建。

来源:http://www.cnblogs.com/feidu/p/8057039.html

0
投稿

猜你喜欢

  • 搜索过滤功能,相信大家都能用到,一般都是针对列表进行过滤的。下面给大家提供一种过滤列表的方法。老规矩,先上图RecycleView搜索过滤器
  • 1、SerialPortHelper「Android串口通信」介绍原项目地址https://github.com/freyskill/Ser
  • 这里写链接内容仿映客送小礼物的特效,顺便复习一下属性动画,话不多说先看效果图。需求分析可以看到整个动画有几部分组成,那我们就把每个部分拆分出
  • 前言在实际生活中,地图是我们经常使用的一种工具,通常我们会用它进行导航,输入一个出发城市,输入一个目的地城市,就可以把路线规划好,而在规划好
  • 本文实例为大家分享了C#实现语音播报功能的具体代码,供大家参考,具体内容如下环境:window10vs2019 16.5.5.netfram
  • 今天有朋友问我一道面试题,有5个人抢5个红包,可重复抢,用多线程程序实现,实现方式有多种,分享一下我的思路:应用了阻塞队列的特性。/**
  • 实践过程效果代码public partial class Form1 : Form{    public Form1()
  • 前言回调的核心就是回调方将本身即this传递给调用方,这样调用方就可以在调用完毕之后告诉回调方它想要知道的信息。1、什么是回调软件模块之间总
  • 前言 短时间提升自己最快的手段就是背面试题,最近总结了Java常用的面试题,分享给大家,希望大家都能圆梦大厂,加油,我命由我不由天
  • Java 序列化和反序列化实例详解在分布式应用中,对象只有经过序列化才能在各个分布式组件之间传输,这就涉及到两个方面的技术-发送者将对象序列
  • 本文实例讲述了C#实现鼠标移动到曲线图上显示值的方法。分享给大家供大家参考。具体实现方法如下:一、问题:完成折线图报表后,产品经理要求把折线
  • 在前面的《基于任务的异步编程模式(TAP)》文章中讲述了.net 4.5框架下的异步操作自我实现方式,实际上,在.net 4.5中部分类已实
  • MyBatis提供了 * 接口,我们可以实现自己的 * ,将其作为一个plugin装入到SqlSessionFactory中。 首先要说的是
  • 使用抽象类应该注意的几个要点:包含一个或者多个抽象方法的类必须被声明为抽象类. 将类声明为抽象类,不一定含有抽象方法.通常认为,在抽象类中不
  • 今天来给大家介绍一个非常有用的Studio Tips,有些时候我们在一个方法内部写了过多的代码,然后想要把一些代码提取出来再放在一个单独的方
  • 本文实例讲述了C# SQLite事务操作方法。分享给大家供大家参考,具体如下:在 C#中执行Sqlite数据库事务有两种方式:SQL代码和C
  • 1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位class Base {Base() {System.out.p
  • 前言Java8新特性java.time.*包学习。 自从java发布模式变更就发现自己有些跟不上他们的速度,java8还有不少没有用透而9、
  • 本文以一个简单实例讲述了C#实现自定义双击事件的方法,分享给大家供大家参考之用。具体方法如下:主要功能代码如下:public partial
  • 在spring中有很多以XXXAware命名的接口,很多人也不清楚这些接口都是做什么用的,这篇文章将描述常用的一些接口。一,Applicat
手机版 软件编程 asp之家 www.aspxhome.com