android 多线程通信,Android 多线程通信总结(一)

HandlerThread

1.HandlerThread特点及注意事项

HandlerThread本质上是一个线程类,继承自Thread。在线程内部,代码是串行处理的。

其内部拥有自己的Looper对象,也就是说它可以自己进行消息的循环。通过getLooper()方法可以将这个Looper对象传递给Handler对象,这样就可以在handleMessage()方法中执行异步任务。HandlerThread将looper对象传递给子线程进行处理,目的是为了分担MainLooper的工作量,降低了主线程的压力,使界面更加流畅。

开启一个线程起到多个线程的作用,处理任务是串行的,按消息发送顺序进行处理,但是由于因为每个任务都会以队列的形式被执行到,如果队列中某个任务执行时间过长,那么就会导致后续的任务都会被延迟处理。

HandlerThread拥有自己的消息队列,因此它不会阻塞UI线程。

创建HandlerThread后需要先调用handlerThread.start()方法,Thread会先调用run()方法,创建Looper对象。

2.应用场景

适用于会长时间在后台执行,并且间隔时间内会调用的任务。

3.Handler,Thread和HandlerThread的区别

Handler会关联一个单独的线程,Looper和消息队列,默认关联是UI线程。

HandlerThread继承自Thread,所以它本质就是一个Thread,只不过其内部有自己的Looper对象和消息队列,可以用来循环消息队列,并将消息回调到子线程中的Handler进行处理。

4.HandlerThread使用实例

1.创建实例对象

HandlerThread handlerThread = new HandlerThread("majiyao");

​ 传入参数的作用主要是标记当前线程的名字,可以任意字符串。

2.启动HandlerThread线程

//实例创建完成之后,必须要先开启线程

handlerThread.start();

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

/**

* 该callback运行于子线程

*/

class ChildCallback implements Handler.Callback {

@Override

public boolean handleMessage(Message msg) {

//在子线程中进行相应的网络请求

//通知主线程去更新UI

mUIHandler.sendMessage(msg1);

return false;

}

}

4.构建异步handler

Handler childHandler = new Handler(handlerThread.getLooper(),new ChildCallback());

​ 创建子线程Handler,获取handlerThread的Looper对象,并将回调事件传入,这样异步handler就拥有了HandlerThread的Looper对象,由于HandlerThread本身是异步线程,因此Looper也与异步线程绑定,从而handlerMessage方法也就可以异步处理耗时任务了,这样我们的Looper+Handler+MessageQueue+Thread异步循环机制构建完成。

5.源码分析

public class HandlerThread extends Thread {

int mPriority;//线程优先级

int mTid = -1;

Looper mLooper;//当前线程持有的Looper对象

private @Nullable Handler mHandler;

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;

}

/**

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

* setup before Looper loops.

*/

protected void onLooperPrepared() {

}

}

​ 从源码可以看出HandlerThread继续自Thread,构造函数的传递参数有两个,一个是name指的是线程的名称,一个是priority指的是线程优先级,我们根据需要调用即可。其中成员变量mLooper就是HandlerThread自己持有的Looper对象。onLooperPrepared()该方法是一个空实现,是留给我们必要时可以去重写的,但是注意重写时机是在Looper循环启动前,再看看run方法:

@Override

public void run() {

mTid = Process.myTid();

Looper.prepare();

synchronized (this) {

mLooper = Looper.myLooper();

notifyAll();

}

Process.setThreadPriority(mPriority);

onLooperPrepared();

Looper.loop();

mTid = -1;

}

前面我们在HandlerThread的常规使用中分析过,在创建HandlerThread对象后必须调用其start()方法才能进行其他操作,而调用start()方法后相当于启动了线程,也就是run方法将会被调用,而我们从run源码中可以看出其执行了Looper.prepare()代码,这时Looper对象将被创建,当Looper对象被创建后将绑定在当前线程(也就是当前异步线程),这样我们才可以把Looper对象赋值给Handler对象,进而确保Handler对象中的handleMessage方法是在异步线程执行的。接着将执行代码:

synchronized (this) {

mLooper = Looper.myLooper();

notifyAll();

}

这里在Looper对象创建后将其赋值给HandlerThread的内部变量mLooper,并通过notifyAll()方法去唤醒等待线程,最后执行Looper.loop();代码,开启looper循环语句。那这里为什么要唤醒等待线程呢?我们来看看,getLooper方法

/**

* This method returns the Looper associated with this thread. If this thread not been started

* or for any reason 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() {

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) {

}

}

}

return mLooper;

}

​ 事实上可以看出外部在通过getLooper方法获取looper对象时会先判断当前线程是否启动了,如果线程已经启动,那么将会进入同步语句并判断Looper是否为null,为null则代表Looper对象还没有被赋值,也就是还没被创建,此时当前调用线程进入等待阶段,直到Looper对象被创建并通过 notifyAll()方法唤醒等待线程,最后才返回Looper对象,之所以需要等待唤醒机制,是因为Looper的创建是在子线程中执行的,而调用getLooper方法则是在主线程进行的,这样我们就无法保障我们在调用getLooper方法时Looper已经被创建,到这里我们也就明白了在获取mLooper对象时会存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值,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方法时,其内部实际上是调用Looper的quit方法而最终执行的则是MessageQueue中的removeAllMessagesLocked方法(Handler消息机制知识点),该方法主要是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送)还是非延迟消息。

当调用quitSafely方法时,其内部调用的是Looper的quitSafely方法而最终执行的是MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理完成后才停止Looper循环,quitSafely相比于quit方法安全的原因在于清空消息之前会派发所有的非延迟消息。最后需要注意的是Looper的quit方法是基于API 1,而Looper的quitSafely方法则是基于API 18的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值