Android性能優(yōu)化整理總結

性能優(yōu)化的目的

在不斷地迭代開發(fā)過程中,我們的應用功能會越來越復雜,代碼量也會不斷增加。再加上偶爾的重構、人員的變更等等原因,我們曾經(jīng)那個如絲順滑的項目也會漸漸變得卡頓。
那么這個時候,就不得不提性能優(yōu)化這個話題了。正好這段時間有空,就整理了一下常見的性能優(yōu)化的幾個方面以及各個方面的注意事項。一來是給自己腦中的知識做個梳理,加深下記憶,二來也能給一些萌新提供點思路。

內(nèi)存優(yōu)化

內(nèi)存優(yōu)化,可以說是性能優(yōu)化中最重要的一部分內(nèi)容了。如果應用占用內(nèi)存過大,輕則應用卡頓降低用戶體驗,重則內(nèi)存溢出(OOM),程序崩潰。所以內(nèi)存優(yōu)化很重要,接下來我們從一下幾個方面來進行講解

android的內(nèi)存管理

  • 垃圾回收

    android的垃圾回收和java 的垃圾回收一直,一旦確定程序不再使用內(nèi)存,便將其釋放,而無需人為干預。

    垃圾回收有兩個步驟:在程序中產(chǎn)兆將來無法訪問的數(shù)據(jù)對象;將那些對象的資源釋放并回收。

  • 內(nèi)存管理

    android作為一個多任務的操作系統(tǒng),為了維持系統(tǒng)功能正常,會對每個應用程序的內(nèi)存大小做出硬性限制。具體的數(shù)值和機型以及內(nèi)存有關。如果應用內(nèi)存打到限制后還要申請內(nèi)存,就會引發(fā)OutOfMemoryError。

    如果在開發(fā)中有獲取剩余內(nèi)存的需求,可以調(diào)用getMemoryClass()方法。用來在內(nèi)存快滿時及時回收一些不必要的東西。

android內(nèi)存監(jiān)測

  • LeakCanary 詳情

    一個可以檢測程序在運行過程中發(fā)生的內(nèi)存泄漏問題,通過簡單的代碼配置,可以方便的找出我們應用中的內(nèi)存問題

  • Memory Monitor

    android studio自帶的實時內(nèi)存分析工具,我們可以通過實時的內(nèi)存、CPU等的波動來分析問題,如果某個頁面反復進入后內(nèi)存持續(xù)增長,我們就要注意了。

    [圖片上傳失敗...(image-211493-1570784432908)]

  • Heap Viewer 詳情

    也是android studio中可以直接使用的內(nèi)存分析工具,需要android系統(tǒng)在5.0以上并保持開發(fā)者選項可用。具體使用情況請點擊詳情。

  • Allocation Tracker 詳情

    可以追蹤內(nèi)存分配信息,按順序排列,這樣我們就可以清晰看出某一個操作的內(nèi)存是如何一步步分配出來的,從而進一步找出發(fā)生問題的代碼。

  • 更多性能測試工具看這里

內(nèi)存優(yōu)化方案

  • 界面不可見時及時回收部分內(nèi)存

    當用戶打開了另外一個程序,我們的程序界面已經(jīng)不再可見的時候,我們應當將所有和界面相關的資源進行釋放。在這種場景下釋放資源可以讓系統(tǒng)緩存后臺進程的能力顯著增加,因此也會讓用戶體驗變得更好。

    檢測界面是否可見我們可以重寫如下方法:

    @Override  
    public void onTrimMemory(int level) {  
        super.onTrimMemory(level);  
        switch (level) {  
        case TRIM_MEMORY_UI_HIDDEN:  
            // 進行資源釋放操作  
            break;  
        }  
    }
    

    onTrimMemory方法只有當一個Activity完全不可見時候才會調(diào)用,這和onStop()方法還是有很大區(qū)別的,因為onStop()方法只是當一個Activity完全不可見的時候就會調(diào)用,比如說用戶打開了我們程序中的另一個Activity

    因此,我們可以在onStop()方法中去釋放一些Activity相關的資源,比如說取消網(wǎng)絡連接或者注銷廣播接收器等,但是像UI相關的資源應該一直要等到onTrimMemory(TRIM_MEMORY_UI_HIDDEN)這個回調(diào)之后才去釋放,這樣可以保證如果用戶只是從我們程序的一個Activity回到了另外一個Activity,界面相關的資源都不需要重新加載,從而提升響應速度。

  • 使用Handler時盡量弱引用

    我們經(jīng)常會在handler中進行一些延時任務,這些延時任務會導致Activity被引用,從而發(fā)生內(nèi)存泄漏,為了避免這類事情發(fā)生,我們可以對handler使用弱引用。

    public class MainActivity extends AppCompatActivity {
    
        public static final String TAG = "carson:";
        private Handler showhandler;
    
        // 主線程創(chuàng)建時便自動創(chuàng)建Looper & 對應的MessageQueue
        // 之后執(zhí)行Loop()進入消息循環(huán)
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            //1. 實例化自定義的Handler類對象->>分析1
            //注:
                // a. 此處并無指定Looper,故自動綁定當前線程(主線程)的Looper、MessageQueue;
                // b. 定義時需傳入持有的Activity實例(弱引用)
            showhandler = new FHandler(this);
    
            // 2. 啟動子線程1
            new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // a. 定義要發(fā)送的消息
                    Message msg = Message.obtain();
                    msg.what = 1;// 消息標識
                    msg.obj = "AA";// 消息存放
                    // b. 傳入主線程的Handler & 向其MessageQueue發(fā)送消息
                    showhandler.sendMessage(msg);
                }
            }.start();
    
            // 3. 啟動子線程2
            new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // a. 定義要發(fā)送的消息
                    Message msg = Message.obtain();
                    msg.what = 2;// 消息標識
                    msg.obj = "BB";// 消息存放
                    // b. 傳入主線程的Handler & 向其MessageQueue發(fā)送消息
                    showhandler.sendMessage(msg);
                }
            }.start();
    
        }
    
        // 分析1:自定義Handler子類
        // 設置為:靜態(tài)內(nèi)部類
        private static class FHandler extends Handler{
    
            // 定義 弱引用實例
            private WeakReference<Activity> reference;
    
            // 在構造方法中傳入需持有的Activity實例
            public FHandler(Activity activity) {
                // 使用WeakReference弱引用持有Activity實例
                reference = new WeakReference<Activity>(activity); }
    
            // 通過復寫handlerMessage() 從而確定更新UI的操作
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        Log.d(TAG, "收到線程1的消息");
                        break;
                    case 2:
                        Log.d(TAG, " 收到線程2的消息");
                        break;
    
    
                }
            }
        }
    }
    

    這樣一來,handler中發(fā)送延時消息便不會發(fā)生內(nèi)存泄漏了。

    當然避免handler內(nèi)存泄漏還可以采取當Activity結束使用時候,清空消息隊列的操作,如下:

    @Override
    protected void onDestroy() {
      super.onDestroy();
      mHandler.removeCallbacksAndMessages(null);
      // 外部類Activity生命周期結束時,同時清空消息隊列 & 結束Handler生命周期
    }
    
  • 加載圖片的注意事項 詳情

    • 不在小控件上顯示大圖
    • 列表類圖片,僅加載當前頁面可見的圖片
    • 有顯示原圖需求時,要判斷可用內(nèi)存,內(nèi)存不夠時要壓縮圖片
  • 避免內(nèi)存抖動 詳情

    • 盡量避免在循環(huán)體內(nèi)創(chuàng)建對象,應該把對象創(chuàng)建移到循環(huán)體外。

    • 注意自定義View的onDraw()方法會被頻繁調(diào)用,所以在這里面不應該頻繁的創(chuàng)建對象。

    • 當需要大量使用Bitmap的時候,試著把它們緩存在數(shù)組中實現(xiàn)復用。

    • 對于能夠復用的對象,同理可以使用對象池將它們緩存起來。

布局優(yōu)化 詳情

避免過度繪制

  • 什么是過度繪制

    過度繪制就是在同一個位置,有多次的顏色繪制過程。常見的情況就是在同一個位置堆疊了許多控件,這會造成一些性能問題,嚴重的情況會造成卡頓。

  • 如何檢測過度繪制

    開發(fā)者選項->調(diào)試GPU過度繪制->顯示過度繪制區(qū)域

  • 過度繪制優(yōu)化

    • 移除控件中不需要的背景
    • 將layout層級扁平化
    • 減少透明度的使用
    • 自定義View中減少重復繪制區(qū)域

布局優(yōu)化技巧

  • 簡單布局優(yōu)先使用LinearLayoutFragmentLayout
  • 復雜布局優(yōu)先使用constrainLayout
  • 使用include標簽提高復用性
  • 使用ViewStub標簽延遲加載
  • onDraw()中不要創(chuàng)建新的局部變量以及不要做耗時操作

網(wǎng)絡優(yōu)化 詳情

為什么要網(wǎng)絡優(yōu)化

  • 過多的無用網(wǎng)絡請求,會消耗用戶的網(wǎng)絡流量。流量消耗過大會流失用戶
  • 頻繁的網(wǎng)絡操作會導致設備用電量提升
  • 網(wǎng)絡彈框的頻繁出現(xiàn)會降低用戶體驗
  • 應用更新、大文件下載等場景,更優(yōu)的網(wǎng)絡傳輸速度可提升用戶體驗

網(wǎng)絡優(yōu)化的方式

  • 使用GZip壓縮,數(shù)據(jù)壓縮后可以減少流量的消耗,減少傳輸?shù)臅r間
  • 使用IP直連,DNS域名解析是一個較為耗時操作,可以直連IP減少解析時間
  • 圖片加載
    • 使用WebP格式可以大幅節(jié)省流量
    • 圖片按需加載,列表中圖片只加載縮略圖
    • 大圖上傳時,采用分片傳輸,失敗只傳對應片段
    • 用戶體驗影響不大時,手機原圖壓縮后再傳輸
  • 減少接口數(shù)量,同一個頁面盡量只使用一個接口,數(shù)據(jù)可以放到后臺去拼湊
  • 利用緩存,對數(shù)據(jù)設定有效期,有效期內(nèi)數(shù)據(jù)不重復請求
  • 檢測網(wǎng)絡狀態(tài),不同網(wǎng)絡轉(zhuǎn)態(tài)執(zhí)行不同策略,例如移動網(wǎng)絡不加載圖片,2G網(wǎng)絡只加載標題等。
  • 文件上傳、下載采用斷點續(xù)傳,不浪費已傳輸完成部分流量
  • 利用抓包工具模擬多種情況,在實踐中調(diào)整不斷優(yōu)化用戶體驗

啟動優(yōu)化 詳情

閃屏頁優(yōu)化

主流APP是在應用啟動時候會加載一個默認的主題,用來去掉應用啟動時候的黑/白屏的情況

<style name="AppThemeWelcome" parent="Theme.AppCompat.NoActionBar">
    ...
    <item name="android:windowBackground">@drawable/logo</item>  <!-- 默認背景-->
</style>

應用主題到ApplicationActivity

<activity android:name=".ui.activity.DemoSplashActivity"
  android:configChanges="orientation|screenSize|keyboardHidden"
  android:theme="@style/AppThemeWelcome"
  android:screenOrientation="portrait">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

其實就是個障眼法而已,提前讓你看到了假的界面。也算是一種不錯的方法,但是治標不治本。

第三方庫懶加載

在開發(fā)中會用到很多的三方庫,如友盟、百度、bugly、圖片庫、網(wǎng)絡庫等。

這些都是必須的,不能去掉,那么辦法就是異步加載了,可以有以下幾種思路

  • 像友盟,bugly這樣的業(yè)務非必要的可以的異步加載。

  • 比如地圖,推送等,非第一時間需要的可以在主線程做延時啟動。當程序已經(jīng)啟動起來之后,在進行初始化。

  • 對于圖片,網(wǎng)絡請求框架必須在主線程里初始化了。

  • 我們一般會有閃屏頁面,也可以把延時啟動的地圖、推送的啟動放到這個頁面

按照以上方式處理后,還可以進一步降低應用啟動時間。

WebView啟動優(yōu)化

  • WebView第一次創(chuàng)建比較耗時,可以預先創(chuàng)建WebView,提前將其內(nèi)核初始化。
  • 使用WebView緩存池,用到WebView的地方都從緩存池取,緩存池中沒有緩存再創(chuàng)建,注意內(nèi)存泄漏問題。
  • 本地預置html和css,WebView創(chuàng)建的時候先預加載本地html,之后通過js腳本填充內(nèi)容部分。

數(shù)據(jù)項預加載

主頁數(shù)據(jù)變化不大時候,可以再第一次啟動后,緩存主頁數(shù)據(jù)到本地,下次啟動先讀取本地數(shù)據(jù),頁面完全顯示后再去請求新數(shù)據(jù)進行增量更新。

安裝包體積優(yōu)化

體積優(yōu)化的必要性

安裝包體積是用戶搜索應用后能第一眼看到的數(shù)據(jù),雖然現(xiàn)在的應用體積越來越大,但小體積的App依舊是很多存儲空間緊張用戶的痛點。所以減少安裝包體積是性能優(yōu)化方面必不可少的一步。

減少應用體積的N種辦法

  • 使用lint工具刪除無用的資源

  • 簡單的切圖盡量替換為shape類型的xml文件

  • 形狀一致的圖片只使用一個切圖,比如方向不同的箭頭、圖像相同著色不同的切圖等

  • 對圖片進行壓縮,優(yōu)先使用WebP格式圖像

  • 使用矢量圖(.9)圖來實現(xiàn)大小可變的背景圖

  • 代碼混淆,使用proGuard代碼混淆器工具,它包括壓縮、優(yōu)化、混淆等功能。

  • 插件化,不需要的部分可以存在服務器,當用到時候動態(tài)下載。

電量優(yōu)化

電量優(yōu)化我放到最后說,是因為這個優(yōu)先級比較低,因為一般APP在使用過程中,很難造成電量的明顯下降,除非是游戲、相機或者視頻類APP

電量優(yōu)化相對來說比較簡單,在開發(fā)中注意一下幾點就可以了:

  • 數(shù)據(jù)備份、日志報告等后臺活動,可以放到電量充足或者正在充電時候執(zhí)行
  • 除視屏播放外,一直避免一直亮屏。
  • 錄音、GPS、相機等耗電操作,在執(zhí)行完成后及時釋放對應資源
  • 后臺不必要的service記得及時關閉

總結

Android的性能優(yōu)化是一個長期且漫長的過程。一般企業(yè)在開發(fā)中都是先實現(xiàn)功能再去管性能,這樣做會導致后期優(yōu)化起來麻煩且耗時。建議有可能的話盡量保持一個好的開發(fā)習慣,在項目初期就注意性能方面的事情,不要引入無用的內(nèi)容、保持代碼整潔、及時刪除已廢棄模塊等,這樣開發(fā)的項目才回高效且易維護。


這篇文章也是根據(jù)我開發(fā)的經(jīng)驗以及網(wǎng)絡中的好多精品文章整理而來,其中一些精彩的深入分析文章大家可以點擊詳情去查看。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容