最近開發(fā)產(chǎn)品遇到一個需求:當(dāng)Android系統(tǒng)若干時間內(nèi)無用戶操作響應(yīng)時啟動多媒體輪播應(yīng)用辉哥。
思路1:監(jiān)聽輸入事件并對其處理
接到需求想到的一個土辦法就是在android系統(tǒng)input事件響應(yīng)端對相關(guān)輸入事件進行處理典鸡,可以追溯到android系統(tǒng)輸入事件的framework層處理,相關(guān)代碼目錄在frameworks/base/core/java/android/view下锣枝,
我起初的處理在在ViewRootImpl.java,讀者如果對Android輸入系統(tǒng)不熟悉的話,最好可以去了解下糠溜,這里我就不進行拓展笤虫。
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
//這里獲取到各種輸入的事件旁瘫,在此進行相關(guān)邏輯處理
//對產(chǎn)生輸入事件的時間進行統(tǒng)計和運算并通知上層應(yīng)用祖凫,如利用廣播機制
Log.d(TAG ,"onInputEvent:"+event.toString());
enqueueInputEvent(event, this, 0, true);
}
@Override
public void onBatchedInputEventPending() {
scheduleConsumeBatchedInput();
}
@Override
public void dispose() {
unscheduleConsumeBatchedInput();
super.dispose();
}
}
在WindowInputEventReceiver這個類里面會接收到輸入系統(tǒng)輸入的各種事件,包括用戶的觸摸酬凳,遙控 惠况,鼠標(biāo)操作,當(dāng)有輸入時候宁仔,WindowInputEventReceiver的onInputEvent()就會響應(yīng)稠屠,這時候就知道系統(tǒng)有用戶在操作了,那么你可以在這個函數(shù)里對產(chǎn)生輸入事件的時間進行統(tǒng)計和運算翎苫,用戶多久操作多久沒操作你都清楚啦权埠,你可以在這里通過廣播或者其他途徑告訴上層應(yīng)用去做響應(yīng)的處理,到此可以完成需求了煎谍。
思路2:利用Android系統(tǒng)原有的休眠機制
Android系統(tǒng)本身是有無操作若干時間后自動休眠的功能攘蔽,一般在設(shè)置程序中的顯示這項中找到休眠一項,這里就直接給出相關(guān)的代碼呐粘,實際上這里只是設(shè)置了一個SCREEN_OFF_TIMEOUT關(guān)鍵字的數(shù)據(jù)庫字段满俗,相關(guān)代碼在設(shè)置程序的DisplaySettings中。
@Override
public boolean onPreferenceChange(Preference preference, Object objValue) {
final String key = preference.getKey();
if (KEY_SCREEN_TIMEOUT.equals(key)) {
int value = Integer.parseInt((String) objValue);
try {
//設(shè)置
Settings.System.putInt(getContentResolver(), SCREEN_OFF_TIMEOUT, value);
updateTimeoutPreferenceDescription(value);
} catch (NumberFormatException e) {
Log.e(TAG, "could not persist screen timeout setting", e);
}
}
if (KEY_FONT_SIZE.equals(key)) {
writeFontSizePreference(objValue);
}
return true;
}
那么實際上的操作處理在哪里呢作岖,經(jīng)過一段搜索后(搜索SCREEN_OFF_TIMEOUT關(guān)鍵字)唆垃,我們發(fā)現(xiàn)是在PowerManagerService中查找到相關(guān)的處理,因為休眠部分涉及到電源管理痘儡,讀者對這部分有疑問辕万,建議去讀下關(guān)于PowerManagerService的相關(guān)分析,在這里介紹調(diào)用到PowerManagerService里關(guān)鍵的方法updateUserActivitySummaryLocked()谤辜。
/**
* Updates the value of mUserActivitySummary to summarize the user requested
* state of the system such as whether the screen should be bright or dim.
* Note that user activity is ignored when the system is asleep.
*
* This function must have no other side-effects.
*/
private long mLastUserActivityTimeRecord =0;
private void updateUserActivitySummaryLocked(long now, int dirty) {
Slog.d(TAG, "updateUserActivitySummaryLocked:"+now
+"mLastUserActivityTime:"+mLastUserActivityTime
+"mLastWakeTime:"+mLastWakeTime);
// Update the status of the user activity timeout timer.
if ((dirty & (DIRTY_USER_ACTIVITY | DIRTY_WAKEFULNESS | DIRTY_SETTINGS)) != 0) {
mHandler.removeMessages(MSG_USER_ACTIVITY_TIMEOUT);
long nextTimeout = 0;
if (mWakefulness != WAKEFULNESS_ASLEEP) {
final int screenOffTimeout = getScreenOffTimeoutLocked();
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
mUserActivitySummary = 0;
if (mLastUserActivityTime >= mLastWakeTime) {
nextTimeout = mLastUserActivityTime
+ screenOffTimeout - screenDimDuration;
if (now < nextTimeout) {
mUserActivitySummary |= USER_ACTIVITY_SCREEN_BRIGHT;
} else {
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
mUserActivitySummary |= USER_ACTIVITY_SCREEN_DIM;
}
}
}
if (mUserActivitySummary == 0
&& mLastUserActivityTimeNoChangeLights >= mLastWakeTime) {
nextTimeout = mLastUserActivityTimeNoChangeLights + screenOffTimeout;
if (now < nextTimeout
&& mDisplayPowerRequest.screenState
!= DisplayPowerRequest.SCREEN_STATE_OFF) {
mUserActivitySummary = mDisplayPowerRequest.screenState
== DisplayPowerRequest.SCREEN_STATE_BRIGHT ?
USER_ACTIVITY_SCREEN_BRIGHT : USER_ACTIVITY_SCREEN_DIM;
}
}
if (mUserActivitySummary != 0) {
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextTimeout);
}
} else {
mUserActivitySummary = 0;
}
if (DEBUG_SPEW) {
Slog.d(TAG, "updateUserActivitySummaryLocked: mWakefulness="
+ wakefulnessToString(mWakefulness)
+ ", mUserActivitySummary=0x" + Integer.toHexString(mUserActivitySummary)
+ ", nextTimeout=" + TimeUtils.formatUptime(nextTimeout));
}
}
}
讀者可以仔細讀下這個部分的注釋蓄坏,大概意思是通過這個辦法統(tǒng)計更新用戶請求的一些信息和狀態(tài),通過打印發(fā)現(xiàn)這個方法在若干毫秒被系統(tǒng)調(diào)用的(具體的回調(diào)過程我還沒仔細研究)丑念;需要注意的是這里有聲明該方法內(nèi)不能有其他副作用的操作涡戳,言下之意不能過多的操作不然系統(tǒng)會崩潰重啟。這里我吃過苦頭了脯倚,反復(fù)發(fā)一個intent也會導(dǎo)致系統(tǒng)重啟渔彰,各位看官如果要操作之記得慎重操作。
需要注意的變量now推正,mLastUserActivityTime恍涂,now是通過SystemClock.uptimeMillis()獲取,表示當(dāng)前時間植榕,mLastUserActivityTime表示用戶上一次操作的時間再沧,now和mLastUserActivityTime的對比可以知道距離用戶上一次多久沒有操作了。通過大家可以研讀其中邏輯打印體會下尊残。只要搞定了這個地方炒瘸,可以滿足目前的需求淤堵,而且不用單獨去輸入系統(tǒng)去做處理,這里的需求推薦思路2去實現(xiàn)顷扩。
在處理一些問題拐邪,在思路上,我建議優(yōu)雅地方式去處理隘截,而不是簡單粗暴地把功能實現(xiàn)了扎阶。何為優(yōu)雅地處理,我認(rèn)為是要建立在對系統(tǒng)的理解上婶芭,利用和結(jié)合系統(tǒng)原有的機制东臀,不做低效重復(fù)地開發(fā),所以要不斷地去認(rèn)識學(xué)習(xí)系統(tǒng)的一些機制犀农。
歡迎關(guān)注我的個人主頁啡邑,謝謝大家!