<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>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                BatteryStatsService(為書寫方便,以后簡稱BSS)主要功能是收集系統中各模塊和應用進程用電量情況。抽象地說,BSS就是一塊電表,不過這塊電表不只是顯示總的耗電量,而是分門別類地顯示耗電量,力圖做到更為精準。 和其他服務不太一樣的是,BSS的創建和注冊是在ActivityManagerService中進行的,相關代碼如下: **ActivityManagerService.java::ActivityManagerService構造函數** ~~~ private ActivityManagerService() { ......//創建BSS對象,傳遞一個File對象,指向/data/system/batterystats.bin mBatteryStatsService= new BatteryStatsService(new File( systemDir, "batterystats.bin").toString()); } ~~~ **ActivityManagerService.java::main** ~~~ //調用BSS的publish函數,在內部將其注冊到ServiceManager m.mBatteryStatsService.publish(context); ~~~ 下面來分析BSS的構造函數,見識一下這塊電表的樣子。 1. BatteryStatsService介紹 讓人大跌眼鏡的是,BSS其實只是一個殼,具體功能委托BatteryStatsImpl(以后簡稱BSImpl)來實現,代碼如下: **BatteryStatsService.java::BatteryStatsService構造函數** ~~~ BatteryStatsService(String filename) { mStats = new BatteryStatsImpl(filename); } ~~~ 圖5-2展示了BSS及BSImpl的家族圖譜。 :-: ![](http://img.blog.csdn.net/20150803122147777?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖5-2 BSS及BSImpl家族圖譜 由圖5-2可知: - BSS通過成員變量mStats指向一個BSImpl類型的對象。 - BSImpl從BatteryStats類派生。更重要的是,該類實現了Parcelable接口,由此可知,BSImpl對象的信息可以寫到Parcel包中,從而可通過Binder在進程間傳遞。實際上,在Android手機的設置中查到的用電信息就是來自BSImpl的。 BSS的getStatistics函數提供了查詢系統用電信息的接口,代碼如下: ~~~ public byte[] getStatistics() { mContext.enforceCallingPermission(//檢查調用進程是否有BATTERY_STATS權限 android.Manifest.permission.BATTERY_STATS, null); Parcel out= Parcel.obtain(); mStats.writeToParcel(out, 0);//將BSImpl信息寫到數據包中 byte[]data = out.marshall();//序列化為一個buffer,然后通過Binder傳遞 out.recycle(); returndata; } ~~~ 由此可以看出,電量統計的核心類是BSImpl,下面就來分析它。 2. 初識BSImpl BSImpl功能是進行電量統計,那么是否存在計量工具呢?答案是肯定的,并且BSImpl使用了不止一種的計量工具。 (1) 計量工具和統計對象介紹 BSImpl一共使用了4種計量工具,如圖5-3所示。 :-: ![](http://img.blog.csdn.net/20150803122205478?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖5-3 計量工具圖例 由圖5-3可知: - 一共有兩大類計量工具,Counter用于計數,Timer用于計時。 - BSImpl實現了StopwatchTimer(即所謂的秒表)、SamplingTimer(抽樣計時)、Counter和SamplingCounter(抽樣計數)等4個具體的計量工具。 - BSImpl中定義了一個Unpluggable接口。當手機插上USB線充電(不論是由AC還是由USB供電)時,該接口的plug函數被調用。反之,當拔去USB線時,該接口的unplug函數被調用。設置這個接口的目的是為了滿足BSImpl對各種情況下系統用電量的統計要求。關于Unpluggable接口的作用,在后續內容中可以能見到。 雖然只有4種計量工具(筆者覺得已經相當多了),但是可以在很多地方使用它們。下面先來認識部分被掛牌要求統計用電量的對象,如表5-5所示。 :-: ![ 用電量統計項](https://box.kancloud.cn/29c4adc2bf3a5e42dd6b8d3ffb0a24de_792x344.png =792x344) 表5-5 用電量統計項 表5-5中的電量統計項已經夠多了吧?還不止這些,為了做到更精確,Android還希望能統計每個進程在各種情況下的耗電量。這是一項龐大的工程,怎么做到的呢?來看下一節的內容。 (2) BatteryStats.Uid介紹 在Android 4.0中,和進程相關的用電量統計并非以單個PID為劃分單元,而是以Uid為組,相關類結構如圖5-4所示。 :-: ![](http://img.blog.csdn.net/20150803122221423?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖5-4 BatteryStats.Uid家族 由圖5-4可知: - Wakelock用于統計該Uid對應進程使用wakeLock的情況。 - Proc用于統計Uid中某個進程的電量使用情況。 - Pkg用于統計某個特定Package的使用情況,其內部類Serv用于統計該Pkg中Service的用電情況。 - Sensor用于統計傳感器用電情況。 基于以上的了解,以后分析將會輕松很多,下面來分析它的代碼。 3. BSImpl流程分析 (1) 構造函數分析 先分析構造函數,代碼如下: **BatteryStatsImpl.java::BatteryStatsImpl構造函數** ~~~ public BatteryStatsImpl(String filename) { //JournaledFile為日志文件對象,內部包含兩個文件,原始文件和臨時文件。目的是雙備份, //以防止在讀寫過程中文件信息丟失或出錯 mFile =new JournaledFile(new File(filename), new File(filename + ".tmp")); mHandler= new MyHandler();//創建一個Handler對象 mStartCount++; //創建表5-5中的用電統計項對象 mScreenOnTimer = new StopwatchTimer(null, -1, null, mUnpluggables); for (inti=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i] = new StopwatchTimer(null, -100-i, null, mUnpluggables); } mInputEventCounter = new Counter(mUnpluggables); ...... mOnBattery= mOnBatteryInternal = false;//設置這兩位成員變量為false initTimes();//①初始化統計時間 mTrackBatteryPastUptime = 0; mTrackBatteryPastRealtime = 0; mUptimeStart= mTrackBatteryUptimeStart = SystemClock.uptimeMillis()* 1000; mRealtimeStart= mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime()* 1000; mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart); mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart); mDischargeStartLevel = 0; mDischargeUnplugLevel = 0; mDischargeCurrentLevel = 0; initDischarge(); //②初始化和電池level有關的成員變量 clearHistoryLocked();//③刪除用電統計的歷史記錄 } ~~~ 要看懂這段代碼比較困難,主要原因是變量太多,并且沒有注釋說明。只能根據名字來推測了。在以上代碼中除了計量工具外,還出現了三大類變量: - 用于統計時間的變量,例如mUptimeStart、mTrackBatteryPastUptime等。這些參數的初始化函數為initTimes。注意,系統時間分為uptime和realtime。uptime和realtime的時間起點都從系統啟動開始算(since the system was booted),但是uptime不包括系統休眠時間,而realtime包括系統休眠時間[^platform]。 - 用于記錄各種情況下電池電量的變量,如mDischargeStartLevel、mDischargeCurrentLevel等,這些成員變量的初始化函數為initDischarge。 - 用于保存歷史記錄的HistroryItem,在clearHistoryLocked函數中初始化,主要有mHistory、mHistoryEnd等成員變量(這些成員在clearHistoryLocked函數中出現)。 上述這些成員變量的具體作用,只有通過后文的分析才能弄清楚。這里先介紹StopwacherTimer。 ~~~ //調用方式 mPhoneSignalScanningTimer = newStopwatchTimer(null, -200+1, null,mUnpluggables); //mUnpluggables類型為ArrayList<Unpluggable>,用于保存插拔USB線時需要對應更新用電 //信息的統計對象 // StopwatchTimer的構造函數 StopwatchTimer(Uid uid, int type,ArrayList<StopwatchTimer> timerPool, ArrayList<Unpluggable>unpluggables) { //在本例中,uid為0,type為負數,timerPool為空,unpluggables為mUnpluggables super(type, unpluggables); mUid =uid; mTimerPool = timerPool; } // Timer的構造函數 Timer(int type, ArrayList<Unpluggable>unpluggables) { mType =type; mUnpluggables = unpluggables; unpluggables.add(this); } ~~~ 在StopwatchTimer中比較難理解的就是unpluggables,根據注釋說明,當拔插USB線時,需要更新用電統計的對象,應該將其加入到mUnpluggables數組中。 在啟動秒表時,調用它的startRunningLocked函數,并傳入BSImpl實例,代碼如下: ~~~ void startRunningLocked(BatteryStatsImpl stats) { if(mNesting++ == 0) {//嵌套調用控制 // getBatteryRealtimeLocked函數返回總的電池使用時間 mUpdateTime = stats.getBatteryRealtimeLocked( SystemClock.elapsedRealtime()* 1000); if (mTimerPool != null) {//不討論這種情況 } mCount++; mAcquireTime = mTotalTime;//計數控制,請讀者閱讀相關注釋說明 } } ~~~ 當停用秒表時,調用它的stopRunningLocked函數,代碼如下: ~~~ void stopRunningLocked(BatteryStatsImpl stats) { if (mNesting == 0) { return; //嵌套控制 } if(--mNesting == 0) { if(mTimerPool != null) {//不討論這種情況 }else { final long realtime = SystemClock.elapsedRealtime() * 1000; //計算此次啟動/停止周期的時間 final long batteryRealtime = stats.getBatteryRealtimeLocked(realtime); mNesting = 1; //mTotalTime代表從啟動開始該秒停表一共記錄的時間 mTotalTime = computeRunTimeLocked(batteryRealtime); mNesting = 0; } if (mTotalTime == mAcquireTime) mCount--; } } ~~~ 在StopwatchTimer中定義了很多的時間參數,無非就是用于記錄各種時間,例如總耗時、最近一次工作周期的耗時等。如果不是工作需要(例如研究Settings應用中和BatteryInfo相關的內容),讀者僅需了解它的作用即可。 (2) ActivityManagerService和BSS交互 ActivityManagerService創建BSS后,還要進行幾項操作,具體代碼分別如下: **ActivityManagerService.java::ActivityManagerService構造函數** ~~~ mBatteryStatsService = new BatteryStatsService(newFile( systemDir, "batterystats.bin").toString()); //操作通過BSImpl創建的JournaledFile文件 mBatteryStatsService.getActiveStatistics().readLocked(); mBatteryStatsService.getActiveStatistics().writeAsyncLocked(); //BSImpl的getIsOnBattery返回mOnBattery變量,初始化值為false mOnBattery= DEBUG_POWER ? true : mBatteryStatsService.getActiveStatistics().getIsOnBattery(); //設置回調,該回調也是用于信息統計,只能留到介紹ActivityManagerService時再來分析了 mBatteryStatsService.getActiveStatistics().setCallback(this); ~~~ **ActivityManagerService.java::main函數** ~~~ m.mBatteryStatsService.publish(context); ~~~ **BatteryStatsService.java::publish** ~~~ public void publish(Context context) { mContext =context; //注意,BSS服務叫做batteryinfo,而BatteryService服務叫做battery ServiceManager.addService("batteryinfo", asBinder()); //PowerProfile見下文解釋 mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps()); //設置通信信號掃描超時時間 mStats.setRadioScanningTimeout(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); } ~~~ 在以上代碼中,比較有意思的是PowerProfile類,它將解析Android 4.0源碼/frameworks/base/core/res/res/xml/power_profile.xml文件。此XML文件存儲的是各種操作(和硬件相關)的耗電情況,如圖5-5所示。 :-: ![](http://img.blog.csdn.net/20150803122238418?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖5-5 PowerProfile文件示例 由圖5-5可知,該文件保存了各種操作的耗電情況,以mAh(毫安)為單位。PowerProfile的getNumSpeedSteps將返回CPU支持的頻率值,目前在該XML中只定義了一個值,即400MHz。 注意在編譯時,各廠家會將特定硬件平臺的power_profile.xml復制到輸出目錄。此處展示的power_profile.xml和硬件平臺無關。 (3) BatteryService和BSS交互 BatteryService在它的processValues函數中和BSS交互,代碼如下: **BatteryService.java** ~~~ private void processValues() { ...... mBatteryStats.setBatteryState(mBatteryStatus,mBatteryHealth, mPlugType, mBatteryLevel, mBatteryTemperature,mBatteryVoltage); } ~~~ BSS的工作由BSImpl來完成,所以直接setBatteryState函數的代碼: **BatteryStatsImpl.java::setBatteryState** ~~~ public void setBatteryState(int status, inthealth, int plugType, int level, int temp, int volt) { synchronized(this) { boolean onBattery = plugType == BATTERY_PLUGGED_NONE;//判斷是否為電池供電 intoldStatus = mHistoryCur.batteryStatus; ...... if(onBattery) { //mDischargeCurrentLevel記錄當前使用電池供電時的電池電量 mDischargeCurrentLevel = level; mRecordingHistory = true;//mRecordingHistory表示需要記錄一次歷史值 } //此時,onBattery為當前狀態,mOnBattery為歷史狀態 if(onBattery != mOnBattery) { mHistoryCur.batteryLevel = (byte)level; mHistoryCur.batteryStatus = (byte)status; mHistoryCur.batteryHealth = (byte)health; ......//更新mHistoryCur中的電池信息 setOnBatteryLocked(onBattery, oldStatus, level); } else { boolean changed = false; if (mHistoryCur.batteryLevel != level) { mHistoryCur.batteryLevel = (byte)level; changed = true; } ......//判斷電池信息是否發生變化 if (changed) {//如果發生變化,則需要增加一次歷史記錄 addHistoryRecordLocked(SystemClock.elapsedRealtime()); } } if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL){ mRecordingHistory = false; } } } ~~~ setBatteryState函數的工作主要有兩項: - 判斷當前供電狀態是否發生變化,由onBattery和mOnBattery進行比較。其中onBattery用于判斷當前是否為電池供電,mOnBattery為上次調用該函數時得到的判斷值。如果供電狀態發生變化(其實就是經歷一次USB拔插過程),則調用setOnBatteryLocked函數。 - 如果供電狀態未發生變化,則需要判斷電池信息是否發生變化,例如電量和電壓等。如果發生變化,則調用addHistoryRecordLocked。該函數用于記錄一次歷史信息。 接下來看setOnBatteryLocked函數的代碼: **BatteryStatsImpl.java::setOnBatteryLocked** ~~~ void setOnBatteryLocked(boolean onBattery, intoldStatus, int level) { boolean doWrite = false; //發送一個消息給mHandler,將在內部調用ActivityManagerService設置的回調函數 Message m= mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE); m.arg1 =onBattery ? 1 : 0; mHandler.sendMessage(m); mOnBattery = mOnBatteryInternal = onBattery; longuptime = SystemClock.uptimeMillis() * 1000; longmSecRealtime = SystemClock.elapsedRealtime(); longrealtime = mSecRealtime * 1000; if(onBattery) { //關于電量信息統計,有一個值得注意的地方:當oldStatus為滿電狀態,或當前電量 //大于90,或mDischargeCurrentLevel小于20并且當前電量大于80時,要清空統計 //信息,以開始新的統計。也就是說在滿足特定條件的情況下,電量使用統計信息會清零并重 //新開始。讀者不妨用自己手機一試 if(oldStatus == BatteryManager.BATTERY_STATUS_FULL || level >= 90 || (mDischargeCurrentLevel < 20 && level >= 80)) { doWrite = true; resetAllStatsLocked(); mDischargeStartLevel = level; } //讀取/proc/wakelock文件,該文件反映了系統wakelock的使用狀態, //感興趣的讀者可自行研究 updateKernelWakelocksLocked(); mHistoryCur.batteryLevel = (byte)level; mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; //添加一條歷史記錄 addHistoryRecordLocked(mSecRealtime); //mTrackBatteryUptimeStart表示使用電池的開始時間,由uptime表示 mTrackBatteryUptimeStart = uptime; // mTrackBatteryRealtimeStart表示使用電池的開始時間,由realtime表示 mTrackBatteryRealtimeStart = realtime; //mUnpluggedBatteryUptime記錄總的電池使用時間(不論中間插拔多少次) mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime); // mUnpluggedBatteryRealtime記錄總的電池使用時間 mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime); //記錄電量 mDischargeCurrentLevel =mDischargeUnplugLevel = level; if(mScreenOn) { mDischargeScreenOnUnplugLevel = level; mDischargeScreenOffUnplugLevel = 0; }else { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenOffUnplugLevel = level; } mDischargeAmountScreenOn = 0; mDischargeAmountScreenOff = 0; //調用doUnplugLocked函數 doUnplugLocked(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime); }else { ......//處理使用USB充電的情況,請讀者在上面討論的基礎上自行分析 } ......//記錄信息到文件 } } ~~~ doUnplugLocked函數將更新對應信息,該函數比較簡單,無須贅述。另外,addHistoryRecordLocked函數用于增加一條歷史記錄(由HistoryItem表示),讀者也可自行研究。 從本節的分析可知,Android將電量統計分得非常細,例如由電池供電的情況需要統計,由USB/AC充電的情況也要統計,因此有setBatteryState函數的存在。 (4) PowerManagerService和BSS交互 PMS和BSS交互是最多的,此處以noteScreenOn和noteUserActivity為例,來介紹BSS到底是如何統計電量的。 先來看noteScreenOn函數。當開啟屏幕時,PMS會調用BSS的noteScreenOn以通知屏幕開啟,該函數在內部調用BSImpl的noteScreenOnLocked,其代碼如下: **BatteryStatsImpl.java::noteScreenOnLocked** ~~~ public void noteScreenOnLocked() { if(!mScreenOn) { mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG; //增加一條歷史記錄 addHistoryRecordLocked(SystemClock.elapsedRealtime()); mScreenOn = true; //啟動mScreenOnTime秒停表,內部就是記錄時間,讀者可自行研究 mScreenOnTimer.startRunningLocked(this); if(mScreenBrightnessBin >= 0)//啟動對應屏幕亮度的秒停表(參考表5-5) mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(this); //屏幕開啟也和內核WakeLock有關,所以這里一樣要更新WakeLock的用電統計 noteStartWakeLocked(-1, -1, "dummy", WAKE_TYPE_PARTIAL); if(mOnBatteryInternal) updateDischargeScreenLevelsLocked(false, true); } } ~~~ 再來看noteUserActivity,當有輸入事件觸發PMS的userActivity時,該函數被調用,代碼如下,: **BatteryStatsImpl.java::noteUserActivityLocked** ~~~ //BSS的noteUserActivity將調用BSImpl的noteUserActivityLocked public void noteUserActivityLocked(int uid, intevent) { getUidStatsLocked(uid).noteUserActivityLocked(event); } ~~~ 先是調用getUidStatsLocked以獲取一個Uid對象,如果該Uid是首次出現的,則要在內部創建一個Uid對象。直接來了解Uid的noteUserActivityLocked函數: ~~~ public void noteUserActivityLocked(int type) { if(mUserActivityCounters == null) { initUserActivityLocked(); } if (type< 0) type = 0; else if(type >= NUM_USER_ACTIVITY_TYPES) type= NUM_USER_ACTIVITY_TYPES-1; // noteUserActivityLocked只是調用對應type的Counter的stepAtomic函數 //每個Counter內部都有個計數器,stepAtomic使該計數器增1 mUserActivityCounters[type].stepAtomic(); } ~~~ mUserActivityCounters為一個7元Counter數組,該數組對應7種不同的輸入事件類型,在代碼中,由BSImpl的成員變量USER_ACTIVITY_TYPES表示,如下所示: ~~~ static final String[] USER_ACTIVITY_TYPES = { "other", "cheek", "touch","long_touch", "touch_up", "button", "unknown" }; ~~~ 另外,在LocalPowerManager中,也定義了相關的type值,如下所示: **LocalPowerManager.java** ~~~ public interface LocalPowerManager { publicstatic final int OTHER_EVENT = 0; publicstatic final int BUTTON_EVENT = 1; publicstatic final int TOUCH_EVENT = 2; //目前只使用這三種事件 ...... } ~~~ [^platform]: 讀者可閱讀SDK文檔中關于SystemClock類的說明。
                  <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>

                              哎呀哎呀视频在线观看