Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android APP耗電優化

Android APP耗電優化

編輯:關於Android編程

可能造成耗電的一些原因

網絡請求耗電,而且手機數據網絡進行http請求比無線網進行http請求更加耗電,因為數據網絡調用到一些底層的硬件模塊,就如GPS一樣,當手機打開GPS功能後,也是啟動了一些硬件模塊就會明顯增加耗電 高頻的刷新UI界面,刷新UI界面其實就是進行layout的繪制,如果一個Activity的布局嵌套太多層,那麼每一層layout都會刷新一次,例如動畫等等這些都會造成耗電 數據庫,SD卡文件操作,這些都是屬於耗時操作,當操作次數很少的時候基本不會有耗電問題,但是當短時間內操作次數很多的話,也會明顯的增加耗電,同時也有可能造成頁面卡頓 AlarmManager,例如一些推送的心跳包實現,AlarmManager會定時喚醒CPU去執行一些任務,也是造成耗電的一大源頭 手機網絡環境不好的時候會頻繁的切換網絡,因為網絡數據交互的時候,系統也是會被喚醒的,所以APP如果在監聽了網絡切換廣播後做了大量的操作,一樣會增加耗電 針對一些任務隊列的處理,如果隊列堆積的任務太多,導致循環執行太久也會造成耗電,因為占用了CPU資源去執行代碼,我們的log日志工具保存到文件就是用任務隊列實現的,當壓力測試SDK一次性接受1萬條消息的時候,那內存就表上來了,跟了下發現日志保存隊列裡面積壓了4千多個任務,這時候即使手機鎖屏,也還會不斷的把隊列中的任務執行完然後CPU才會休眠下去的,同樣會造成嚴重的耗電,耗內存,好在release版本的日志都是關閉的 執行一些高運算量的代碼,例如json數據解析,一些二進制協議的數據編碼和解碼 接收系統的一分鐘廣播,然後做一些程序邏輯處理,其實接收一分鐘廣播不耗電,耗電的是一分鐘執行一次程序處理 Wake Lock使用不當導致沒有及時的釋放,Wake Lock可以阻止cpu進入休眠的,如果沒有及時的release會造成cpu無法休眠,程序耗電嚴重 如果程序中有定時任務,在cpu休眠之後,定時任務就會被掛起不執行,這時候並不會造成太大的耗電,但是如果這個定時任務的時間間隔很短,1秒執行一次,那麼當手機app集成了推送,推送就會有心跳包通過AlarmManager來喚醒,每次喚醒的時候就會再去執行掛起的定時任務,雖然執行定時任務的耗電量可能比心跳包的耗電量少很多,不過還是需要注意的,積少成多 其實android中的Log日志的打印也會耗電的,在日常開發中,我們可能不僅會把log打印到AndroidStudio裡面,有可能還會保存起來,而且可能在打印對象信息數據的時候會用到json格式轉換,這些都會增加耗電,但是在正式發布的apk包中日志管理一般都是關閉的 在手機鎖屏後,CPU會過一段時間才休眠,如果程序中有定時任務,在CPU休眠後會被掛起不執行,但是在CPU休眠之前,定時任務還是會一直的執行的,之前遇到過這麼一個問題,我們采用Picasso庫:Picasso.with(context)
Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
      RequestTransformer requestTransformer, List extraRequestHandlers, Stats stats,
      Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
    this.context = context;
    this.dispatcher = dispatcher;
    this.cache = cache;
    this.listener = listener;
    this.requestTransformer = requestTransformer;
    this.defaultBitmapConfig = defaultBitmapConfig;

    int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
    int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
    List allRequestHandlers =
        new ArrayList(builtInHandlers + extraCount);

    // ResourceRequestHandler needs to be the first in the list to avoid
    // forcing other RequestHandlers to perform null checks on request.uri
    // to cover the (request.resourceId != 0) case.
    allRequestHandlers.add(new ResourceRequestHandler(context));
    if (extraRequestHandlers != null) {
      allRequestHandlers.addAll(extraRequestHandlers);
    }
    allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
    allRequestHandlers.add(new MediaStoreRequestHandler(context));
    allRequestHandlers.add(new ContentStreamRequestHandler(context));
    allRequestHandlers.add(new AssetRequestHandler(context));
    allRequestHandlers.add(new FileRequestHandler(context));
    allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
    requestHandlers = Collections.unmodifiableList(allRequestHandlers);

    this.stats = stats;
    this.targetToAction = new WeakHashMap

在Picasso構造的過程中:啟動了一個線程CleanupThread

private static class CleanupThread extends Thread {
    private final ReferenceQueue

CleanupThread線程裡面是有一個while(true){}循環,這個就會在手機鎖屏後持續運行一段時間才會被掛起的,這個過程就增加了耗電,然後打開查看CPU使用情況,發現app一直被占用一點點,正常情況下,app的退到後台後,cpu應該是不會一直被占用的,如下圖:
這裡寫圖片描述
包名com.xtc.watch在退到後台之後仍然在占用CPU的資源執行一些東西,然後打開AndroidStudio,查看monitor裡面的cpu的方法跟蹤功能(Start method tracing)
方法跟蹤功能,紅色部分就表示cpu被占用的比例,過一段時間後再次點擊Start Method Tracing按鈕停止方法跟蹤,然後AndroidStudio會自動生成一個後綴名為.trace的分析文件:
方法執行線程信息
上圖中Thread後面的是執行方法所在線程的名稱,Wall Clock Time表示程序從運行到終止所花費的時間,Thread Time是線程執行的時長,CPU Time是CPU的運行時間,在Thread列表裡面的就表示在方法跟蹤期間有執行過的線程,下面有顏色的長條是線程執行的時間信息Wall clock time和cpu time,如果在程序退到後台,手機鎖屏後,仍然不斷有程序裡面的線程在跑,那麼就存在了耗電風險了,你想想,程序退到後台,手機鎖屏了,這時候所有程序線程有在方法跟蹤期間的縣城都已經展示出來了,如果發現程序耗電,很可能就是由於線程還在不斷的運行造成的
- 在Service裡面執行一些循環代碼,也
可能造成耗電;如果Service是前台Service,那Service的進程優先級會比較高,或者是一些後台常駐Service,在這些Service執行無限循環的代碼,耗電耗到手機發燙:

public class PushService extends Service {

    private static final String tag = PushService.class.getSimpleName();

    private ExecutorService executorService = Executors.newSingleThreadExecutor();

    private Queue taskQueue = new LinkedList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onCreate.");
        Runnable task = null;
        while (!executorService.isShutdown()) {
            if ((task = taskQueue.poll()) != null) {
                task.run();
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(tag, this.getPackageName() + ":" + this.getClass().getSimpleName() + " onDestroy.");
        executorService.shutdownNow();
    }
}

while循環裡面的poll()方法並不是阻阻塞的,因此會一直重復的跑這段代碼導致CUP即使在手機鎖屏的時候仍然高速的在運行著循環代碼

針對以上原因進行相應的優化

針對Http請求:
對http請求數據做GZIP壓縮,當前流行的http第三方看默認都支持GZIP壓縮 Http緩存,Http協議有一個Cache機制,當發出http請求的時候會先到指定目錄下檢查是否已經存在這個請求的數據,如果存在並且還沒過時,那麼就會直接返回;而一些第三方例如OkHttp也有有自己的緩存機制OKHTTP緩存 合並Http請求來減少Http請求次數,因為Http底層也是TCP連接,對於每個Http請求,發出請求的時候都會創建TCP連接,請求結束後會斷開TCP連接,那麼當Http請求次數很多的時候就會頻繁的創建和斷開TCP連接,如果把當中一些請求進行合理的合並,那麼就會減少Http請求次數 制定合理的Http請求數據格式和返回數據格式,做到請求數據中沒有冗余字段 可以在Http請求數據格式裡面加一個字段dataVersion代表本地已有數據的版本號,然後傳到服務器,服務器的數據表中也有一個字段是dataVersion,當服務器數據被修改的時候,dataVersion就加一,當檢測到客戶端傳上來的dataVersion小於服務器數據表中的dataVersion的時候就返回最新數據,否則可以直接返回空數據代表當前本地數據已經是最新數據,這樣就不會每次請求http的時候都會返回大量數據,當數據沒有被改變的時候直接返回空,減少了http請求過程中的數據交互(但是要考慮一點,只有一些請求數據量比較大的才適合,因為增加了dataVersion字段後無論給客戶端還是服務端也都相應的增加了維護的成本) 針對數據庫,SD卡文件操作:
APP在對數據庫或者SD卡文件操作的時候無非也是涉及到一些數據的轉換,json轉換,可以采用json解析效率高的第三方庫,例如fast json,Jackson,gson 可以把一些需要持久化到數據庫或者文件中的數據先緩存在內存中,然後在一個時間點一起觸發一同更新到數據庫或者文件中;例如,在進入Activity的時候會首先從數據庫中搜索出帳戶信息並且展示在界面,然後會再去發Http請求服務器的帳戶信息數據,再把服務器最新的帳戶信息數據刷新到界面中,同時也存在一個內存對象中,這時候先不更新到數據庫,當退出這個Activity界面的時候可以去檢測帳戶數據是否發生改變,如果改變了就更新到數據中;這麼做的好處是:如果在Activity界面多次修改數據,那最新的數據都是只更新到內存中的,當Activity退出後才把最新的帳戶信息數據更新到數據庫中,沒必要更改一次就同時把數據更新一次到數據庫 SD卡的文件讀寫操作比數據庫要快,數據庫也是屬於文件,但是在寫數據的時候還要經過sqlite的一些列數據庫操作,但是SD卡在寫數據的時候是直接寫到文件中的,所以針對與簡單數據,標志變量或者數據條數很少的數據,而且安全性要求也不高的數據可以直接存在文件中,例如SharedPreferences中,只有關系型的數據,數據安全性較高的,數據記錄條數比較多的可以選擇數據庫存儲,而且如果數據庫進行加密後,對於數據庫的讀寫操作會更慢了 針對數據庫操作盡量不要直接使用android裡面的Sqlite來手寫讀寫的那些sql語句,可以選擇一些orm框架庫,例如ormlite,GreenDao等等,我們選擇的是ormlite,因為ormlite數據庫加密已經有現成的庫可以提供使用
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved