"
Free apps like Free Chess and Angry Birds spend under 25-35 percent of their energy on game play, but over 65-75 percent on user tracking, uploading user information and downloading ads.
"
android電量統(tǒng)計(jì)的原理可以參看這篇文章:
http://duanqz.github.io/2015-07-21-batterystats-part1
大致原理摘錄如下:
電量記錄
1. Android在進(jìn)行電量統(tǒng)計(jì)時(shí),并不是采用直接記錄電流消耗量的方式臭埋,而是跟蹤硬件模塊在不同狀態(tài)下的使用時(shí)間,收集一些可用信息搀庶,用來近似的計(jì)算出電池消耗量蕉汪。
舉一個(gè)例子盟蚣,假定某個(gè)APK的使用了GPS刺覆,使用時(shí)間用 t 表示绽族。GPS模塊單位時(shí)間的耗電量用 w 表示姨涡,那么,這個(gè)APK使用GPS的耗電量就可以按照如下方式計(jì)算:
耗電量 = 單位時(shí)間耗電量(w) × 使用時(shí)間(t)
frameworks.jar里的frameworks/base/core/res/res/xml/power_profile.xml這個(gè)文件吧慢,記錄著各個(gè)模塊單位時(shí)間的耗電量涛漂, 由廠商定義。
以下是Nexus 5(hammerhead)耗電參數(shù)配置的代碼片段:
<device name="Android">
<!-- All values are in mAh except as noted -->
<item name="none">0</item>
...
<item name="wifi.on">3.5</item>
<item name="wifi.active">73.24</item>
<item name="wifi.scan">75.48</item>
...
<item name="battery.capacity">2300</item>
</device>
2. Android框架層通過一個(gè)名為batterystats的系統(tǒng)服務(wù)检诗,實(shí)現(xiàn)了電量統(tǒng)計(jì)的功能匈仗。
收集信息被組織起來,在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)是由BatteryStats類描述的逢慌。 為了能夠從不同維度統(tǒng)計(jì)耗電量悠轩,這個(gè)數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)得比較復(fù)雜,我們不在這里展開討論攻泼,僅通過一個(gè)收集應(yīng)用程序前臺(tái)運(yùn)行時(shí)間的例子哗蜈,來說明信息收集過程。
記錄應(yīng)用程序中所有Activity從顯示狀態(tài)(Resumed)到消失狀態(tài)(Paused)的時(shí)間坠韩,就能夠統(tǒng)計(jì)應(yīng)用程序的前臺(tái)運(yùn)行時(shí)間距潘。Activity狀態(tài)的切換是由AMS掌控的,因此AMS需要將Activity的狀態(tài)信息通知給batterystats服務(wù)只搁。
當(dāng)Activity要切換到顯示狀態(tài)(Resumed)時(shí)音比,
會(huì)調(diào)用ActivityStackSupervisor.resumeTopActivitiesLocked()方法,
接下來會(huì)調(diào)用ActivityStack.resumeTopActivityInnerLocked()方法來完成Activity的狀態(tài)切換氢惋,在完成狀態(tài)切換后洞翩, 會(huì)調(diào)用
ActivityStackSupervisor.reportResumedActivityLocked()方法,從這里開始焰望,就開始通報(bào)了:“本Activity已經(jīng)進(jìn)入了顯示狀態(tài)”骚亿。
在ActivityStackSupervisor.reportResumedActivityLocked()中得到BatteryStatsImpl對(duì)象, 并啟動(dòng)一個(gè)計(jì)時(shí)器(StopwatchTimer)熊赖,記錄下了啟動(dòng)時(shí)間.在Activity pause時(shí)来屠, 再得到結(jié)束時(shí)間, 這樣就得到了應(yīng)用程序的acitiviy在前臺(tái)的運(yùn)行時(shí)間了震鹉。
除了應(yīng)用程序前臺(tái)運(yùn)行時(shí)間俱笛,還有很多信息是batterystats服務(wù)關(guān)注的,包括WakeLock传趾、Sendor迎膜、Wifi、Audio浆兰、Video等磕仅,這些信息的采集方式與上述過程雷同珊豹,都會(huì)經(jīng)過以下步驟:
- 由相應(yīng)的模塊發(fā)起狀態(tài)變更的通知
- BatteryStats使用定時(shí)器記錄起止時(shí)間
電量信息的儲(chǔ)存
Android支持歷史電量信息的顯示的,如果重新啟動(dòng)Android榕订,那內(nèi)存中的數(shù)據(jù)就丟失了平夜, 所以需要把這些信息存儲(chǔ)到磁盤上,磁盤上的 /data/system/batterystats.bin 文件中就是電量信息的序列化數(shù)據(jù)卸亮。
batterystats服務(wù)啟動(dòng)時(shí)忽妒,會(huì)從 batterystats.bin 這個(gè)文件中讀取數(shù)據(jù),來初始化BatteryStats這個(gè)數(shù)據(jù)結(jié)構(gòu)兼贸。
電量計(jì)算
BatteryStatsHelper.refreshStats()承載了電量計(jì)算的全部過程段直,在需要顯示電量統(tǒng)計(jì)信息的地方,就可以通過BatteryStatsHelper這個(gè)類溶诞,來獲取統(tǒng)計(jì)完成的電量信息鸯檬。 Setting.apk就引用了這個(gè)類。電量計(jì)算大體可以分為兩塊:
1. AppUsage:應(yīng)用程序耗電量計(jì)算螺垢,是指每一個(gè)應(yīng)用程序使用硬件模塊所產(chǎn)生的耗電量
在BatteryStatsHelper.processAppUsage()這個(gè)方法中喧务,實(shí)現(xiàn)了應(yīng)用程序的電量計(jì)算(實(shí)際上統(tǒng)計(jì)的粒度是uid,不同的apk可以運(yùn)行在同一個(gè)uid)枉圃。
2. MiscUsage:其他雜項(xiàng)耗電量計(jì)算
所謂雜項(xiàng)功茴,其實(shí)就是用戶比較關(guān)心的一大類,包括:待機(jī)的耗電量孽亲、亮屏的耗電量坎穿、通話的耗電量、Wifi的耗電量等返劲,這個(gè)統(tǒng)計(jì)是系統(tǒng)層面的玲昧, 作為app的開發(fā)人員可以忽略掉這部分內(nèi)容。
我們來總結(jié)一下應(yīng)用程序的電量計(jì)算過程篮绿。Android通過一個(gè)名為BatteryStats.Uid的數(shù)據(jù)結(jié)構(gòu)來維護(hù)一個(gè)應(yīng)用程序的電量統(tǒng)計(jì)信息孵延。 這個(gè)數(shù)據(jù)結(jié)構(gòu)中,又包含很多子結(jié)構(gòu):
Proc:表示屬于Uid的進(jìn)程亲配,一個(gè)Uid中可能會(huì)有多個(gè)進(jìn)程尘应,每個(gè)進(jìn)程都有CPU占用時(shí)間
WakeLock:表示Uid持有的WakeLock鎖的電量統(tǒng)計(jì),一個(gè)Uid也可能會(huì)持有多個(gè)鎖
Mobile Radio:表示Uid使用數(shù)據(jù)流量的電量統(tǒng)計(jì)弃榨,譬如3G流量菩收、4G流量
Wifi:表示Uid使用wifi的電量統(tǒng)計(jì)
Sendor:表示Uid使用傳感器的電量統(tǒng)計(jì)
Android提供的dumpsys命令用于查看系統(tǒng)服務(wù)的信息, 將batterystats作為參數(shù)鲸睛,就能輸出完整的電量統(tǒng)計(jì)信息。
adb shell dumpsys batterystats
App電量優(yōu)化實(shí)踐
1. 打點(diǎn)網(wǎng)絡(luò)優(yōu)化
現(xiàn)在用戶操作的打點(diǎn)信息是實(shí)時(shí)上報(bào)坡贺, 每單擊一次菜單項(xiàng)就有大概1k的數(shù)據(jù)上傳. 可以把這打點(diǎn)上傳的執(zhí)行封裝起來官辈, 間隔一段時(shí)間統(tǒng)一上傳箱舞, 或是在特殊時(shí)間點(diǎn)統(tǒng)一上傳, 可以有效減少頻繁地使用wifi和mobile radio模塊拳亿。
TXD 發(fā)送數(shù)據(jù) Transmit(tx) Data 的簡寫形式
RXD 接收數(shù)據(jù) Receive(rx) Data 的簡寫形式
2. 盡量減少wakeLock的使用
例如現(xiàn)在的下載文件的邏輯, 應(yīng)該使用帶超時(shí)參數(shù)的acquire() API, 避免長時(shí)間使用wake lock.
DownloadThread.java
PowerManager.WakeLock wakeLock = null;
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Constants.TAG);
wakeLock.acquire();
3. 使用prepn profile 工具實(shí)時(shí)檢測應(yīng)用的耗電量
===DONE===