1.HandlerThread 的使用场景和用法?
HandlerThread 是 Android 封装的一个线程类,将 Thread 跟 Handler 封装。使用步骤如下:
创建 HandlerThread
实例对象:
HandlerThread mHandlerThread = new HandlerThread("mHandlerThread");
启动线程:
mHandlerThread .start();
创建Handler对象,重写handleMessage方法:
Handler mHandler= new Handler( mHandlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
//消息处理
return true;
}
});
使用工作线程Handler向工作线程的消息队列发送消息:
Message message = Message.obtain();
message.what = “2”
message.obj = "骚风"
mHandler.sendMessage(message);
结束线程,即停止线程的消息循环:
mHandlerThread.quit();
2.IntentService 的应用场景和使用姿势?
IntentService
是 Service
的子类,默认为我们开启了一个工作线程,使用这个工作线程逐一处理所有启动请求,在任务执行完毕后会自动停止服务,使用简单,只要实现一个方法 onHandleIntent
,该方法会接收每个启动请求的 Intent
,能够执行后台工作和耗时操作。可以启动 IntentService
多次,而每一个耗时操作会以队列的方式在 IntentService 的 onHandlerIntent
回调方法中执行,并且,每一次只会执行一个工作线程,执行完第一个再执行第二个。并且等待所有消息都执行完后才终止服务。
IntentService
适用于 APP 在不影响当前用户的操作的前提下,在后台默默的做一些操作。
IntentService源码:
- 通过
HandlerThread
单独开启一个名为IntentService
的线程 - 创建一个名叫
ServiceHandler
的内部Handler
- 把内部Handler与HandlerThread所对应的子线程进行绑定
- 通过
onStartCommand()
传递给服务intent
,依次插入到工作队列中,并逐个发送给onHandleIntent()
- 通过
onHandleIntent()
来依次处理所有Intent
请求对象所对应的任务
示例代码:
public class MyIntentService extends IntentService {
public static final String TAG ="MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
boolean isMainThread = Thread.currentThread() == Looper.getMainLooper().getThread();
Log.i(TAG,"is main thread:"+isMainThread); // 这里会打印false,说明不是主线程
// 模拟耗时操作
download();
}
/**
* 模拟执行下载
*/
private void download(){
try {
Thread.sleep(5000);
Log.i(TAG,"下载完成...");
}catch (Exception e){
e.printStackTrace();
}
}
}
3.AsyncTask的优点和缺点?
AsyncTask是一个抽象类,主要由Handler+2个线程池构成,SERIAL_EXECUTOR是任务队列线程池,用于调度任务,按顺序排列执行,THREAD_POOL_EXECUTOR是执行线程池,真正执行具体的线程任务。Handler用于工作线程和主线程的异步通信。
优点:AsyncTask操作简单,轻便,适用简单的异步操作
缺点:存在新开大量线程,消耗系统资源的风险
4.谈谈你对 Activity.runOnUiThread 的理解?
一般是用来将一个runnable绑定到主线程,在runOnUiThread源码里面会判断当前runnable是否是主线程,如果是直接run,如果不是,通过一个默认的空构造函数handler将runnable post 到looper里面,创建构造函数handler,会默认绑定一个主线程的looper对象
5.子线程能否更新UI?为什么?
子线程是不能直接更新UI的
注意这句话,是不能直接更新,不是不能更新(极端情况下可更新)
绘制过程要保持同步(否则页面不流畅),而我们的主线程负责绘制ui,极端情况就是,在Activity的onResume(含)之前的生命周期中子线程都可以进行更新ui,也就是 onCreate,onStart和onResume,此时主线程的绘制还没开始。
6.谈谈 Handler 机制和原理?
说道handler就不得不提 Looper
,MessageQueue
和 Handler
。
Looper:它在消息机制里是用来把普通线程转成looper线程的(Looper.prepare()),并担当消息循环的任务,当然,UI
线程是通过Looper.PrepareMainLoper()。
它会开启无限循环(Loper.loop())并不停的从 MessageQueue
中查看是否有新消息,如果有就拿出来处理,如果没有呢,就阻塞(其实真正的阻塞在 MessageQueue
的 next
里)。Loper在构造的时候会构建一个 MessageQueue
并持有它的引用。
MessageQueue
: 消息队列,它用来插入(enqueueMessage),读取消息(next)。它虽然是个消息队列,但实际实现是个单链表,因为单链表在插入和读取上有优势。它插入是根据时间戳排序来的,根据时间戳的排序来指定next的下一条消息。而当next读取的时候,如果有消息,就取出来,如果没有,就阻塞。如果 next
取出来是 null,那么就表示整个app可以结束运行了。
Handler
的主要工作就是负责消息的发送和接收了。发送消息是 post
和 send
的一堆函数,其实到最后 enqueueMessage()
来进行入队,接收消息处理则是通过msg的next里返回的 looper
在交给 handler
的 dispatchMessage()
方法。Handler
在哪个线程之下构造就会持有当前这个线程的 looper
引用。Handler
工作的时候首先会判断 msg.callback
(Runnable对象)是不是 null,不为 null 就会变为 handlerCallback
(执行msg的callback),然后检查 mCallback
是不是 null,不为 null 执行 handlerMessage
。而这个 mCallback
则又是通过 msg
的 target
(对应的handler)来进行回调调用的。
那么整个流程是:activityThread
里 main
方法执行即 app 启动的时候,会把当前线程转为 UI
线程(Loper.PrepareMainLoper()),并且会构造 activityThread
(下面简称actThread)实例,它的 attach
方法会创建 Binder
线程通道(ApplicationThread
,用来接受系统进程传递过来的信息的线程)。然后通过 Looper.loop
来开启无限循环。无限循环开启后,需要一个 handler
和消息队列进行交互,这个handler
(ActivityThread.H,简称mH)主要用来管理四大组件的生命周期,启动,停止等消息类型。actThread
通过之前创建的bind线程与ams进行通信,ams执行完actThread的请求后会回调 binder
线程的 bind
方法,然后 binder
线程会向 mH
发送消息,mH
收到消息后会回到 actThread
中去执行。
7.为什么在子线程中创建Handler会抛异常?
主线程默认执行了looper.prepare方法,此时使用Handler就可以往相信队列中不断进行发送消息和取出消息处理,反之没有执行looper.prepare方法,就会抛出异常,这个在源码中有所体现
8.从源码角度分析Handler的post和sendMessage方法的区别和应用场景?
handler.post和handler.sendMessage方法最后都会调用sendMessageAtTime方法进行消息的发送,但是在post方法中message是通过getPostMessage(Runnable r)这个方法获取的message,在这个方法中有这样一句代码m.callback = r ,给message的callback赋值为runnable对象,而在dispatchMessage这个方法中对消息进行分发的时候,先进行了msg.callback != null的判断,如果不为null,消息是通过handleCallback(msg);这个方法处理的,在这个方法中message.callback.run();调用的是post方法传递过来的runnable内的run方法处理消息,如果为空,再进行handler内部的callback判断mCallback != null,如果handler内的callback不为空,执行mCallback.handleMessage(msg)这个处理消息并判断返回是否为true,如果返回true,消息处理结束,如果返回false,消息交给handler的handleMessage(msg)处理。
所以区别就是调用post方法的消息是在post传递的Runnable对象的run方法中处理,而调用sendMessage方法需要重写handleMessage方法或者给handler设置callback,在callback的handleMessage中处理并返回true。
9.Handler中有Loop死循环,为什么没有阻塞主线程,原理是什么?
可以这样简单的来理解一下,一个Thread对应一个Looper和一个MessageQueue
这个MessageQueue是个一个阻塞队列,类似BlockingQueue,不同之处在于MessageQueue的阻塞方式是通过Pipe机制实现的。
阻塞队列,就是当队列里没有数据时,如果调用获取队首数据的方法时,当前线程会被阻塞(相当于执行了线程的wait方法),如果队列里面有了插入了新数据,则会唤醒被阻塞的方法(相当于执行了线程的notify方法),并返回该数据。再来看MessageQueue,这里的数据指的就是是每一个消息,这个消息则是通过handler来发送的。
综上所述,线程并没有一直死循环的工作,而是在没消息时被暂时挂起了,当有新消息进来的时候,就会又开始工作。