java handlerthread_Android HandlerThread 详解 - huansky - 博客园

首先是 DownloadThread类,继承于 HandlerThread,用于下载。

public class DownloadThread extendsHandlerThread{private static final String TAG = "DownloadThread";public static final int TYPE_START = 2;//通知主线程任务开始

public static final int TYPE_FINISHED = 3;//通知主线程任务结束

private Handler mUIHandler;//主线程的Handler

publicDownloadThread(String name) {super(name);

}/** 执行初始化任务

**/@Overrideprotected voidonLooperPrepared() {

Log.e(TAG,"onLooperPrepared: 1.Download线程开始准备");super.onLooperPrepared();

}//注入主线程Handler

public voidsetUIHandler(Handler UIhandler) {

mUIHandler=UIhandler;

Log.e(TAG,"setUIHandler: 2.主线程的handler传入到Download线程");

}//Download线程开始下载

public voidstartDownload() {

Log.e(TAG,"startDownload: 3.通知主线程,此时Download线程开始下载");

mUIHandler.sendEmptyMessage(TYPE_START);//模拟下载

Log.e(TAG, "startDownload: 5.Download线程下载中...");

SystemClock.sleep(2000);

Log.e(TAG,"startDownload: 6.通知主线程,此时Download线程下载完成");

mUIHandler.sendEmptyMessage(TYPE_FINISHED);

}

}

然后是 MainActivity部分,UI 和处理消息。

public class MainActivity extendsAppCompatActivity {private static final String TAG = "MainActivity";private DownloadThread mHandlerThread;//子线程

private Handler mUIhandler;//主线程的Handler

@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);//初始化,参数为线程的名字

mHandlerThread = new DownloadThread("mHandlerThread");//调用start方法启动线程

mHandlerThread.start();//初始化Handler,传递 mHandlerThread 内部的一个 looper

mUIhandler = newHandler(mHandlerThread.getLooper()) {

@Overridepublic voidhandleMessage(Message msg) {//判断mHandlerThread里传来的msg,根据msg进行主页面的UI更改

switch(msg.what) {caseDownloadThread.TYPE_START://不是在这里更改UI哦,只是说在这个时间,你可以去做更改UI这件事情,改UI还是得在主线程。

Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");break;caseDownloadThread.TYPE_FINISHED:

Log.e(TAG,"7.主线程知道Download线程下载完成了...这时候可以更改主界面UI,收工");break;default:break;

}super.handleMessage(msg);

}

};//子线程注入主线程的mUIhandler,可以在子线程执行任务的时候,随时发送消息回来主线程

mHandlerThread.setUIHandler(mUIhandler);//子线程开始下载

mHandlerThread.startDownload();

}

@Overrideprotected voidonDestroy() {//有2种退出方式

mHandlerThread.quit();//mHandlerThread.quitSafely(); 需要API>=18

super.onDestroy();

}

}

运行的Log日志如下

cd925936eff0e52474374399cd672274.png

源码解析

public class HandlerThread extends Thread {

HandlerThread 本质是一个线程,只是其持有了 handler,所以可在子线程进行消息处理和分发。

接下去看下构造函数相关的:

int mPriority;//优先级

int mTid = -1;

Looper mLooper;//自带的Looper

private@Nullable Handler mHandler;publicHandlerThread(String name) {super(name);

mPriority=Process.THREAD_PRIORITY_DEFAULT;

}/*** Constructs a HandlerThread.

*@paramname

*@parampriority The priority to run the thread at. The value supplied must be from

* {@linkandroid.os.Process} and not from java.lang.Thread.*/

public HandlerThread(String name, intpriority) {super(name);

mPriority=priority;

}

这里有两个构造方法,一个 HandlerThread(String name),一个 HandlerThread(String name, int priority),我们可以自己设定线程的名字以及优先级。注意!是 Process 里的优先级而不是Thread 的。

/*** Call back method that can be explicitly overridden if needed to execute some

* setup before Looper loops.*/

protected voidonLooperPrepared() {

}

@Overridepublic voidrun() {

mTid=Process.myTid();

Looper.prepare();synchronized (this) {

mLooper=Looper.myLooper();

notifyAll();

}

Process.setThreadPriority(mPriority);

onLooperPrepared();

Looper.loop();

mTid= -1;

}

这里面有一个方法 onLooperPrepared(),在实际中,我们可以重写这个方法做一些初始化的操作,这个 run() 是重点。

run方法中首先获取线程 id,然后就调用了 Looper.prepare方法创建一个 Looper,接着调用了 Looper.myLooper方法获取到了当前线程的 Looper。

接着通过 notifyAll通知等带唤醒,这里的等待是在 HandlerThread的 getLooper方法里调用的 wait方法,getLooper方法是为了获取该 HandlerThread中的 Looper。

如果在没调用 HandlerThread的 start方法开启线程前就调用 getLooper方法就通过 wait方法暂时先进入等待,等到 run方法运行后再进行唤醒。唤醒之后 run方法中继续设置了构造函数中传入的优先级,接着调用了onLooperPrepared方法,该方法是个空实现,该方法是为了在 Looper开启轮询之前如果要进行某些设置,可以复写该方法。

最后调用Looper.loop开启轮询。退出的时候,将 mTid  = -1;

publicLooper getLooper() {if (!isAlive()) {return null;

}//If the thread has been started, wait until the looper has been created.

synchronized (this) {while (isAlive() && mLooper == null) {try{

wait();

}catch(InterruptedException e) {

}

}

}returnmLooper;

}

这个方法是获取当前的 Looper,可以看到如果没有获取的时候就一直等待直到获取,而前面也提到了获取到了就唤醒了所有的线程,看来这是线程的等待-唤醒机制应用。

publicHandler getThreadHandler() {if (mHandler == null) {

mHandler= newHandler(getLooper());

}returnmHandler;

}

这个是获取 HandlerThread 绑定的 Looper 线程的 Handler

public booleanquit() {

Looper looper=getLooper();if (looper != null) {

looper.quit();return true;

}return false;

}public booleanquitSafely() {

Looper looper=getLooper();if (looper != null) {

looper.quitSafely();return true;

}return false;

}

可以看到这两个方法去退出线程的 Looper 循环,那么这两个方法有什么区别呢,实际上都是调用了 MessageQueue 的 quit() 方法,源码如下:

void quit(booleansafe) {if (!mQuitAllowed) {throw new IllegalStateException("Main thread not allowed to quit.");

}synchronized (this) {if(mQuitting) {return;

}

mQuitting= true;if(safe) {

removeAllFutureMessagesLocked();

}else{

removeAllMessagesLocked();

}//We can assume mPtr != 0 because mQuitting was previously false.

nativeWake(mPtr);

}

}

可以看到: 当我们调用 quit 方法的时候,实际上执行了 MessageQueue 中的 removeAllMessagesLocked 方法,该方法的作用是把 MessageQueue 消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过 sendMessageDelayed 或通过 postDelayed 等方法发送的需要延迟执行的消息,只要不是立即执行的消息都是延迟消息)还是非延迟消息。

而 quitSafely 方法时,实际上执行了 MessageQueue 中的 removeAllFutureMessagesLocked 方法,通过名字就可以看出,该方法只会清空 MessageQueue 消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让 Handler 去处理,quitSafely 相比于 quit 方法安全之处在于清空消息之前会派发所有的非延迟消息,一句话,就是清除未来需要执行的消息。

这两个方法有一个共同的特点就是:Looper 不再接收新的消息了,消息循环就此结束,此时通过 Handler 发送的消息也不会在放入消息杜队列了,因为消息队列已经退出了。应用这2个方法的时候需要注意的是:quit 方法从 API 1 就开始存在了,比较早,而 quitSafely 直到 API 18 才添加进来.

总结

如果经常要开启线程,接着又是销毁线程,这是很耗性能的,HandlerThread很好的解决了这个问题;

HandlerThread由于异步操作是放在 Handler的消息队列中的,所以是串行的,但只适合并发量较少的耗时操作。

HandlerThread用完记得调用退出方法。

注意使用 handler 避免出现内存泄露

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值