第八章 性能優化 之 其他優化(四)

第八章 性能優化 之 其他優化(四)

(一)網絡優化

(1)網絡優化的必要性

1、流量消耗小
2、電量消耗小
3、用戶等待時間短

(2)分析網絡連接的工具

1、Network Monitor

屬於Android內置Monitor工具,實時跟蹤選定應用的數據請求情況. 我們可以連上手機, 選定調試應用進程, 然後在App上操作我們需要分析的頁面請求.

2、網絡代理工具

一般來說, 網絡代理工具有兩個作用:

  • 截獲網絡請求響應包, 分析網絡請求
  • 設置代理網絡, 移動App開發中一般用來做不同網絡環境的測試, 例如Wifi/4G/3G/弱網等.
    代理工具很多, 諸如Wireshark, Fiddler, Charles等

(3)網絡連接的優化

1、思想

減少Radio活躍時間
也就是減少網絡數據獲取的頻次.這就減少了radio的電量消耗, 控制電量使用.
減少獲取數據包的大小
可以減少流量消耗也可以讓每次請求更快, 在網絡情況不好的情況下也有良好表現, 提升用戶體驗.

2、設計

(1)接口設計
  • API設計
    App與Server之間的API設計要考慮網絡請求的頻次, 資源的狀態等. 以便App可以以較少的請求來完成業務需求和界面的展示.
  • Gzip壓縮
    使用Gzip來壓縮request和response, 減少傳輸數據量, 從而減少流量消耗.
  • 使用Protocol Buffer代替JSON
    Protocol Buffer是Google推出的一種數據交換格式.如果我們的接口每次傳輸的數據量很大的話, 可以考慮下protobuf, 會比JSON數據量小很多.
    JSON也有其優勢, 可讀性更高.減少數據量(當然還有映射成POJO的方便程度)
  • 圖片的Size
    圖片相對於接口請求來說, 數據量要大得多. 故而也是我們需要優化的一個點.我們可以在獲取圖片時告知服務器需要的圖片的寬高, 以便服務器給出合適的圖片, 避免浪費.
(2)網絡緩存

適當的緩存, 既可以讓我們的應用看起來更快, 也能避免一些不必要的流量消耗.

  • 有網時根據設置的Cache Control時間來判斷是使用緩存還是重新做網絡請求.
  • 無網絡環境下直接使用緩存, 保證閱讀體驗.
(3)打包網絡請求

當接口設計不能滿足我們的業務需求時. 例如可能一個界面需要請求多個接口, 或是網絡良好, 處於Wifi狀態下時我們想獲取更多的數據等.
這時就可以打包一些網絡請求, 例如請求列表的同時, 獲取Header點擊率較高的的item項的詳情數據.

(4)監聽相關狀態

通過監聽設備的狀態:休眠狀態、充電狀態、網絡狀態
結合JobScheduler來根據實際情況做網絡請求. 比方說Splash閃屏廣告圖片, 我們可以在連接到Wifi時下載緩存到本地; 新聞類的App可以在充電, Wifi狀態下做離線緩存.

(5)弱網測試&優化

除了正常的網絡優化, 我們還需考慮到弱網情況下, App的表現.創建和啓動Android模擬器可以設置網絡速度和延遲
弱網優化, 本質上是在弱網的情況下能讓用戶流暢的使用我們的App. 我們要做的就是結合上述的優化項:

  • 壓縮/減少數據傳輸量
  • 利用緩存減少網絡傳輸
  • 針對弱網(移動網絡), 不自動加載圖片
  • 界面先反饋, 請求延遲提交
(6)服務器優化

包括服務器端的代碼開發, 部署方式等

(4)Http與Https對訪問速度(性能)的影響

HTTPS 在保護用戶隱私,防止流量劫持方面發揮着非常關鍵的作用,但與此同時,HTTPS 也會降低用戶訪問速度,增加網站服務器的計算資源消耗。
影響主要來自兩方面:
a、協議交互所增加的網絡 RTT(round trip time)。
b、加解密相關的計算耗時。

1、網絡耗時增加

HTTP 首個請求的網絡耗時,用戶只需要完成 TCP 三次握手建立 TCP 連接就能夠直接發送 HTTP 請求獲取應用層數據,此外在整個訪問過程中也沒有需要消耗計算資源的地方。HTTPS 的訪問過程,相比 HTTP 要複雜很多,在部分場景下,使用HTTPS 訪問有可能增加 7 個 RTT。

2、計算耗時增加

密鑰交換時加密解密過程,需要非常消耗CPU資源的計算耗時

(二)電池使用優化

(1)分析電量使用情況工具

a.Batterystats & bugreport
b.Battery Historian

(2)主要的耗電因素

1、網絡請求

2、WakeLock

Android系統本身爲了優化電量的使用, 會在沒有操作時進入休眠狀態, 來節省電量.
可以用WakeLock來保持CPU運行, 或是防止屏幕變暗/關閉, 讓手機可以在用戶不操作時依然可以做一些事兒(如播放視屏);或者滅屏後執行其他操作。

3、GPS

(3)電量優化

1、優化網絡請求

(見網絡優化)

2、謹慎使用WakeLock

(1)WakeLock獲取釋放成對出現
(2)使用超時WakeLock,以防異常導致沒有沒有釋放

// Acquires the wake lock with a timeout.
acquire(long timeout)

3、監聽手機充電狀態

BatteryManager會發送一個包含充電狀態的持續廣播, 我們可以通過此廣播獲取充電狀態和電量詳情:

IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, ifilter);

注意: 因爲這是一個持續廣播, 我們無需寫receiver, 可以直接通過intent獲取相關數據.

(1)通過Intent獲取充電狀態和電量詳情
例如, 如果設備正在充電:

// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
                     status == BatteryManager.BATTERY_STATUS_FULL;

// How are we charging?
int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;

(2)註冊receiver監聽充電狀態變化
只要設備連接或斷開電源, BatteryManager就會廣播相應的操作, 我們可以註冊receiver來監聽:

<receiver android:name=".PowerConnectionReceiver">
  <intent-filter>
    <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
    <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
  </intent-filter>
</receiver>

監聽電池狀態, 可以讓我們將一些操作放在充電或是電量足夠的情況下進行, 以提升用戶體驗. 例如用戶數據同步, Log上傳等.

4、Doze and App Standby

Android 6.0提供了兩個用來節省電量的技術Doze和App Standby.
(1)Doze
瞌睡. 如果設備閒置了一段較長時間, Doze技術將通過延遲後臺網絡活動, CPU運行等來減少電量損耗.
(2)App Standy
應用待機. 不是最近得到過用戶"寵幸"的App, App Standy將延緩這個應用的後臺網絡活動.

5、關於定位

(1)定位中使用GPS, 請記得及時關閉

// Remove the listener you previously added
locationManager.removeUpdates(locationListener);

(2)減少更新頻率
(3)根據實際情況選擇GPS或網絡或兩者. 只使用一個會降低電量損耗.

(三)ANR詳解

(1)ANR簡介

1、定義

ANR全名Application Not Responding, 也就是"應用無響應".

2、產生原因

在Android裏, App的響應能力是由Activity Manager和Window Manager系統服務來監控的. 通常在如下兩種情況下會彈出ANR對話框:
(1)5s內無法響應用戶輸入事件(例如鍵盤輸入, 觸摸屏幕等).
(2)BroadcastReceiver在10s內無法結束.
造成以上兩種情況的首要原因就是在主線程(UI線程)裏面做了太多的阻塞耗時操作, 例如文件讀寫, 數據庫讀寫, 網絡查詢等等.

3、如何避免

不要在主線程(UI線程)裏面做繁重的操作.

(2)ANR分析

1、獲取ANR產生的trace文件

ANR產生時, 系統會生成一個traces.txt的文件放在/data/anr/下. 可以通過adb命令將其導出到本地:

$adb pull data/anr/traces.txt .

2、分析trace.txt

1.普通阻塞導致的ANR
 - locked <0x35fc9e33> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:985) // 主線程中sleep過長時間, 阻塞導致無響應.
2.CPU滿負荷
100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait

CPU佔用100%,滿負荷,覺得多數是被IOwait,即IO操作佔用。此時分析方法調用棧, 一般來說會發現是方法中有頻繁的文件讀寫或是數據庫讀寫操作放在主線程來做了.

3.內存問題(內存泄露/溢出)
size: 17036 23111 N/A 40147
allocated: 16484 20675 N/A 37159
free: 296 2436 N/A 2732//free內存所剩無幾

3、ANR處理

1.主線程阻塞

開闢單獨的子線程來處理耗時阻塞事務.

2.CPU滿負荷/IO阻塞

I/O阻塞一般來說就是文件讀寫或數據庫操作執行在主線程了, 也可以通過開闢子線程的方式異步執行.

3.內存不足

增大VM內存, 使用largeHeap屬性, 排查內存泄露(內存優化)等.

(3)避免ANR方法

以預防爲主,有主線程和子線程概念,認清代碼的阻塞點,善用線程。
思想:不要在主線程(UI線程)裏面做繁重的操作.

1、在主線程執行的操作

  • Activity的所有生命週期回調都是執行在主線程的.
  • Service默認是執行在主線程的.
  • BroadcastReceiver的onReceive回調是執行在主線程的.
  • 沒有使用子線程的looper的Handler的handleMessage, post(Runnable)是執行在主線程的.
  • AsyncTask的回調中除了doInBackground, 其他都是執行在主線程的.
  • View的post(Runnable)是執行在主線程的.

2、使用子線程的方式

1.Thread方式

繼承Thread

class PrimeThread extends Thread {
    long minPrime;
    PrimeThread(long minPrime) {
        this.minPrime = minPrime;
    }

    public void run() {
        // compute primes larger than minPrime
         . . .
    }
}

PrimeThread p = new PrimeThread(143);
p.start();

實現Runnable接口

class PrimeRun implements Runnable {
    long minPrime;
    PrimeRun(long minPrime) {
        this.minPrime = minPrime;
    }

    public void run() {
        // compute primes larger than minPrime
         . . .
    }
}

PrimeRun p = new PrimeRun(143);
new Thread(p).start();
2.AsyncTask方式

AsyncTask開啓異步任務

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    // Do the long-running work in here
    // 執行在子線程
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }

    // This is called each time you call publishProgress()
    // 執行在主線程
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    // This is called when doInBackground() is finished
    // 執行在主線程
    protected void onPostExecute(Long result) {
        showNotification("Downloaded " + result + " bytes");
    }
}

// 啓動方式
new DownloadFilesTask().execute(url1, url2, url3);
3.HandlerThread方式

Android中結合Handler和Thread的一種方式. 默認情況下Handler的handleMessage是執行在主線程的, 但是如果我給這個Handler傳入了子線程的looper, handleMessage就會執行在這個子線程中的. HandlerThread正是這樣的一個結合體:

// 啓動一個名爲new_thread的子線程
HandlerThread thread = new HandlerThread("new_thread");
thread.start();

// 取new_thread賦值給ServiceHandler
private ServiceHandler mServiceHandler;
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);

private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
      super(looper);
    }
    
    @Override
    public void handleMessage(Message msg) {
      // 此時handleMessage是運行在new_thread這個子線程中了.
    }
}
4.IntentService方式

Service是運行在主線程的, 然而IntentService是運行在子線程的.
實際上IntentService就是實現了一個HandlerThread + ServiceHandler的模式.

5.Loader方式

Android 3.0引入的數據加載器, 可以在Activity/Fragment中使用. 支持異步加載數據, 並可監控數據源在數據發生變化時傳遞新結果. 常用的有CursorLoader, 用來加載數據庫數據.

// 使用LoaderManager來初始化Loader
getLoaderManager().initLoader(0, null, this);

//如果 ID 指定的加載器已存在,則將重複使用上次創建的加載器。
//如果 ID 指定的加載器不存在,則 initLoader() 將觸發 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以實現代碼以實例化並返回新加載器

// 創建一個Loader
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // This is called when a new Loader needs to be created.  This
    // sample only has one Loader, so we don't care about the ID.
    // First, pick the base URI to use depending on whether we are
    // currently filtering.
    Uri baseUri;
    if (mCurFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(mCurFilter));
    } else {
        baseUri = Contacts.CONTENT_URI;
    }

    // Now create and return a CursorLoader that will take care of
    // creating a Cursor for the data being displayed.
    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
            + Contacts.DISPLAY_NAME + " != '' ))";
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}

// 加載完成
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in.  (The framework will take care of closing the
    // old cursor once we return.)
    mAdapter.swapCursor(data);
}

(四)性能分析工具

在這裏插入圖片描述

1、官方工具

Android本身給我們提供了很多App性能測試和分析工具, 而且大部分都集成到Android Studio或DDMS中, 非常方便使用.
1.1 StrictMode
(1)說明
顧名思義, “嚴格模式”, 主要用來限制應用做一些不符合性能規範的事情. 一般用來檢測主線程中的耗時操作和阻塞. 開啓StrictMode後, 如果線程中做一些諸如讀寫文件, 網絡訪問等操作, 將會在Log console輸出一些警告, 警告信息包含Stack Trace來顯示哪個地方出了問題.
(2)作用
主要用來做主線程優化分析
1.2 Systrace
(1)說明
Systrace是一個收集和檢測時間信息的工具, 它能顯示CPU和時間被消耗在哪兒了, 每個進程和線程都在其CPU時間片內做了什麼事兒. 而且會指示哪個地方出了問題, 以及給出Fix建議.
其以trace文件(html)的方式記錄. 可以直接用Chrome瀏覽器打開查看. 界面如下:
在這裏插入圖片描述
(2)作用
用來分析UI的繪製時間, 結合Hierarchy Viewer來提升UI性能.也可以用來發現耗時操作.
1.3 Hierarchy Viewer
(1)說明
Hierarchy Viewer提供了一個可視化的界面來觀測佈局的層級, 讓我們可以優化佈局層級, 刪除多餘的不必要的View層級, 提升佈局速度.
在這裏插入圖片描述
有必要說明下的是:
上圖紅框標出的三個點是關鍵分析數據. 左起依次代表View的Measure, Layout和Draw的性能. 另外顏色表示該View的該項時間指數, 分爲:

  • 綠色, 表示該View的此項性能比該View Tree中超過50%的View都要快.
  • 黃色, 表示該View的此項性能比該View Tree中超過50%的View都要慢.
  • 紅色, 表示該View的此項性能是View Tree中最慢的.
    (2)作用
    用來做View層級分析, 可以分析出View Tree中的性能阻塞點, 以便對症下藥, 提升佈局性能.
    1.4 TraceView
    (1)說明
    在這裏插入圖片描述
    一個圖形化的工具, 用來展示和分析方法的執行時間.
    (2)作用
    分析方法調用棧以及其執行時間, 優化方法執行.
    1.5 Memory Monitor
    (1)說明
    內存使用檢測器, 可以實時檢測當前Application的內存使用和釋放等信息, 並以圖形化界面展示.
    (2)作用
    在這裏插入圖片描述
    用來做內存分析, 內存泄露排查的不二之選. 可以結合heap viewer, allocation tracker來分析.可以導出hprof文件結合第三方的MAT工具分析泄露點.
    1.6 Other Monitor
    (1)說明
    Android Studio的Monitor還提供了其他三個Motinor — CPU, GPU, Network.
    (2)作用
    分別用來跟蹤監測CPU,GPU和Network的使用極其變化, 可以作爲網絡優化, 流量優化和渲染優化等的一個指導.
    1.7 其他
    Android的開發者模式中也提供了較多的用來監測性能的選項, 可以用下:
    在這裏插入圖片描述
    Developer options

2、第三方工具

2.1 Google的Battery Historian
(1)說明
Google出品, 通過Android系統的bugreport文件來做電量使用分析的工具.
(2)作用
用來做電量使用分析.
2.2 Emmagee(網易)
(1)說明
針對Android App的CPU, 內存, 網絡, 電量等多項綜合的測試分析.
(2)作用
比官方工具更適合國人使用來做App的整體性能分析.
2.3 leakcanary
(1)說明
Square出品, 必屬精品.類似與App探針的內存泄露監測工具.
(2)作用
集成到App中, 用來做內存問題預防最好不過了.

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