在車載系統中,倒車或藍牙通話時需保持螢幕常亮,即取消螢幕逾時鎖定計時器。傳統做法是重置計時器,但更優雅的方式是動態啟用/停用該計時任務。
經研究 PhoneWindowManager 原始碼,螢幕鎖定逾時最終由 mScreenLockTimeout 這個 Runnable 觸發:
ScreenLockTimeout mScreenLockTimeout = new ScreenLockTimeout();
class ScreenLockTimeout implements Runnable {
Bundle options;
@Override
public void run() {
synchronized (this) {
if (mKeyguardDelegate != null) {
mKeyguardDelegate.doKeyguardTimeout(options);
}
mLockScreenTimerActive = false;
options = null;
}
}
public void setLockOptions(Bundle options) { this.options = options; }
}
關鍵在於透過 Handler 的 postDelayed 與 removeCallbacks 動態控制此 Runnable 的執行。
利用 ContentObserver 監聽系統設定值變化,實現進程間通訊:寫入特定值即可動態新增/移除螢幕逾時任務。
private final class CalcScnTimeoutObserver extends ContentObserver {
public CalcScnTimeoutObserver(Handler handler) { super(handler); }
@Override
public void onChange(boolean selfChange) {
ContentResolver resolver = mContext.getContentResolver();
int value = Settings.System.getIntForUser(resolver,
Settings.System.SCREEN_TIMEOUT_CALC_INFO, 0, UserHandle.USER_CURRENT);
if (value == 1) { // 取消任務
synchronized (mScreenLockTimeout) {
if (mLockScreenTimerActive) {
mHandler.removeCallbacks(mScreenLockTimeout);
mLockScreenTimerActive = false;
}
}
} else if (value == 2) { // 新增/重置任務
synchronized (mScreenLockTimeout) {
if (mLockScreenTimerActive) {
mHandler.removeCallbacks(mScreenLockTimeout);
}
mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
mLockScreenTimerActive = true;
}
}
}
void observe() {
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.SCREEN_TIMEOUT_CALC_INFO),
false, this, UserHandle.USER_ALL);
}
}
Android 系統原始碼中蘊含大量實用設計模式,深入閱讀並靈活運用,能事半功倍。