1. HandlerThread 的使用场景和实现原理
HandlerThread 其实就是一个带有消息循环的线程,特别适合用在需要长期在后台运行,并且需要按顺序处理任务的场景。
使用场景:
- 网络请求队列处理
- 文件读写操作
- 数据库操作
实现原理:
public class HandlerThread extends Thread {
@Override
public void run() {
Looper.prepare(); // 创建消息循环
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop(); // 开启消息循环
}
}
底层实现原理
让我以生动形象的方式讲解 HandlerThread 的实现原理:
🌟 HandlerThread 的形象比喻
想象一下有一个专门的快递分拣员(HandlerThread),他有以下特点:
- 他有自己的工作台(Looper)
- 有一个任务箱(MessageQueue)
- 按顺序处理包裹(Message)
- 一直工作直到被告知停止(quit)
🔄 工作流程
public class HandlerThread extends Thread {
Looper mLooper;
private @Nullable Handler mHandler;
@Override
public void run() {
// 1. 准备工作台
Looper.prepare();
// 2. 通知工作台准备完毕
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
// 3. 开始无限循环处理消息
Looper.loop();
}
// 获取该线程的Looper
public Looper getLooper() {
// 如果线程没有启动或已经结束,返回null
if (!isAlive()) {
return null;
}
// 等待线程创建Looper
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
}
📊 流程图
graph TD
A[创建HandlerThread] --> B[调用start方法]
B --> C[run方法执行]
C --> D[Looper.prepare创建Looper]
D --> E[保存Looper实例并通知等待线程]
E --> F[Looper.loop开启消息循环]
F --> G{是否调用quit?}
G -->|否| F
G -->|是| H[结束循环]
I[外部获取Looper] --> J{线程是否存活?}
J -->|是| K[等待Looper创建完成]
J -->|否| L[返回null]
K --> M[返回Looper实例]
🌈 使用示例
// 创建HandlerThread
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
// 启动线程
handlerThread.start();
// 创建Handler
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
// 发送消息
handler.sendMessage(msg);
// 使用完毕后退出
handlerThread.quit();
💡 关键点总结
-
初始化过程:
- 创建 HandlerThread 实例
- 调用 start() 方法启动线程
- 在 run() 方法中创建 Looper
- 保存 Looper 实例并通知等待线程
- 开启消息循环
-
同步机制:
- 使用 synchronized 和 wait/notify 确保 Looper 创建完成
- getLooper() 方法会等待直到 Looper 创建完成
-
消息处理:
- 所有消息在同一个线程中按顺序处理
- 通过 Handler 发送消息到 HandlerThread
- 可以通过 quit() 方法终止消息循环
-
生命周期:
- 创建 → 启动 → 循环处理消息 → 退出
- 需要手动调用 quit() 来终止线程
这就是 HandlerThread 的核心实现原理,它通过封装 Handler 和 Looper 的创建过程,为我们提供了一个简单易用的消息处理线程。
更生活化的例子
让我用一个真实的快递分拣中心的例子来解释 HandlerThread。
想象你开了一个快递分拣中心:
- 前台接待员(主线程)
- 负责接收快递包裹
- 负责与客户沟通
- 不能去后面分拣,否则没人接待客户了
- 分拣工人(HandlerThread)
- 专门在后面分拣包裹
- 按照包裹到达的顺序处理
- 一个人按顺序处理,不会搞乱
- 传送带(Handler)
- 用来传递包裹和消息
- 接待员用它传递包裹给分拣工人
- 分拣工人用它通知接待员分拣完成
看代码示例:
public class ExpressCenter extends AppCompatActivity {
private HandlerThread sorterThread; // 分拣工人
private Handler sorterHandler; // 传递给分拣工人的传送带
private Handler frontDeskHandler; // 前台接待的传送带
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 雇佣一个分拣工人
sorterThread = new HandlerThread("分拣工人");
sorterThread.start();
// 2. 给前台一个传送带
frontDeskHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
// 前台收到分拣完成的通知,更新状态
if (msg.what == 1) {
String packageId = (String) msg.obj;
updateUI("包裹 " + packageId + " 分拣完成");
}
}
};
// 3. 给分拣工人一个传送带
sorterHandler = new Handler(sorterThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 分拣工人开始处理包裹
if (msg.what == 1) {
String packageId = (String) msg.obj;
// 模拟分拣时间
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 分拣完成,通知前台
Message doneMsg = frontDeskHandler.obtainMessage(1);
doneMsg.obj = packageId;
frontDeskHandler.sendMessage(doneMsg);
}
}
};
// 4. 当新包裹到达时
Button newPackageButton = findViewById(R.id.newPackageButton);
newPackageButton.setOnClickListener(v -> {
// 生成包裹ID
String packageId = "PKG" + System.currentTimeMillis();
// 通过传送带发给分拣工人
Message msg = sorterHandler.obtainMessage(1);
msg.obj = packageId;
sorterHandler.sendMessage(msg);
updateUI("包裹 " + packageId + " 已交给分拣工人");
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 5. 下班了,让分拣工人回家
sorterThread.quitSafely();
}
}
特点说明:
-
有序处理:
- 就像快递分拣一样,一个包裹一个包裹按顺序处理
- 不会因为同时处理多个包裹而混乱
-
专人专责:
- 分拣工人(HandlerThread)专门负责分拣
- 前台(主线程)专门负责接待
- 各司其职,互不干扰
-
通信方便:
- 通过传送带(Handler)方便地传递包裹和消息
- 前台知道每个包裹的分拣状态
- 分拣工人也能及时通知前台
生活中的其他例子:
- 餐厅的后厨(一个厨师按顺序做菜)
- 咖啡店的咖啡师(按顺序制作咖啡)
- 理发店的理发师(按顺序为客人理发)
这些场景都和 HandlerThread 很像:
- 需要按顺序处理任务
- 有专门的人(线程)处理
- 处理过程不能打断
- 需要和前台及时通信
这样理解 HandlerThread 是不是清晰多了?
2. IntentService 的应用场景和实现原理
IntentService 是一个很特别的 Service,它能自动处理异步任务并在处理完成后自动停止。
应用场景:
- 后台下载
- 数据备份
- 文件上传
核心实现:
public abstract class IntentService extends Service {
private final class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj); // 处理Intent
stopSelf(msg.arg1); // 任务完成后自动停止
}
}
}
底层实现原理
IntentService 工作原理详解
想象一下,IntentService 就像是一个有序的快递处理中心:
- 你发送的每个 Intent 就像一个快递包裹
- HandlerThread 就像是一个专门处理包裹的工作人员
- ServiceHandler 就像是工作人员的任务清单
核心实现代码解析
public abstract class IntentService extends Service {
// 专门处理任务的线程
private volatile HandlerThread mServiceThread;
// 用于在工作线程处理消息的 Handler
private volatile ServiceHandler mServiceHandler;
// Handler 的具体实现
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// 处理具体的 Intent
onHandleIntent((Intent)msg.obj);
// 处理完一个消息后,检查是否还有待处理的消息
// 如果没有,就停止 Service
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
super.onCreate();
// 创建后台工作线程
mServiceThread = new HandlerThread("IntentService[" + mName + "]");
// 启动线程
mServiceThread.start();
// 获取工作线程的 Looper
mServiceHandler = new ServiceHandler(mServiceThread.getLooper());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 将 Intent 包装成消息,发送给工作线程处理
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
// 退出工作线程的消息循环
mServiceThread.quit();
}
}
工作流程图
graph TD
A[应用程序] -->|startService| B[IntentService]
B -->|onCreate| C[创建HandlerThread]
C -->|start| D[创建ServiceHandler]
B -->|onStartCommand| E[发送消息到Handler]
E -->|handleMessage| F[onHandleIntent处理任务]
F -->|处理完成| G[stopSelf停止服务]
G -->|如果没有新消息| H[onDestroy销毁服务]
关键特点总结
-
串行处理
- 所有任务在同一个工作线程中按顺序处理
- 不需要担心线程同步问题
-
自动停止
- 处理完所有任务后自动停止服务
- 不需要手动管理服务的生命周期
-
工作流程
- 创建服务时启动工作线程
- 接收到 Intent 时将其封装成消息
- 在工作线程中处理消息
- 处理完成后检查是否需要停止服务
使用示例
public class DownloadService extends IntentService {
public DownloadService() {
super("DownloadService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 在工作线程中执行下载任务
String url = intent.getStringExtra("url");
downloadFile(url);
}
}
// 使用方式
Intent intent = new Intent(context, DownloadService.class);
intent.putExtra("url", "http://example.com/file.pdf");
context.startService(intent);
适用场景
- 后台下载:按顺序下载多个文件
- 数据同步:按顺序同步多个数据源
- 定时任务:需要在后台按顺序处理的定时任务
这就是 IntentService 的核心实现原理,它通过 HandlerThread 和 Handler 的配合,实现了一个简单但功能强大的异步任务处理机制。
3. AsyncTask 的优缺点和实现原理
优点:
- 简化异步操作
- 自动切换线程
- 可以显示进度
缺点:
- 可能导致内存泄露
- 在 Activity 旋转时容易出问题
- 在 Android 3.0 后串行执行任务
实现原理:
public abstract class AsyncTask<Params, Progress, Result> {
private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR;
@MainThread
protected void onPreExecute() {} // 主线程执行
@WorkerThread
protected abstract Result doInBackground(Params... params); // 工作线程执行
@MainThread
protected void onPostExecute(Result result) {} // 主线程执行
}
底层原理
AsyncTask 工作流程图
graph TD
A[新建AsyncTask] --> B[执行execute]
B --> C[onPreExecute 主线程]
C --> D[doInBackground 后台线程]
D --> |进度更新| E[onProgressUpdate 主线程]
D --> F[onPostExecute 主线程]
style A fill:#f9f,stroke:#333,stroke-width:2px
style D fill:#bbf,stroke:#333,stroke-width:2px
style F fill:#bfb,stroke:#333,stroke-width:2px
详细实现原理讲解
想象 AsyncTask 就像一个搬家公司的工作流程:
- 准备阶段 (构造函数):
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() {
// 相当于搬家公司的工人准备工具
return doInBackground(mParams);
}
};
mFuture = new FutureTask<Result>(mWorker);
}
- 开工前的准备 (execute 方法):
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
// 就像搬家前的现场勘察
onPreExecute(); // 在主线程执行
// 派遣工人去干活
sExecutor.execute(mFuture);
return this;
}
- 实际工作过程 (doInBackground):
protected abstract Result doInBackground(Params... params);
// 这就像搬家工人在实际搬运物品
// 在后台线程执行耗时操作
- 进度通知 (publishProgress):
protected final void publishProgress(Progress... values) {
// 就像工人实时汇报工作进度
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
- 完工通知 (onPostExecute):
protected void onPostExecute(Result result) {
// 相当于工作完成后的验收环节
}
线程池机制
AsyncTask 使用的线程池就像一个搬家公司的人力资源管理:
private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
// 核心线程数(常驻工人)
CORE_POOL_SIZE,
// 最大线程数(最多可以雇佣的临时工)
MAXIMUM_POOL_SIZE,
// 空闲线程等待新任务的最长时间
KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
// 任务队列(等待处理的搬家订单)
sPoolWorkQueue
);
消息传递机制
private static class InternalHandler extends Handler {
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_PROGRESS:
// 通知主线程更新进度
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_RESULT:
// 通知主线程任务完成
result.mTask.finish(result.mData[0]);
break;
}
}
}
总结要点:
-
线程切换:
- onPreExecute:主线程
- doInBackground:后台线程
- onProgressUpdate:主线程
- onPostExecute:主线程
-
工作流程:
- 创建 → 准备 → 执行 → 进度更新 → 完成
-
特点:
- 自动线程切换
- 支持进度更新
- 有序的任务队列
- 结果自动回调主线程
这就像一个完整的搬家服务:从接单、准备工具、实际搬运、进度通知到最终完工验收,每个环节都井然有序。
4. Activity.runOnUiThread 的理解
这是一个很巧妙的设计,它能确保代码在主线程执行:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() == mUiThread) {
action.run();
} else {
mHandler.post(action);
}
}
底层原理
1. 形象化解释
想象一下我们在玩篮球:
- 主线程就像是球场上的主裁判
- runOnUiThread 就像是一个"传球"的动作
- Runnable 就是我们要传递的球
当我们调用 runOnUiThread 时,系统会先看:
- 如果当前是主裁判(主线程)持球,就直接投篮(执行任务)
- 如果是其他球员(子线程)持球,就把球传给主裁判(通过 Handler 转发给主线程)
2. 源码分析
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() == mUiThread) {
// 如果当前就是UI线程,直接执行
action.run();
} else {
// 如果是其他线程,通过Handler发送到UI线程
mHandler.post(action);
}
}
3. 流程图
graph TD
A[调用 runOnUiThread] --> B{是否在主线程?}
B -->|是| C[直接执行 Runnable]
B -->|否| D[通过 Handler 投递到消息队列]
D --> E[主线程 Handler 处理消息]
E --> F[执行 Runnable]
4. 详细执行流程
-
判断当前线程
- 通过
Thread.currentThread() == mUiThread
判断 - mUiThread 是 Activity 持有的主线程引用
- 通过
-
主线程情况
- 直接调用
action.run()
- 不需要任何线程切换
- 执行效率最高
- 直接调用
-
非主线程情况
- 使用 Activity 的 mHandler 发送消息
- Handler 将 Runnable 包装成 Message
- 投递到主线程的 MessageQueue
- Looper 循环取出并执行
5. 使用示例
// 在任意线程中
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
// 这里的代码保证在主线程执行
textView.setText("更新UI");
}
});
6. 注意事项
-
生命周期安全
- Activity 销毁时要注意取消未执行的 Runnable
- 可能导致内存泄露
-
执行时机
- 非主线程调用时,执行时机取决于主线程消息队列的情况
- 不能保证立即执行
-
性能考虑
- 主线程直接执行时性能最好
- 通过 Handler 调度会有轻微的性能损耗
7. 实际应用建议
- 简单任务
runOnUiThread(() -> updateUI());
- 带延时的任务
runOnUiThread(new Runnable() {
@Override
public void run() {
// 可以配合 Handler 实现延时
mHandler.postDelayed(this, 1000);
}
});
- 取消任务
private Runnable mUpdateUITask = new Runnable() {
@Override
public void run() {
updateUI();
}
};
// 在需要时取消
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mUpdateUITask);
}
这个机制看似简单,但实际上是 Android 线程切换的一个非常优雅的设计,让开发者可以在任意线程安全地更新 UI。
5. Android 子线程更新 UI
严格来说,Android 是允许子线程更新 UI 的,但需要满足两个条件:
- 这个 View 必须是你自己创建的
- 必须在 ViewRootImpl 创建之前
但是为了线程安全,我们还是应该在主线程更新 UI。
底层原理
一、基本原理讲解
想象一下,UI 就像一个画布,而 Android 系统就是这个画布的保安。一般情况下,保安只允许主线程(专业画家)在画布上作画,其他线程(业余画家)都会被拦住。
但是这个"拦截机制"是在 ViewRootImpl 被创建的时候才开始的。就像画布还没有被架设好的时候,任何人都可以在上面涂鸦。
二、源码分析
// ViewRootImpl.java
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
这段代码就是那个"保安",它会检查当前操作 UI 的线程是否是创建 View 的原始线程。
三、具体流程
- View 的创建阶段
// Activity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 此时 ViewRootImpl 还未创建
}
- ViewRootImpl 的创建时机
// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
// ...
ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
// ...
}
四、流程图
graph TD
A[Activity onCreate] --> B[setContentView]
B --> C[View 创建]
C --> D[第一次 ViewRootImpl 创建]
D --> E{是否在主线程更新 UI}
E -->|是| F[允许更新 UI]
E -->|否| G[检查线程]
G --> H{是否在 ViewRootImpl 创建前}
H -->|是| F
H -->|否| I[抛出异常]
五、实际演示
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view);
// 这时候可以在子线程更新 UI(不推荐)
new Thread(new Runnable() {
@Override
public void run() {
textView.setText("Hello"); // 这里不会崩溃
}
}).start();
// 等待一会,ViewRootImpl 创建后
textView.postDelayed(new Runnable() {
@Override
public void run() {
new Thread(new Runnable() {
@Override
public void run() {
try {
textView.setText("World"); // 这里会崩溃
} catch (Exception e) {
Log.e("MainActivity", "不能在子线程更新UI");
}
}
}).start();
}
}, 1000);
}
}
六、总结要点
-
时机很重要:
- ViewRootImpl 创建前:子线程可以更新 UI
- ViewRootImpl 创建后:只能主线程更新 UI
-
实际应用建议:
- 始终在主线程更新 UI
- 使用 Handler、runOnUiThread 等安全机制
- 不要依赖这个"漏洞"特性
-
常用的线程切换方式:
// 方式一:Handler
private Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(() -> updateUI());
// 方式二:runOnUiThread
runOnUiThread(() -> updateUI());
// 方式三:View.post
view.post(() -> updateUI());
这个机制就像是一个特殊时期的"漏洞",虽然存在,但在实际开发中我们应该遵循 Android 的线程规范,始终在主线程更新 UI,这样才能保证应用的稳定性和可维护性。
6. Android 消息机制原理
核心组件:
- Handler:发送和处理消息
- Looper:消息循环
- MessageQueue:消息队列
- Message:消息本身
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler();
Looper.loop();
}
}
底层原理
一、核心角色介绍
想象一个邮局系统:
- Handler(邮递员):负责投递和处理信件
- Message(信件):需要传递的消息
- MessageQueue(邮箱):存放消息的队列
- Looper(邮局分拣中心):不断循环处理消息
二、详细工作流程
1. 消息的发送过程
// Handler 发送消息
public boolean sendMessage(Message msg) {
// 1. 给消息标记目标 Handler
msg.target = this;
// 2. 将消息放入队列
return mQueue.enqueueMessage(msg, uptimeMillis);
}
2. 消息队列的维护
// MessageQueue 维护消息
public boolean enqueueMessage(Message msg, long when) {
// 根据时间顺序将消息插入队列
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
}
// ...
}
3. Looper 的循环处理
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
// 1. 取出下一条消息
Message msg = queue.next();
if (msg == null) {
return;
}
// 2. 分发消息给对应的 Handler 处理
msg.target.dispatchMessage(msg);
}
}
三、流程图
graph TD
A[Handler] -->|1. sendMessage| B[Message]
B -->|2. enqueueMessage| C[MessageQueue]
D[Looper] -->|3. loop循环取消息| C
D -->|4. dispatchMessage| A
A -->|5. handleMessage| E[处理消息]
四、关键要点解析
- 消息池机制
// Message 复用机制
Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
- 同步屏障机制
// 发送同步屏障
public int postSyncBarrier() {
// 插入一个没有 target 的 Message
// 用于拦截同步消息,优先处理异步消息
}
五、实际应用示例
// 典型使用场景
class MyActivity extends Activity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_UI:
updateUI();
break;
case MSG_SHOW_TOAST:
showToast();
break;
}
}
};
private void doWork() {
new Thread(new Runnable() {
@Override
public void run() {
// 耗时操作
Message msg = Message.obtain();
msg.what = MSG_UPDATE_UI;
mHandler.sendMessage(msg);
}
}).start();
}
}
六、完整流程总结
-
初始化阶段:
- 主线程创建时自动初始化 Looper
- 创建 MessageQueue
- 创建主线程 Handler
-
消息发送阶段:
- 创建 Message 对象
- Handler 将消息加入 MessageQueue
- 消息按时间顺序排队
-
消息处理阶段:
- Looper 不断从队列提取消息
- 分发给对应的 Handler
- Handler 处理消息
-
线程切换:
- 子线程通过 Handler 发送消息到主线程
- 主线程 Looper 处理消息实现线程切换
七、性能优化建议
- 使用 Message.obtain() 而不是 new Message()
- 及时移除不需要的消息
- 避免在 Handler 中执行耗时操作
- 注意内存泄漏问题
这套机制的精妙之处在于:
- 完美解决了 Android 的线程通信问题
- 保证了 UI 操作的线程安全
- 通过消息池和同步屏障提供了性能保障
7. 子线程创建 Handler 抛异常原因
因为 Handler 需要依附于 Looper,而子线程默认没有 Looper:
public Handler() {
if (Looper.myLooper() == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
}
底层原理
子线程创建 Handler 抛异常的原理详解
想象一下,Handler 就像是一个邮递员,它需要依赖于一个邮局系统(Looper)来工作。
1. 创建过程解析
// 当我们在子线程中直接创建 Handler
public class MyThread extends Thread {
private Handler handler;
@Override
public void run() {
// 直接创建 Handler 会抛出异常
handler = new Handler(); // RuntimeException!
}
}
2. 源码分析
// Handler 构造函数
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback, boolean async) {
// 获取当前线程的 Looper
mLooper = Looper.myLooper();
// 关键点:如果 Looper 为空则抛出异常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
3. 正确的使用方式
public class MyThread extends Thread {
private Handler handler;
@Override
public void run() {
// 1. 首先为当前线程创建 Looper
Looper.prepare();
// 2. 然后创建 Handler
handler = new Handler();
// 3. 开启消息循环
Looper.loop();
}
}
关键要点总结:
-
为什么会抛异常?
- Handler 必须绑定一个 Looper
- 主线程默认有 Looper(ActivityThread 会自动创建)
- 子线程默认没有 Looper
-
如何解决?
- 调用 Looper.prepare() 创建 Looper
- 调用 Looper.loop() 开启消息循环
- 使用 HandlerThread(它内部已经做好了这些工作)
-
实际应用建议:
// 推荐使用 HandlerThread
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
完整的消息处理流程:
sequenceDiagram
participant Thread as 子线程
participant Looper as Looper
participant Handler as Handler
participant MessageQueue as 消息队列
Thread->>Looper: 1. Looper.prepare()
Looper->>MessageQueue: 2. 创建消息队列
Thread->>Handler: 3. 创建 Handler
Handler->>Looper: 4. 获取 Looper
Looper->>Handler: 5. 绑定消息队列
Thread->>Looper: 6. Looper.loop()
Looper->>MessageQueue: 7. 循环处理消息
这样设计的好处是保证了线程安全,因为每个 Handler 都必须绑定一个 Looper,而 Looper 又与特定线程绑定,这样就确保了消息的处理是在指定线程中进行的。
8. Handler 的 post 和 sendMessage 区别
post 方法:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
sendMessage 方法:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
本质上 post 是对 sendMessage 的封装,post 更适合简单的任务,sendMessage 更适合复杂的消息传递。
底层原理
一、原理讲解
想象一下我们在餐厅点餐:
- post 方法 就像你直接告诉服务员:"我要一份炒饭"(Runnable)
- sendMessage 方法 就像你填写了一张详细的点餐单(Message)
二、源码分析
1. post 方法的实现:
public final boolean post(Runnable r) {
// 将 Runnable 包装成 Message
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r; // 将 Runnable 对象存储在 Message 的 callback 字段中
return m;
}
2. sendMessage 方法的实现:
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
三、执行流程
当消息被处理时:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
// 如果是 post 方式,直接执行 Runnable
handleCallback(msg);
} else {
if (mCallback != null) {
// 如果 Handler 有 Callback
if (mCallback.handleMessage(msg)) {
return;
}
}
// 调用 Handler 的 handleMessage 方法
handleMessage(msg);
}
}
四、流程图
graph TD
A[开始] --> B{选择方式}
B -->|post| C[创建 Runnable]
B -->|sendMessage| D[创建 Message]
C --> E[包装成 Message]
E --> F[设置 callback]
D --> G[直接使用 Message]
F --> H[发送到 MessageQueue]
G --> H
H --> I[Looper 获取消息]
I --> J{判断消息类型}
J -->|有 callback| K[执行 Runnable]
J -->|无 callback| L[执行 handleMessage]
K --> M[结束]
L --> M
五、区别总结
-
使用方式不同
- post:适合简单任务,直接传入 Runnable
- sendMessage:适合复杂消息,需要自己构建 Message
-
处理方式不同
- post:直接执行 Runnable 的 run 方法
- sendMessage:调用 Handler 的 handleMessage 方法
-
灵活性不同
- post:封装程度高,使用简单
- sendMessage:可以传递更多信息,更灵活
-
使用场景
- post:适合单一任务执行
- sendMessage:适合需要传递多个参数或者区分消息类型的场景
六、实际应用示例
// post 方式
handler.post(new Runnable() {
@Override
public void run() {
// 直接执行的代码
updateUI();
}
});
// sendMessage 方式
Message msg = Message.obtain();
msg.what = MSG_UPDATE_UI;
msg.arg1 = 100;
msg.obj = data;
handler.sendMessage(msg);
// Handler 处理
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE_UI:
// 根据 Message 的参数处理不同逻辑
updateUI(msg.arg1, msg.obj);
break;
}
}
这样的设计让 Handler 能够适应不同的使用场景,既可以简单直接,又能满足复杂的消息传递需求。
七、如何设置的callback
让我详细讲解 Handler 中 callback 的设置机制:
1 Message 中的 callback
1. Message 结构:
public final class Message implements Parcelable {
// Runnable 类型的 callback
/*package*/ Runnable callback;
// 其他字段...
/*package*/ Handler target; // Handler
/*package*/ int what; // 消息标识
public int arg1; // 参数1
public int arg2; // 参数2
public Object obj; // 携带的对象
}
2. callback 的设置过程:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain(); // 获取消息对象
m.callback = r; // 将 Runnable 设置给 message 的 callback
return m;
}
2 Handler 中的 Callback
Handler 还有另一种 callback 机制:
public interface Callback {
boolean handleMessage(@NonNull Message msg);
}
// Handler 的 Callback 成员变量
final Callback mCallback;
// 构造函数中设置 Callback
public Handler(@Nullable Callback callback) {
this(callback, false);
}
3、消息分发流程
public void dispatchMessage(@NonNull Message msg) {
// 1. 优先处理 Message 的 callback
if (msg.callback != null) {
handleCallback(msg);
} else {
// 2. 其次处理 Handler 的 Callback
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 3. 最后调用 Handler 的 handleMessage
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
4、流程图
graph TD
A[消息分发开始] --> B{检查 Message.callback}
B -->|存在| C[执行 Message.callback.run]
B -->|不存在| D{检查 Handler.mCallback}
D -->|存在| E[执行 Handler.mCallback.handleMessage]
D -->|不存在| F[执行 Handler.handleMessage]
E -->|返回 true| G[处理结束]
E -->|返回 false| F
C --> G
F --> G
5、使用示例
1. Message callback 方式:
// 使用 post 方法
handler.post(new Runnable() {
@Override
public void run() {
// 直接执行的代码
System.out.println("通过 Message callback 执行");
}
});
2. Handler callback 方式:
// 创建带有 Callback 的 Handler
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
System.out.println("通过 Handler callback 处理");
return true; // 返回 true 表示消息已处理完成
default:
return false; // 返回 false 继续执行 handleMessage
}
}
});
3. 常规 handleMessage 方式:
Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
System.out.println("通过 handleMessage 处理");
break;
}
}
};
6、优先级总结
消息处理的优先级顺序:
- Message 的 callback(通过 post 方法设置的 Runnable)
- Handler 的 Callback(通过构造函数设置的 Callback)
- Handler 的 handleMessage 方法(重写 Handler 的方法)
7、使用建议
- 简单任务:使用 post 方法,代码简洁直观
handler.post(() -> updateUI());
- 通用消息处理:使用 Handler.Callback
Handler handler = new Handler(msg -> {
// 处理所有消息
return false; // 允许继续传递
});
- 特定消息处理:重写 handleMessage
Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
// 处理特定消息
}
};
这种多层次的 callback 机制使得 Handler 的消息处理更加灵活,能够适应不同的使用场景。
9. Handler Loop 为什么不阻塞主线程
这涉及到 Android 消息循环机制的底层实现:
- Loop 的死循环是在 native 层实现的
- 主线程的消息循环与 Linux 的 epoll 机制相关
- 当没有消息时,主线程会释放 CPU 资源,进入休眠状态
- 有新消息到来时,会被唤醒继续处理消息
这就是为什么即使有死循环,也不会导致 ANR 的原因。
底层原理
让我用生动的方式来解释 Handler Loop 不阻塞主线程的原理:
🌟 形象化解释
想象一个餐厅服务员(主线程)的工作方式:
- 🧑🍳 服务员不会一直站着干等客人点单(不会死等)
- 📱 当没有客人时,服务员会坐下休息(线程休眠)
- 🔔 有客人按铃时(消息到来),服务员立即醒来服务(线程唤醒)
- 👨💼 这个过程由餐厅经理(Linux 系统)智能调度
🔄 具体实现原理
// 简化的消息循环实现
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
// 可能阻塞的地方
Message msg = queue.next();
// 处理消息
msg.target.dispatchMessage(msg);
}
}
在 native 层:
// frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(timeoutMillis);
}
🎯 核心原理
-
消息队列管理
- MessageQueue 通过 native 层的 epoll 机制管理消息
- 没有消息时,线程会通过 epoll_wait 进入休眠状态
-
系统调度
- 主线程的 Looper 与 Binder 线程池配合工作
- 通过 Linux pipe/epoll 机制实现高效的线程间通信
-
唤醒机制
- 新消息到来时,通过 pipe 写端写入数据
- epoll_wait 收到通知后唤醒线程
📊 流程图
graph TD
A[主线程启动] --> B{检查消息队列}
B -->|有消息| C[处理消息]
B -->|无消息| D[调用 nativePollOnce]
D --> E[线程休眠]
E --> F{等待新消息}
F -->|新消息到来| G[唤醒线程]
G --> B
C --> B
🎨 详细的消息处理流程
sequenceDiagram
participant MT as 主线程
participant MQ as MessageQueue
participant EP as Epoll
participant BP as Binder/其他线程
MT->>MQ: 1. 查询消息
MQ->>EP: 2. 无消息,调用 epoll_wait
MT->>MT: 3. 主线程休眠
BP->>MQ: 4. 发送新消息
MQ->>EP: 5. 写入管道
EP->>MT: 6. 唤醒主线程
MT->>MQ: 7. 处理新消息
📝 总结要点
-
为什么不阻塞?
- 使用 epoll 机制实现高效的 I/O 多路复用
- 线程休眠时不占用 CPU 资源
- 有消息时会立即被唤醒
-
效率保证
- 使用 pipe/epoll 机制,而不是简单的忙等待
- 系统级别的事件通知,性能很高
- 完美配合 Android 的事件驱动模型
-
实际应用
- UI 更新
- 接收系统事件
- 处理用户输入
- Activity 生命周期回调
这种机制保证了 Android 系统的高效运行,既不会因为循环卡住主线程,也能及时响应各种事件。
pipe/epoll 机制
让我用生动的比喻来解释 pipe/epoll 机制:
🚰 Pipe(管道)机制
想象一个水管:
- 一端负责往里面倒水(写端)
- 一端负责接水(读端)
graph LR
A[写端] -->|数据流| B((管道)) -->|数据流| C[读端]
style B fill:#f9f,stroke:#333,stroke-width:4px
生活场景类比: 就像两个人用对讲机通话:
- 说话的人 = 写端
- 听的人 = 读端
- 无线电波 = 管道
// 简化的 Pipe 示例代码
public class PipeExample {
private static final int[] pipe = new int[2]; // pipe[0]=读端,pipe[1]=写端
public static void main() {
// 创建管道
pipe(pipe);
if (fork() == 0) {
// 子进程:写数据
write(pipe[1], "Hello!", 6);
} else {
// 父进程:读数据
byte[] buffer = new byte[6];
read(pipe[0], buffer, 6);
}
}
}
📡 Epoll 机制
想象一个超级前台服务员:
- 不需要挨个问每位客人是否需要服务
- 客人按铃时,立即知道是谁需要服务
- 没人按铃时,可以休息
graph TD
A[Epoll 服务员] --> B{监听多个铃}
B --> C[1号桌铃]
B --> D[2号桌铃]
B --> E[3号桌铃]
C -->|响铃| F[立即处理]
D -->|响铃| F
E -->|响铃| F
生活场景类比: 就像现代餐厅的智能服务系统:
- 餐桌上的呼叫按钮 = 文件描述符
- 服务员的接收器 = epoll
- 客人按铃 = 事件发生
// 简化的 Epoll 示例代码
public class EpollExample {
public static void main() {
// 创建 epoll 实例
int epfd = epoll_create();
// 注册感兴趣的事件
epoll_ctl(epfd, EPOLL_CTL_ADD, socketfd, EPOLLIN);
while (true) {
// 等待事件发生
int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
// 处理就绪的事件
for (int i = 0; i < nfds; i++) {
handleEvent(events[i]);
}
}
}
}
🎯 在 Android 中的应用
sequenceDiagram
participant Handler as Handler线程
participant Pipe as 管道
participant Epoll as Epoll监听器
participant Looper as 主线程Looper
Handler->>Pipe: 发送消息
Pipe->>Epoll: 通知有新数据
Epoll->>Looper: 唤醒主线程
Looper->>Pipe: 读取消息
Looper->>Looper: 处理消息
📝 关键特点对比
传统轮询方式:
while (true) {
for (Socket socket : sockets) {
if (socket.hasData()) {
// 处理数据
}
}
}
Epoll 方式:
// 注册所有 socket
epoll.register(sockets);
while (true) {
// 只处理有事件的 socket
Socket[] activeSockets = epoll.wait();
for (Socket socket : activeSockets) {
// 处理数据
}
}
💡 优势总结
-
高效性
- 不需要轮询所有设备
- 只在有事件时才会被唤醒
- 支持同时监听大量文件描述符
-
性能表现
- 传统方式:O(n)
- Epoll方式:O(1)
-
资源占用
- 休眠时几乎不占用 CPU
- 内存使用效率高
- 系统开销小
🌟 实际应用场景
-
网络服务器
- 处理大量并发连接
- 实现高效的 I/O 多路复用
-
Android 消息机制
- Handler 消息传递
- 事件监听
- 异步操作
-
系统监控
- 文件变化监控
- 设备状态监听
- 进程间通信
这就是为什么 Android 的消息机制能够如此高效,既不会卡住主线程,又能及时处理各种事件的原因。
Binder 线程池
让我用生动的方式来解释 Binder 线程池:
🎯 形象化解释
想象一个快递公司的运作模式:
- 快递公司 = Binder 驱动
- 快递员团队 = Binder 线程池
- 收发包裹 = 进程间通信
- 派单系统 = Binder 调度器
📊 架构图
graph TD
A[应用进程A] -->|请求| B[Binder驱动]
C[应用进程B] -->|请求| B
D[应用进程C] -->|请求| B
B -->|分配| E[Binder线程池]
E -->|线程1| F[处理请求]
E -->|线程2| F
E -->|线程3| F
F -->|响应| A
F -->|响应| C
F -->|响应| D
🔄 工作原理
// Binder线程池的简化实现
public class BinderThreadPool {
// 默认最大线程数
private static final int MAX_THREADS = 15;
// 最小线程数
private static final int MIN_THREADS = 1;
static void spawnPooledThread(boolean onlyOne) {
if (onlyOne) {
// 创建单个线程
createThread();
} else {
// 根据需要创建多个线程
while (needMoreThreads()) {
createThread();
}
}
}
}
🌟 具体流程
sequenceDiagram
participant Client as 客户端进程
participant Driver as Binder驱动
participant Pool as Binder线程池
participant Server as 服务端进程
Client->>Driver: 1. 发起请求
Driver->>Pool: 2. 唤醒空闲线程
Pool->>Server: 3. 线程处理请求
Server->>Pool: 4. 处理完成
Pool->>Driver: 5. 返回结果
Driver->>Client: 6. 接收响应
📝 重要特性
- 线程管理
public class BinderThread extends Thread {
@Override
public void run() {
// 循环处理请求
while (true) {
// 等待新的事务
waitForWork();
// 处理事务
doTransaction();
// 完成后返回线程池
returnToPool();
}
}
}
- 线程池特点
- 动态调整线程数量
- 默认最大 15 个线程
- 按需创建和销毁
💡 使用场景
- 系统服务调用
// ActivityManager服务调用示例
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> processes = am.getRunningAppProcesses();
- AIDL通信
// AIDL接口示例
interface IMyService {
void doSomething();
String getData();
}
- ContentProvider访问
// ContentProvider查询示例
Cursor cursor = contentResolver.query(
Uri.parse("content://com.example.provider/data"),
null, null, null, null
);
🎨 优势分析
- 高效性
- 线程复用
- 避免频繁创建销毁
- 统一调度管理
- 安全性
- 身份验证
- 权限控制
- 调用监控
- 灵活性
- 动态伸缩
- 负载均衡
- 优先级管理
📱 实际应用示例
// 服务端示例
public class MyService extends Service {
private final IMyService.Stub mBinder = new IMyService.Stub() {
@Override
public void doSomething() {
// 在Binder线程池中执行
// 处理耗时操作
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
// 客户端示例
public class MyClient {
private IMyService service;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
service = IMyService.Stub.asInterface(binder);
// 现在可以调用服务方法了
service.doSomething();
}
};
}
🔍 注意事项
- 线程安全
public class SafeBinderService extends Service {
private final Object lock = new Object();
private void safeOperation() {
synchronized (lock) {
// 线程安全的操作
}
}
}
- ANR预防
// 避免在Binder线程中执行耗时操作
private void handleRequest() {
if (isTimeConsumingTask()) {
// 转移到工作线程
workHandler.post(() -> {
// 执行耗时操作
});
}
}
- 资源管理
public void cleanup() {
// 释放资源
if (service != null) {
unbindService(connection);
service = null;
}
}
Binder线程池是Android系统IPC(进程间通信)机制的核心组件,它通过高效的线程管理和调度,确保了系统服务和应用程序之间的通信既快速又可靠。理解Binder线程池的工作原理,对于开发高性能的Android应用程序非常重要。