<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                **PowerManagerService.java::acquireWakeLock** ~~~ public void acquireWakeLock(int flags, IBinderlock, String tag, WorkSource ws) { intuid = Binder.getCallingUid(); intpid = Binder.getCallingPid(); if(uid != Process.myUid()) { mContext.enforceCallingOrSelfPermission(//檢查WAKE_LOCK權限 android.Manifest.permission.WAKE_LOCK,null); } if(ws != null) { //如果ws不為空,需要檢查調用進程是否有UPDATE_DEVICE_STATS的權限 enforceWakeSourcePermission(uid, pid); } longident = Binder.clearCallingIdentity(); try{ synchronized (mLocks) {調用acquireWakeLockLocked函數 acquireWakeLockLocked(flags, lock, uid, pid, tag, ws); } } ...... } ~~~ 接下來分析acquireWakeLockLocked函數。由于此段代碼較長,宜分段來看。 1. acquireWakeLockLocked分析之一 開始分析之前,有必要先介紹另外一個數據結構,它為PowerManagerService的內部類,名字也為WakeLock。其定義如下: **PowerManagerService.java** ~~~ class WakeLock implements IBinder.DeathRecipient ~~~ PMS的WakeLock實現了DeathRecipient接口。根據前面Binder系統的知識可知,當Binder服務端死亡后,Binder系統會向注冊了訃告接收的Binder客戶端發送訃告通知,因此客戶端可以做一些資源清理工作。在本例中,PM.WakeLock是Binder服務端,而PMS.WakeLock是Binder客戶端。假如PM.WakeLock所在進程在release喚醒鎖(即WakeLock)之前死亡,PMS.WakeLock的binderDied函數則會被調用,這樣,PMS也能及時進行釋放(release)工作。對于系統的重要資源來說,采用這種安全保護措施尤其必要。 回到acquireWakeLockLocked函數,先看第一段代碼: **PowerManagerService.java::acquireWakeLockLocked** ~~~ public void acquireWakeLockLocked(int flags,IBinder lock, int uid, int pid, Stringtag,WorkSource ws) { ...... //mLocks是一個ArrayList,保存PMS.WakeLock對象 int index= mLocks.getIndex(lock); WakeLockwl; booleannewlock; booleandiffsource; WorkSourceoldsource; if (index< 0) { //創建一個PMS.WakeLock對象,保存客戶端acquire傳來的參數 wl = new WakeLock(flags, lock, tag, uid, pid); switch(wl.flags & LOCK_MASK) { //將flags轉換成對應的minState casePowerManager.FULL_WAKE_LOCK: if(mUseSoftwareAutoBrightness) { wl.minState = SCREEN_BRIGHT; }else { wl.minState = (mKeyboardVisible ? ALL_BRIGHT: SCREEN_BUTTON_BRIGHT); } break; casePowerManager.SCREEN_BRIGHT_WAKE_LOCK: wl.minState = SCREEN_BRIGHT; break; casePowerManager.SCREEN_DIM_WAKE_LOCK: wl.minState = SCREEN_DIM; break; case PowerManager.PARTIAL_WAKE_LOCK: //PROXIMITY_SCREEN_OFF_WAKE_LOCK在SDK中并未輸出,原因是有部分手機并沒有接近 //傳感器 casePowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK: break; default: return; } mLocks.addLock(wl);//將PMS.WakeLock對象保存到mLocks中 if (ws!= null) { wl.ws = new WorkSource(ws); } newlock= true; //設置幾個參數信息,newlock表示新創建了一個PMS.WakeLock對象 diffsource = false; oldsource = null; }else{ //如果之前保存有PMS.WakeLock,則要判斷新傳入的WorkSource和之前保存的WorkSource //是否一樣。此處不討論這種情況 ...... } ~~~ 在上面代碼中,很重要一部分是將前面flags信息轉成PMS.WakeLock的成員變量minState,下面是對轉換關系的總結。 - FULL_WAKE_LOCK:當啟用mUseSoftwareAutoBrightness時,minState為SCREEN_BRIGHT(表示屏幕全亮),否則為ALL_BRIGHT(屏幕、鍵盤、按鍵全亮。注意,只有在打開鍵盤時才能選擇此項)或SCREEN_BUTTON_BRIGHT(屏幕、按鍵全亮)。 - SCREEN_BRIGHT_WAKE_LOCK:minState為SCREEN_BRIGHT,表示屏幕全亮。 - SCREEN_DIM_WAKE_LOCK:minState為SCREEN_DIM,表示屏幕Dim。 - 對PARTIAL_WAKE_LOCK和PROXIMITY_SCREEN_OFF_WAKE_LOCK情況不做處理。 該做的準備工作都做了,下面來看第二階段的工作是什么。 2. acquireWakeLockLocked分析之二 代碼如下: ~~~ //isScreenLock用于判斷flags是否和屏幕有關,除PARTIAL_WAKE_LOCK外,其他WAKE_LOCK //都和屏幕有關 if (isScreenLock(flags)) { if ((flags& LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) { mProximityWakeLockCount++;//引用計數控制 if(mProximityWakeLockCount == 1) { enableProximityLockLocked();//使能Proximity傳感器 } } else { if((wl.flags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0) { ......//ACQUIRE_CAUSES_WAKEUP標志處理 } else { //①gatherState返回一個狀態,稍后分析該函數 mWakeLockState = (mUserState | mWakeLockState) &mLocks.gatherState(); } //②設置電源狀態, setPowerState(mWakeLockState | mUserState); } } ~~~ 以上代碼列出了兩個關鍵函數,一個是gatherState,另外一個是setPowerState,下面來分析它們。 (1) gatherState分析 gatherState函數的代碼如下: **PowerManagerService.java::gatherState** ~~~ int gatherState() { intresult = 0; int N =this.size(); for (inti=0; i<N; i++) { WakeLock wl = this.get(i); if(wl.activated) if(isScreenLock(wl.flags)) result |= wl.minState;//對系統中所有活躍PMS.WakeLock的狀態進行或操作 } returnresult; } ~~~ 由以上代碼可知,gatherState將統計當前系統內部活躍WakeLock的minState。這里為什么要“使用”或“操作”呢?舉個例子,假如WakeLock A的minState為SCREEN_DIM,而WakeLock B的minState為SCREEN_BRIGHT,二者共同作用,最終的屏幕狀態顯然應該是SCREEN_BRIGHT。 提示讀者也可參考PowerManagerService中SCREEN_DIM等變量的定義。 下面來看setPowerState,本章前面曾兩次對該函數避而不談,現在該見識見識它了。 (2) setPowerState分析 setPowerState用于設置電源狀態,先來看其在代碼中的調用: setPowerState(mWakeLockState | mUserState); 在以上代碼中除了mWakeLockState外,還有一個mUserState。根據前面對gatherState函數的介紹可知,mWakeLockState的值來源于系統當前活躍WakeLock的minState。那么mUserState代表什么呢? mUserState代表用戶觸發事件導致的電源狀態。例如,按Home鍵后,將該值設置為SCREEN_BUTTON_BRIGHT(假設手機沒有鍵盤)。很顯然,此時系統的電源狀態應該是mUserState和mWakeLockState的組合。 >[info] **提示 **“一個小小的變量背后代表了一個很重要的case”,讀者能體會到嗎? 下面來看setPowerState的代碼,這段代碼較長,也適合分段來看。第一段代碼如下: **PowerManagerService.java::setPowerState** ~~~ private void setPowerState(int state) {//調用另外一個同名函數 setPowerState(state, false,WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT); } //setPowerState private void setPowerState(int newState, booleannoChangeLights, int reason) { synchronized (mLocks) { int err; if (noChangeLights)//在這種情況中,noChangeLights為false newState = (newState & ~LIGHTS_MASK) | (mPowerState &LIGHTS_MASK); if(mProximitySensorActive)//如果打開了接近感應器,就不需要在這里點亮屏幕了 newState = (newState & ~SCREEN_BRIGHT); if(batteryIsLow())//判斷是否處于低電狀態 newState |= BATTERY_LOW_BIT; else newState &= ~BATTERY_LOW_BIT; ...... //如果還沒啟動完成,則需要將newState置為ALL_BRIGHT。細心的讀者有沒有發現,在手機開機過程中 //鍵盤、屏幕、按鍵等都會全部點亮一會兒呢? if(!mBootCompleted && !mUseSoftwareAutoBrightness) newState |= ALL_BRIGHT; booleanoldScreenOn = (mPowerState & SCREEN_ON_BIT) != 0; boolean newScreenOn = (newState &SCREEN_ON_BIT) != 0; finalboolean stateChanged = mPowerState != newState; ~~~ 第一段代碼主要用于得到一些狀態值,例如在新狀態下屏幕是否需要點亮(newScreenOn)等。再來看第二段代碼,它將根據第一段的狀態值完成對應的工作。 **PowerManagerService::setPowerState** ~~~ if(oldScreenOn != newScreenOn) { if(newScreenOn) { if(mStillNeedSleepNotification) { //對sendNotificationLocked函數的分析見后文 sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER); }// mStillNeedSleepNotification判斷 booleanreallyTurnScreenOn = true; if(mPreventScreenOn)// mPreventScreenOn是何方神圣? reallyTurnScreenOn= false; if(reallyTurnScreenOn) { err = setScreenStateLocked(true);//點亮屏幕 ......//通知mBatteryStats做電量統計 mBatteryStats.noteScreenBrightness(getPreferredBrightness()); mBatteryStats.noteScreenOn(); } else {//reallyTurnScreenOn為false setScreenStateLocked(false);//關閉屏幕 err =0; } if (err == 0) { sendNotificationLocked(true, -1); if(stateChanged) updateLightsLocked(newState, 0);//點亮按鍵燈或者鍵盤燈 mPowerState |= SCREEN_ON_BIT; } } ~~~ 以上代碼看起來比較簡單,就是根據情況點亮或關閉屏幕。事實果真的如此嗎?的還記得前面所說“一個小小的變量背后代表一個很重要的case”這句話嗎?是的,這里也有一個很重要的case,由mPreventScreenOn表達。這是什么意思呢? PMS提供了一個函數叫preventScreenOn,該函數(在SDK中未公開)使應用程序可以阻止屏幕點亮。為什么會有這種操作呢?難道是因為該應用很丑,以至于不想讓別人看見?根據該函數的解釋,在兩個應用之間進行切換時(尤其是正在啟動一個Activity卻又接到來電通知時),很容易出現閃屏現象,會嚴重影響用戶體驗。因此提供了此函數,由應用來調用并處理它。 * * * * * **注意**:閃屏的問題似乎解決了,但事情還沒完,這個解決方案還引入了另外一個問題:假設應用忘記重新使屏幕點亮,手機豈不是一直就黑屏了?為此,在代碼中增加了一段處理邏輯,即如果5秒鐘后應用還沒有使屏幕點亮,PMS將自己設置mPreventScreenOn為false。 * * * * * Google怎么會寫這種代碼?還好,代碼開發者也意識到這是一個很難看的方法,只是目前還沒有一個比較完美的解決方案而已。 繼續看setPowerState最后的代碼: ~~~ else {//newScreenOn為false的情況 ......//更新鍵盤燈、按鍵燈的狀態 //從mHandler中移除mAutoBrightnessTask,這和光傳感器有關。此處不討論 mHandler.removeCallbacks(mAutoBrightnessTask); mBatteryStats.noteScreenOff();//通知BatteryStatsService,屏幕已關 mPowerState = (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK); updateNativePowerStateLocked(); } }//if(oldScreenOn != newScreenOn)判斷結束 else if(stateChanged) {//屏幕的狀態不變,但是light的狀態有可能變化,所以 updateLightsLocked(newState, 0);//單獨更新light的狀態 } mPowerState= (mPowerState & ~LIGHTS_MASK) | (newState & LIGHTS_MASK); updateNativePowerStateLocked(); }//setPowerState完畢 ~~~ setPowerState函數是在PMS中真正設置屏幕及Light狀態的地方,其內部將通過Power類與這些硬件交互。相關內容見5.3.3節。 (3) sendNotificationLocked函數分析 sendNotificationLocked函數用于觸發SCREEN_ON/OFF廣播的發送,來看以下代碼: **PowerManagerService.java::sendNotificationLocked** ~~~ private void sendNotificationLocked(boolean on,int why) { ...... if (!on) { mStillNeedSleepNotification = false; } int index= 0; while(mBroadcastQueue[index] != -1) { index++; } // mBroadcastQueue和mBroadcastWhy均定義為int數組,成員個數為3,它們有什么作用呢 mBroadcastQueue[index] = on ? 1 : 0; mBroadcastWhy[index] = why; /* mBroadcastQueue數組一共有3個元素,根據代碼中的注釋,其作用如下: 當取得的index為2時,即0,1元素已經有值,由于屏幕ON/OFF請求是配對的,所以在這種情況 下只需要處理最后一次的請求。例如0元素為ON,1元素為OFF,2元素為ON,則可以去掉0, 1的請求,而直接處理2的請求,即屏幕ON。對于那種頻繁按Power鍵的操作,通過這種方式可以 節省一次切換操作 */ if (index== 2) { if (!on&& mBroadcastWhy[0] > why) mBroadcastWhy[0] = why; //處理index為2的情況,見上文的說明 mBroadcastQueue[0] = on ? 1 : 0; mBroadcastQueue[1] = -1; mBroadcastQueue[2] = -1; mBroadcastWakeLock.release(); index =0; } /* 如果index為1,on為false,即屏幕發出關閉請求,則無需處理。根據注釋中的說明, 在此種情況,屏幕已經處于OFF狀態,所以無需處理。為什么在此種情況下屏幕已經關閉了呢? */ if (index== 1 && !on) { mBroadcastQueue[0] = -1; mBroadcastQueue[1] = -1; index = -1; mBroadcastWakeLock.release(); } if(mSkippedScreenOn) { updateLightsLocked(mPowerState, SCREEN_ON_BIT); } //如果index不為負數,則拋送mNotificationTask給mHandler處理 if (index>= 0) { mBroadcastWakeLock.acquire(); mHandler.post(mNotificationTask); } } ~~~ sendNotificationLocked函數相當詭異,主要是mBroadcastQueue數組的使用讓人感到困惑。其目的在于減少不必要的屏幕切換和廣播發送,但是為什么index為1時,屏幕處于OFF狀態呢?下面來分析mNotificationTask,希望它能回答這個問題。 **PowerManagerService.java::mNotificationTask** ~~~ private Runnable mNotificationTask = newRunnable() { publicvoid run() { while(true) {//此處是一個while循環 intvalue; int why; WindowManagerPolicy policy; synchronized (mLocks) { value =mBroadcastQueue[0];//取mBroadcastQueue第一個元素 why= mBroadcastWhy[0]; for(int i=0; i<2; i++) {//將后面的元素往前挪一位 mBroadcastQueue[i] = mBroadcastQueue[i+1]; mBroadcastWhy[i] = mBroadcastWhy[i+1]; } policy = getPolicyLocked();//policy指向PhoneWindowManager if(value == 1 && !mPreparingForScreenOn) { mPreparingForScreenOn = true; mBroadcastWakeLock.acquire(); } }// synchronized結束 if(value == 1) {//value為1,表示發出屏幕ON請求 mScreenOnStart = SystemClock.uptimeMillis(); //和WindowManagerService交互,和鎖屏界面有關 //mScreenOnListener為回調通知對象 policy.screenTurningOn(mScreenOnListener); ActivityManagerNative.getDefault().wakingUp();//和AMS交互 if (mContext != null &&ActivityManagerNative.isSystemReady()) { //發送SCREEN_ON廣播 mContext.sendOrderedBroadcast(mScreenOnIntent,null, mScreenOnBroadcastDone, mHandler, 0, null, null); }...... }elseif (value == 0) { mScreenOffStart = SystemClock.uptimeMillis(); policy.screenTurnedOff(why);//通知WindowManagerService ActivityManagerNative.getDefault().goingToSleep();//和AMS交互 if(mContext != null && ActivityManagerNative.isSystemReady()) { //發送屏幕OFF廣播 mContext.sendOrderedBroadcast(mScreenOffIntent, null, mScreenOffBroadcastDone, mHandler, 0, null,null); } }elsebreak; } }; ~~~ mNotificationTask比較復雜,但是它對mBroadcastQueue的處理比較有意思,每次取出第一個元素值后,將后續元素往前挪一位。這種處理方式能解決之前提出的那個問題嗎? 說實話,目前筆者也沒找到能解釋index為1時,屏幕一定處于OFF的證據。如果有哪位讀者找到證據,不妨分享一下。 另外,mNotificationTask和ActivityManagerService及WindowManagerService都有交互。因為這兩個服務內部也使用了WakeLock,所以需要通知它們釋放WakeLock,否則會導致不必要的電力資源消耗。具體內容只能留待以后分析相關服務時再來討論了。 (4) acquireWakeLocked第二階段工作總結 acquireWakeLocked第二階段工作是處理和屏幕相關的WAKE_LOCK方面的工作(isScreenLock返回為true的情況)。其中一個重要的函數就是setPowerState,該函數將根據不同的狀態設置屏幕光、鍵盤燈等硬件設備。注意,和硬件交互相關的工作是通過Power類提供的接口完成的。 3. acquireWakeLocked分析之三 acquireWakeLocked處理WAKE_LOCK為PARTIAL_WAKE_LOCK的情況。來看以下代碼: **PowerManagerService.java::acquiredWakeLockLocked** ~~~ else if ((flags & LOCK_MASK) == PowerManager.PARTIAL_WAKE_LOCK){ if(newlock) { mPartialCount++; } //獲取kernel層的PARTIAL_WAKE_LOCK,該函數后續再分析 Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME); }//else if判斷結束 if(diffsource) { noteStopWakeLocked(wl, oldsource); } if(newlock || diffsource) { noteStartWakeLocked(wl, ws);//通知BatteryStatsService做電量統計 } ~~~ 當客戶端使用PARTIAL_WAKE_LOCK時,PMS會調用Power.acquireWakeLock申請一個內核的WakeLock。 4. acquireWakeLock總結 acquireWakeLock有三個階段的工作,總結如下: - 如果對應的WakeLock不存在,則創建一個WakeLock對象,同時將WAKE_LOCK標志轉換成對應的minState;否則,從mLocks中查找對應的WakeLock對象,然后更新其中的信息。 - 當WAKE_LOCK標志和屏幕有關時,需要做相應的處理,例如點亮屏幕、打開按鍵燈等。實際上這些工作不僅影響電源管理,還會影響到用戶感受,所以其中還穿插了一些和用戶體驗有關的處理邏輯(如上面注釋的mPreventScreenOn變量)。 - 當WAKE_LOCK和PARTIAL_WAKE_LOCK有關時,僅簡單調用Power的acquireWakeLock即可,其中涉及和Linux Kernel電源管理系統的交互。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看