關(guān)于Android系統(tǒng)無(wú)用戶操作相關(guān)處理

最近開發(fā)產(chǎn)品遇到一個(gè)需求:當(dāng)Android系統(tǒng)若干時(shí)間內(nèi)無(wú)用戶操作響應(yīng)時(shí)啟動(dòng)多媒體輪播應(yīng)用。

思路1:監(jiān)聽(tīng)輸入事件并對(duì)其處理

接到需求想到的一個(gè)土辦法就是在android系統(tǒng)input事件響應(yīng)端對(duì)相關(guān)輸入事件進(jìn)行處理,可以追溯到android系統(tǒng)輸入事件的framework層處理,相關(guān)代碼目錄在frameworks/base/core/java/android/view下,
我起初的處理在在ViewRootImpl.java,讀者如果對(duì)Android輸入系統(tǒng)不熟悉的話,最好可以去了解下,這里我就不進(jìn)行拓展。

 final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }    

        @Override
        public void onInputEvent(InputEvent event) {
            //這里獲取到各種輸入的事件,在此進(jìn)行相關(guān)邏輯處理
            //對(duì)產(chǎn)生輸入事件的時(shí)間進(jìn)行統(tǒng)計(jì)和運(yùn)算并通知上層應(yīng)用,如利用廣播機(jī)制
            Log.d(TAG ,"onInputEvent:"+event.toString());
            enqueueInputEvent(event, this, 0, true);
        }    

        @Override
        public void onBatchedInputEventPending() {
            scheduleConsumeBatchedInput();
        }    

        @Override
        public void dispose() {
            unscheduleConsumeBatchedInput();
            super.dispose();
        }    
    }    

在WindowInputEventReceiver這個(gè)類里面會(huì)接收到輸入系統(tǒng)輸入的各種事件,包括用戶的觸摸,遙控 ,鼠標(biāo)操作,當(dāng)有輸入時(shí)候,WindowInputEventReceiver的onInputEvent()就會(huì)響應(yīng),這時(shí)候就知道系統(tǒng)有用戶在操作了,那么你可以在這個(gè)函數(shù)里對(duì)產(chǎn)生輸入事件的時(shí)間進(jìn)行統(tǒng)計(jì)和運(yùn)算,用戶多久操作多久沒(méi)操作你都清楚啦,你可以在這里通過(guò)廣播或者其他途徑告訴上層應(yīng)用去做響應(yīng)的處理,到此可以完成需求了。

思路2:利用Android系統(tǒng)原有的休眠機(jī)制

Android系統(tǒng)本身是有無(wú)操作若干時(shí)間后自動(dòng)休眠的功能,一般在設(shè)置程序中的顯示這項(xiàng)中找到休眠一項(xiàng),這里就直接給出相關(guān)的代碼,實(shí)際上這里只是設(shè)置了一個(gè)SCREEN_OFF_TIMEOUT關(guān)鍵字的數(shù)據(jù)庫(kù)字段,相關(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;
    }   

那么實(shí)際上的操作處理在哪里呢,經(jīng)過(guò)一段搜索后(搜索SCREEN_OFF_TIMEOUT關(guān)鍵字),我們發(fā)現(xiàn)是在PowerManagerService中查找到相關(guān)的處理,因?yàn)樾菝卟糠稚婕暗诫娫垂芾?,讀者對(duì)這部分有疑問(wè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));
            }
        }
    }

讀者可以仔細(xì)讀下這個(gè)部分的注釋,大概意思是通過(guò)這個(gè)辦法統(tǒng)計(jì)更新用戶請(qǐng)求的一些信息和狀態(tài),通過(guò)打印發(fā)現(xiàn)這個(gè)方法在若干毫秒被系統(tǒng)調(diào)用的(具體的回調(diào)過(guò)程我還沒(méi)仔細(xì)研究);需要注意的是這里有聲明該方法內(nèi)不能有其他副作用的操作,言下之意不能過(guò)多的操作不然系統(tǒng)會(huì)崩潰重啟。這里我吃過(guò)苦頭了,反復(fù)發(fā)一個(gè)intent也會(huì)導(dǎo)致系統(tǒng)重啟,各位看官如果要操作之記得慎重操作。
需要注意的變量now,mLastUserActivityTime,now是通過(guò)SystemClock.uptimeMillis()獲取,表示當(dāng)前時(shí)間,mLastUserActivityTime表示用戶上一次操作的時(shí)間,now和mLastUserActivityTime的對(duì)比可以知道距離用戶上一次多久沒(méi)有操作了。通過(guò)大家可以研讀其中邏輯打印體會(huì)下。只要搞定了這個(gè)地方,可以滿足目前的需求,而且不用單獨(dú)去輸入系統(tǒng)去做處理,這里的需求推薦思路2去實(shí)現(xiàn)。

在處理一些問(wèn)題,在思路上,我建議優(yōu)雅地方式去處理,而不是簡(jiǎn)單粗暴地把功能實(shí)現(xiàn)了。何為優(yōu)雅地處理,我認(rèn)為是要建立在對(duì)系統(tǒng)的理解上,利用和結(jié)合系統(tǒng)原有的機(jī)制,不做低效重復(fù)地開發(fā),所以要不斷地去認(rèn)識(shí)學(xué)習(xí)系統(tǒng)的一些機(jī)制。

歡迎關(guān)注我的個(gè)人主頁(yè),謝謝大家!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,716評(píng)論 25 709
  • 夜半獨(dú)自歸, 燈昏影斜斜。 遙聞家犬吠, 應(yīng)知有歸人。 此時(shí)舉世寂, 余與汝未眠。 待吾換來(lái)酒, 與君促膝談。
    左雨塘閱讀 201評(píng)論 0 0
  • 現(xiàn)在的娛樂(lè)圈,小編是看花了眼,身邊的人說(shuō)起娛樂(lè)圈無(wú)非三個(gè)字:水太深,有水的地方自然也有清流濁流,然而今天我們說(shuō)說(shuō)這...
    巫女_c089閱讀 612評(píng)論 0 1
  • 回答 每一次出發(fā)都是為了到達(dá);每一次找尋都是為了相逢。
    黑皮曰閱讀 192評(píng)論 0 0
  • 前情提要:戀你十年,未曾改變(六十七) 我穿著內(nèi)褲走到客廳,四處尋找我的衣服,剛要給杜嘉撥電話,一條短信蹦了出來(lái)...
    lemoney閱讀 732評(píng)論 5 8

友情鏈接更多精彩內(nèi)容