Android耗電優化實踐 (一)- 利用Hook方式監控排查耗電

Android耗電優化實踐 (一)

Java Hook

Hook 方案的好處在於使用者接入非常簡單,不需要去修改自己的代碼。下面我以幾個比較常用的規則爲例,看看如果使用 Java Hook 達到監控的目的。

  • WakeLock。WakeLock 用來阻止 CPU、屏幕甚至是鍵盤的休眠。類似 Alarm、JobService 也會申請 WakeLock 來完成後臺 CPU 操作。WakeLock 的核心控制代碼都在PowerManagerService中,實現的方法非常簡單。
// 代理 PowerManagerService
ProxyHook().proxyHook(context.getSystemService(Context.POWER_SERVICE), "mService", this);

@Override
public void beforeInvoke(Method method, Object[] args) {
    // 申請 Wakelock
    if (method.getName().equals("acquireWakeLock")) {
        if (isAppBackground()) {
            // 應用後臺邏輯,獲取應用堆棧等等     
         } else {
            // 應用前臺邏輯,獲取應用堆棧等等
         }
    // 釋放 Wakelock
    } else if (method.getName().equals("releaseWakeLock")) {
       // 釋放的邏輯    
    }
}

// 代理 AlarmManagerService
new ProxyHook().proxyHook(context.getSystemService
(Context.ALARM_SERVICE), "mService", this);

public void beforeInvoke(Method method, Object[] args) {
    // 設置 Alarm
    if (method.getName().equals("set")) {
        // 不同版本參數類型的適配,獲取應用堆棧等等
    // 清除 Alarm
    } else if (method.getName().equals("remove")) {
        // 清除的邏輯
    }
}

  • 其他。對於後臺 CPU,我們可以使用卡頓監控相關的方法。對於後臺網絡,同樣我們可以通過網絡監控相關的方法。對於 GPS 監控,我們可以通過 Hook 代理LOCATION_SERVICE。對於 Sensor,我們通過 Hook SENSOR_SERVICE中的“mSensorListeners”,可以拿到部分信息。

通過 Hook,我們可以在申請資源的時候將堆棧信息保存起來。當我們觸發某個規則上報問題的時候,可以將收集到的堆棧信息、電池是否充電、CPU 信息、應用前後臺時間等輔助信息也一起帶上。

開始編碼

下面開始lib_battery_hook的編寫,這個框架花了兩天時間完成,主要是裏面代理Hook的實現,需要翻閱相關的源碼並分析,最終實現了IAlarmManagerHook、IPowerManagerHook、ILocationManagerHook這三那個代理,分別實現對AlarmManager、PowerManager、LocationManager的監聽。


/**
 * @date on 2019/2/19
 * @author  lyg-hhy1
 * @email  [email protected]
 * @describe
 */
public class BatteryHookManager {

    /**
     * 初始化
     * @param context
     */
    public void initHook(Context context){
        //初始化日誌
        BatteryLogUtil.init(context);

        //初始化Hook
        new IAlarmManagerHook(context).onInstall();
        new IPowerManagerHook(context).onInstall();
        new ILocationManagerHook(context).onInstall();
    }




    private BatteryHookManager(){}
    private static class BatteryHookManagerHolder{
        private static final BatteryHookManager INSTANCE= new BatteryHookManager();
    }

    public static BatteryHookManager getImp(){
        return BatteryHookManagerHolder.INSTANCE;
    }

}

在項目的application裏面初始化BatteryHook,

public class BatteryApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        BatteryHookManager.getImp().initHook(this);

    }
}

發現問題

集成後,迫不及待跑了一下,果然看到了下面這些日誌:

在這裏插入圖片描述
這個好像是高德地圖SDK的定位,每次項目啓動app都會觸發一次,

在這裏插入圖片描述
還發現一處比較詭異的,IMPushService長期持有設備喚醒鎖,看了一下代碼

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        try {
            if (wakeLock != null && !wakeLock.isHeld()) {
                wakeLock.acquire();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        ……
    }




    @Override
    public void onDestroy() {
        if (wakeLock != null && wakeLock.isHeld()) {
            wakeLock.release();
        }
        wakeLock = null;
        ……
    }

很奇怪,從IM的服務啓動,到銷燬,一直持有設備喚醒鎖,這肯定非常的耗電。接下來我們採用的是Battery Historian2.0驗證了一下,Historian2.0這是Google推出的一款Android系統電量分析工具,支持5.0(API 21)及以上系統手機的電量分析。

修復並驗證

這是優化之前的好定分析:

在這裏插入圖片描述
在這裏插入圖片描述

下面是去掉IMService中長期喚醒設備,這是優化之後的結果:

在這裏插入圖片描述
在這裏插入圖片描述

統計結果如下:

測試場景 測試時長 消耗電量
去掉wakeLock之前 44m24s466ms 0.27%
去掉之後 34m56s238ms 0.03%

從測試結果可以看出,沒有優化前耗電是優化後的9倍

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章