舊模型中,後台任務 BackgroundTask 輪詢前台 ProgressGaugeUI 的 isStopped() 標誌來決定是否退出。缺陷顯而易見:
Thread.sleep()、網絡 I/O、RMS 讀寫時,無法檢查標誌,用戶點「取消」無響應,體感死鎖。本文引入 回調 + 接口隔離,實現「前台強制中斷後台、雙向解耦」。
// 1. 可取消契約:後台實現,前台調用
public interface Cancelable {
/** 非阻塞、幂等、立即返回;內部通常 interrupt() 或 close() 資源 */
void cancel();
}
// 2. 進度觀察者:前台實現,後台推送進度
public interface ProgressObserver {
void reset();
void setMax();
void show(Display display);
void exit();
void updateProgress(Object param); // 進度值或提示文本
boolean isStoppable(); void setStoppable(boolean b);
boolean isStopped(); void setStopped(boolean b);
void setTitle(String t); void setPrompt(String p);
/** 關鍵:注入可取消對象,建立回調鏈 */
void setCancelableObject(Cancelable co);
}
Form + Gauge(INDEFINITE/INCREMENTAL) + Command.STOP。commandAction() 捕獲用戶點擊「取消」 → setStopped(true) → cancelableObject.cancel() 回調後台。getInstance() 保證全進程唯一進度條,節省內存。public abstract class BackgroundTask extends Thread implements Cancelable {
protected ProgressObserver poUI;
protected Displayable preScreen; // 任務前界面
protected Alert alertScreen; // 取消/異常提示
protected boolean needAlert;
public BackgroundTask(ProgressObserver poUI, Displayable pre, Display display) { ... }
public final void run() {
boolean taskComplete = false;
try {
taskComplete = runTask(); // 子類實現具體業務
} catch (Exception e) { showErrorAlert(e); }
finally {
// 任務結束統一處理
if (!taskComplete && poUI.isStoppable() && poUI.isStopped()) {
display.setCurrent(needAlert ? alertScreen : preScreen, preScreen);
}
poUI.exit();
}
}
/** 子類必須實現:返回 true=成功跳轉、false=失敗/取消 */
protected abstract boolean runTask();
/** 取消回調:默認 interrupt(),子類可覆寫 close 流/連接 */
public void cancel() { this.interrupt(); }
/** 靜態啟動器:一鍵綁定 UI、註冊回調、啟動線程 */
public static void runWithProgressGauge(BackgroundTask task, String title,
String prompt, boolean stoppable, Display display) { ... }
}
BackgroundTask bkTask = new BackgroundTask(poUI, currentScreen, display) {
public boolean runTask() {
poUI.updateProgress("Sleep 3s...");
try { Thread.sleep(3000); } catch (InterruptedException e) { return false; } // 捕獲中斷,快速退出
poUI.updateProgress("Sleep another 3s...");
try { Thread.sleep(3000); } catch (InterruptedException e) { return false; }
poUI.setMax();
display.setCurrent(new Form("Complete")); // 成功跳轉
return true;
}
@Override public void cancel() {
// 針對性中斷:關閉 HttpConnection、InputStream 等
this.interrupt();
}
};
BackgroundTask.runWithProgressGauge(bkTask, "Long Task", "Working...", true, display);
| 痛點 | 舊模型 | 新模型 |
|---|---|---|
| 阻塞操作可取消性 | 依賴輪詢,不可靠 | interrupt() / close() 強制拋異常,catch 即退出 |
| 耦合度 | 後台依賴前台具體類 | 雙方依賴接口 Cancelable/ProgressObserver |
| 代碼侵入性 | 業務邏輯混雜 if (poUI.isStopped()) | 僅在阻塞點 catch (InterruptedException),業務碼純淨 |
| 複用性 | 綁定特定 UI | 任意實現 ProgressObserver 的 Canvas/Form/自定義組件均可 |
InterruptedException,或自行檢查 Thread.interrupted()。cancel() 中完成:conn.close()、is.close()、recordStore.close(),觸發底層 I/O 異常打斷阻塞。updateProgress() 必須線程安全(JXL 內部已同步),避免直接在後台操作 LCDUI 組件。cancel() 多次調用無副作用,ProgressObserver.setStopped(true) 守衛重複回調。通過 Cancelable 接口反向注入,將「用戶意圖」即時傳遞給「執行引擎」,徹底解決了 J2ME 單線程阻塞模型下的取消難題。此模式不僅適用於進度條,同樣可推廣至下載管理器、數據同步、圖片加載等所有長耗時異步場景,是 J2ME 併發編程的基石範式。