嘗試在子線程 `new TextView()` / `new EditText()` 等 View,主線程再 `addView`。平板 (5.1.1) 正常,手機 (4.2.2) 崩潰:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.widget.Editor$UserDictionaryListener.<init>(Editor.java:3836)
at android.widget.EditText.<init>(EditText.java:60)
| 方案 | 可行性 | 缺點 |
|---|---|---|
| 子線程 `Looper.prepare()` + `Looper.loop()` | 理論可行 | 子線程進入死循環,無法退出,資源洩漏 |
| 子線程創建 View,主線程 `addView` | 不穩定 | 構造階段仍可能觸發 UI 操作,跨版本崩潰 |
| 主線程異步分批創建 (Handler.postDelayed) | 推薦 | 需拆分任務,稍複雜 |
| 預先創建 View 池 / 複用 | 最佳 | 需管理生命週期 |
// 任務隊列
private final Queue viewCreationTasks = new LinkedList<>();
private final Handler mainHandler = new Handler(Looper.getMainLooper());
// 生產任務(可在子線程調用)
public void scheduleViewCreation(ViewFactory factory, ViewGroup parent) {
viewCreationTasks.offer(() -> {
View view = factory.createView();
parent.addView(view);
});
processNextTask();
}
// 消費任務(主線程逐個執行,避免卡頓)
private void processNextTask() {
if (viewCreationTasks.isEmpty()) return;
mainHandler.post(() -> {
Runnable task = viewCreationTasks.poll();
if (task != null) task.run();
processNextTask(); // 遞歸處理下一個
});
}
// 使用弱引用持有 Context/Parent,防洩漏
private static class ViewFactory {
private final WeakReference contextRef;
private final WeakReference parentRef;
// ... 構造函數
public View createView() {
Context ctx = contextRef.get();
if (ctx == null) return null;
return new EditText(ctx); // 在主線程安全創建
}
}
子線程創建 View 是反模式。將「數據準備」與「UI 構建」解耦,數據在子線程處理,UI 在主線程分批渲染,配合弱引用管理生命週期,是穩健高效的 Android UI 優化範式。