1. Java多线程基础
2. Android常用线程
HandlerThread:
HandlerThread:线程中实现了Handler机制。
// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
Android 的 Handler 机制
本质:在某个指定的运行中的线程上执行代码
思路:在接受任务的线程上执行循环判断
基本实现:
Thread 里 while 循环检查,加上 Looper(优势在于⾃定义 Thread 的代码可以少写很多),再加上 Handler(优势在于功能分拆,而且可以有多个 Handler)。
Java 的 Handler 机制:
HandlerThread:具体的线程
Looper:负责循环、条件判断和任务执⾏
Handler:负责任务的定制和线程间传递
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
/**
* 源码分析:此处调用的是父类(Thread类)的start(),最终回调HandlerThread的run()
*/
@Override
public void run() {
// 1. 获得当前线程的id
mTid = Process.myTid();
// 2. 创建1个Looper对象 & MessageQueue对象
Looper.prepare();
// 3. 通过持有锁机制来获得当前线程的Looper对象
synchronized (this) {
// 发出通知:当前线程已经创建mLooper对象成功
// 此处主要是通知getLooper()中的wait()
mLooper = Looper.myLooper();
notifyAll();
// 此处使用持有锁机制 + notifyAll() 是为了保证后面获得Looper对象前就已创建好Looper对象
}
// 4. 设置当前线程的优先级
Process.setThreadPriority(mPriority);
// 5. 在线程循环前做一些准备工作 ->>分析1
// 该方法实现体是空的,子类可实现 / 不实现该方法
onLooperPrepared();
// 6. 进行消息循环,即不断从MessageQueue中取消息 & 派发消息
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
// 若线程不是存活的,则直接返回null
if (!isAlive()) {
return null;
}
// 若当前线程存活,再判断线程的成员变量mLooper是否为null
// 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
// 此处会调用wait方法去等待
wait();
} catch (InterruptedException e) {
}
}
}
// 上述步骤run()使用 持有锁机制 + notifyAll() 获得Looper对象后
// 则通知当前线程的wait()结束等待 & 跳出循环
// 最终getLooper()返回的是在run()中创建的mLooper对象
return mLooper;
}
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
// 方式1:quit() -> MessageQueue.quit(boolean safe)
// 特点:效率高,但线程不安全
/**
* MessageQueue.quit(false) -> removeAllMessagesLocked()
* 原理:遍历Message链表、移除所有信息的回调 & 重置为null
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
// 方式2:quitSafely() -> MessageQueue.quit(boolean safe)
// 特点:效率低,但线程安全
/**
* MessageQueue.quit(true) -> removeAllFutureMessagesLocked()
* 原理:先判断当前消息队列是否正在处理消息
* a. 若不是,则类似分析2移除消息
* b. 若是,则等待该消息处理处理完毕再使用分析2中的方式移除消息退出循环
* 结论:退出方法安全与否(quitSafe() 或 quit()),在于该方法移除消息、退出循环时是否在意当前队列是否正在处理消息
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
}
AsyncTask:
AsyncTask 的内存泄露
众所周知的原因: AsyncTask 持有外部 Activity 的引用
没提到的原因:执行中的线程不会被系统回收
Java 回收策略:没有被 GC Root 直接或间接持有引用的对象,会被回收。
GC Root:(1. 运行中的线程 2. 静态对象 3. 来自 native code 中的引用)
所以:AsyncTask 的内存泄露,其他类型的线程方案(Thread、 Executor、HandlerThread)⼀样都有,所以不要忽略它们,或者认为 AsyncTask 比别的方案更危险。并没有。
就算是使⽤ AsyncTask,只要任务的时间不⻓(例如 10 秒之内),那就完全没必要做防止内存泄露的处理。
AsyncTask原理及不足
Service 和 IntentService
Service:后台任务的活动空间。适用场景:音乐播放器等。
IntentService:执行单个任务后自动关闭的 Service
IntentService原理
Executor、 AsyncTask、 HandlerThead、 IntentService 的选择
原则:哪个简单用哪个,能用 Executor 就用 Executor。
需要用到「后台线程推送任务到 UI 线程」时,再考虑 AsyncTask 或者 Handler。
HandlerThread 的使⽤场景:原本它设计的使⽤场景是「在已经运⾏的指定线程上执行代码」,但现实开发中,除了主线程之外,几乎没有这种需求,因为 HandlerThread 和 Executor相比在实际应⽤中并没什么优势,反而用起来会麻烦⼀点。
IntentService:首先,它是⼀个 Service;另外,它在处理线程本身,没有比 Executor 有任何优势。
关于 Executor 和 HandlerThread 的关闭
如果在界面组件⾥创建 Executor 或者 HandlerThread,记得要在关闭的时候(例如Activity.onDestroy() )关闭 Executor 和 HandlerThread。
@Override
protected void onDestroy() {
super.onDestroy();
executor.shutdown();
}
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();
}