Android 主線程 Looper 持有 MessageQueue,消息中攜帶 Handler 引用。若 Handler 是非靜態內部類或匿名類,它隱式持有外部 Activity/Service 引用。當發送延遲消息(postDelayed)後,Activity 即使調用 finish() 也無法被 GC 回收,導致其持有的 View、Bitmap 等資源全部洩漏。
// 1. 匿名 Handler 子類
handler = new Handler() { @Override public void handleMessage(Message msg) {...} };
// 2. 匿名 Runnable
handler.postDelayed(new Runnable() { @Override public void run() {...} }, delay);
public class LeakSafeActivity extends AppCompatActivity {
// 靜態 Handler 子類
private static class SafeHandler extends Handler {
private final WeakReference ref;
SafeHandler(LeakSafeActivity activity) { ref = new WeakReference<>(activity); }
@Override public void handleMessage(Message msg) {
LeakSafeActivity act = ref.get();
if (act != null) { /* 安全操作 */ }
}
}
// 靜態 Runnable(若需訪問 Activity 同樣用弱引用)
private static class SafeRunnable implements Runnable {
private final WeakReference ref;
SafeRunnable(LeakSafeActivity activity) { ref = new WeakReference<>(activity); }
@Override public void run() {
LeakSafeActivity act = ref.get();
if (act != null) { /* 安全操作 */ }
}
}
private final SafeHandler handler = new SafeHandler(this);
private final SafeRunnable runnable = new SafeRunnable(this);
@Override protected void onCreate(Bundle saved) {
super.onCreate(saved);
handler.postDelayed(runnable, 10_000);
}
@Override protected void onDestroy() {
handler.removeCallbacksAndMessages(null); // 移除所有待執行消息
super.onDestroy();
}
}
// build.gradle
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0"
// 代碼
class LifecycleHandler(handler: Handler) : DefaultLifecycleObserver {
override fun onDestroy(owner: LifecycleOwner) {
handler.removeCallbacksAndMessages(null)
}
}
// Activity 中
lifecycle.addObserver(LifecycleHandler(handler))
handler.removeCallbacks(runnable) 在 onDestroy 調用就萬無一失。事實:若消息已進入隊列等待執行,移除成功;但若消息正在執行 handleMessage,外部類引用仍被棧幀持有,洩漏窗口期依然存在。靜態弱引用才是根治。static Handler handler = new Handler() 直接在類中定義。事實:此處 Handler() 無參構造綁定當前線程 Looper,若在 Application 類中初始化則綁定主線程,但若在 Activity 靜態字段初始化,類加載時刻可能早於主線程 Looper 準備好,拋出 RuntimeException。應在 onCreate 中實例化。HandlerLeak 警告(需啟用)。