移動應用效能優化

效能優化實踐者發表於2021-11-24

前言

距離我的GoodWeather開發已經過去一年多的時間了,這個App我是完全開源,並且把開發的步驟都公佈了出來,在開發過程中我遇到過很多問題,剛好藉著這個機會來說一下。

正文

GoodWeather App是一個天氣的App,可以實時定位你當前的所在位置,查詢到當地的天氣,並且提供地圖定位模式和語音輸入快速查詢的方式,專案的原始碼地址如下:GoodWeather,不方便下載原始碼的可以直接通過二維碼下載安裝體驗一下:

這個專案我利用工作之餘休息的時間陸陸續續的寫完了,寫的過程中不斷的在完善和修改,也會聽取讀者的建議增加相應的功能,這個過程中也遇到了一些問題,例如:ANR(程式無響應)、NullPointerException(空指標異常)、RunningTimeException(執行時異常)、ArrayIndexOutOfBoundsException(陣列索引越界異常)、IllegalArgumentException(非法引數)、NumberFormatException(數字格式異常)等等。

一、問題

就上述的這些異常你在日常開發中肯定會遇到的,大體說一下。

① ANR

ANR(全稱:Application Not Responding)程式無響應,要解決問題首先要知道問題出現有哪些可能性,然後在結合你當前應用的實際情況去排查,最終找到解決方法。這是我的思路,那麼造成ANR有哪些可能呢?

可能性一:主執行緒阻塞,在主執行緒中進行耗時操作過多(資料庫讀寫、檔案讀寫、網路請求、大資料計算等)。 可能性二:記憶體洩漏,列如App的啟動頁是一個高清的圖片,在有的手機上可以正常執行,有的手機機會閃退。 可能性三:過度繪製,這個說法就是上面兩種可能的相結合,首先你在主執行緒中繪製UI,其次繪製的圖比較大或者重複繪製都會有ANR可能。

實際中的可能性可不止這三點,說說怎麼解決的,第一個可能性,主執行緒阻塞,由於執行了大量的耗時操作造成的,那麼這個時候就要使用子執行緒去進行耗時操作的處理。例如網路請求、資料庫讀寫、檔案讀寫都應該開一個子執行緒去執行,而不應該在主執行緒中處理,對於Activity來說UI執行緒就是主執行緒,UI執行緒要負責頁面UI的繪製,這裡又會引發另一個問題,那就是當你的view由主執行緒繪製時,在子執行緒中進行改變時會報錯,所以子執行緒可不可以重新整理view呢?是可以的,只不過有一個前提,那就是你的子執行緒建立了這個view,此時這個子執行緒就是這個view的UI執行緒。

而對於記憶體洩漏來說,常規情況下是資源處理不當造成的。舉個通俗的例子,有兩座橋,A和B,A最大承重2噸,B最大承重6噸,有100輛車要過橋,車上載重1噸到6噸不等,正常的分配就是小重量走小橋,大重量走大橋,當資源分配不合理就會出現1噸的車走承重6噸的橋,然後6噸的車走承重2噸的橋,那怕橋質量再好終究會塌,可能例子不是很恰當,但是意思應該說的很清楚了,就是資源合理使用,不用就要回收,回到一個我之前遇到的問題,就是高清圖片,國產手機的定製化是很多的,各個廠商都更改了Google的原始碼變成了定製化系統,這對使用者來說沒啥,但是對開發者來說就很難受了,因為一個問題你是躲不開的,那就是適配。

我之前遇到的問題就是我在啟動頁用了一個高清圖,然後在我的手機上正常執行,然後在一個讀者的手機上就直接閃退了。報錯的圖如下:

從這個圖能看出什麼內容呢?
首先這是一個執行時異常,其次和圖片的繪製有關係,那麼這麼一結合就是過度繪製的問題。
當時這個讀者就找到我,然後我就開始排查,首先是啟動App的時候做了什麼,這裡還會涉及到一個點,那就是App的啟動優化,這個點很關鍵。不過首先要解決bug,經過排查最終定位到是圖片的問題,這裡就說明了國內廠商對於手機螢幕及自身系統的定製是千差萬別的,最終的解決辦法就是針對於高清圖的檔案修改到大解析度資料夾下,這屬於一個比較低階的錯誤,我之前放置的都是常規的資料夾,吃一塹長一智。

② NullPointerException

NullPointerException(空指標異常),我相信用Java寫Android的朋友肯定遇到過找個問題,那就是null,常說的空物件。這個問題一般來說在開發的時候做得好可以避免90%的出現概率。最大的出現情況就是賦值的時候,只要出現這個,那麼對應的就是你的程式閃退了,哦豁!這個月獎金又沒了,打工人的辛酸,留下了悔恨的淚水。所以使用Java開發Android的時候要特別注意這一點,注意null。這一點Kotlin就做的很好,因為空安全這個特性。那麼假如出現問題了,線上的專案,使用者就說會閃退,甚至都不說是什麼時候閃退的,你要怎麼辦呢?怎麼去解決呢?通過第三方SDK,例如友盟,你對接SDK之後,釋出之後,報錯時,平臺上會有報錯的資訊與日誌,這樣開發者就可以快速定位問題,然後解決問題了,然後對App做一個更新,這就完美化解了,雖然扣的錢回不來了,但是你及時止損了。至於其他的一些異常都是常規的,發現了就能解決,在開發過程中。最麻煩的就是上線之後的問題要怎麼去定位和解決。

其實在實際的App開發中,大部分的問題都能在開發和測試階段發現並且解決,但是巧的就是在上線之後到了使用者手裡,就出現了之前沒有發現到的問題,這個時候開發和測試就要互相甩鍋了,扯皮是常事。扯完之後還是要想辦法解決才行,因此對於現在的線上專案來說,上線之前對接一款效能檢測,錯誤收集的SDK是很有必要的,下面我將針對於我的這個GoodWeather進行這個SDK的對接與使用。

二、友盟使用

點選友盟進入官網,然後註冊和登入。

1、建立平臺應用

登入後點選 進入工作臺,這裡可以檢視應用資訊,如果還沒有建立過應用就新增新應用。

在友盟上建立應用,獲取AppKey

註冊應用。此時AppKey已經生成了,然後選擇需要開通的產品,這裡選擇應用效能監控U-APM。

確認開通。

複製這個App Key到自己的專案中,一會兒會用到。開啟專案的build.gradle,新增maven庫。兩處地方,同一句程式碼:
groovy

maven { url 'https://repo1.maven.org/maven2/' }


然後開啟app模組的build.gradle,在dependencies中新增如下程式碼:
groovy
// 友盟基礎元件庫(所有友盟業務SDK都依賴基礎元件庫)

implementation "com.umeng.umsdk:common:9.4.2" //(必選)
implementation "com.umeng.umsdk:asms:1.4.1" // asms包依賴(必選)
implementation "com.umeng.umsdk:apm:1.4.2" // U-APM包依賴(必選)

然後Sync Now,同步一下專案。
由於友盟的SDK需要獲取手機的裝置資訊和網路狀態,因此需要在AndroidManifest.xml中配置相應的許可權
xml

<uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permissionandroid:name="android.permission.READ_PHONE_STATE"/>
<uses-permissionandroid:name="android.permission.INTERNET"/>

其中READ_PHONE_STATE需要動態申請。

然後就是初始化了,這裡要注意一點就是需要在在《隱私政策》中向使用者告知使用友盟SDK,參考條款如下: 使用SDK名稱:友盟SDK 服務型別:請按SDK功能填寫,如應用效能監控平臺 U-APM 收集個人資訊型別:裝置資訊(IMEI/MAC/Android ID/IDFA/OpenUDID/GUID/SIM 卡 IMSI 地理位置資訊等) 隱私權政策連結:https://www.umeng.com/page/po... 這個隱私協議的彈窗理應做成類似這樣的效果。假設這是你之前的隱私政策,

那麼你需要在這個裡面加上關於友盟+SDK的使用說明。然後使用者同意後才能進行這個初始化的操作,在Application的onCreate中進行。 這裡有一個預初始化和一個正式初始化,預初始化在程式第一次安裝執行時使用。在使用者同意了隱私政策之後呼叫正式的初始化,比如下面這個圖這樣

好了,現在基本上就完成了對接的工作,下面就來使用它。

二、使用

① 日誌使用
當對接了友盟SDK之後,就會列印友盟的相關日誌,而上線的時候就不用再列印了。

可以通過下面這行程式碼設定關閉。
java
UMConfigure.setLogEnabled(boolean)

② 崩潰分析
完成了SDK對接之後,即可使用Java、Native崩潰分析、ANR分析功能,無需額外其他接入操作。如果需要對Native進行採集可以這樣寫:
java

      //針對於Native崩潰資訊採集
        final Bundle customInfo = new Bundle();
        customInfo.putBoolean("mCallNativeDefaultHandler",true);
        CrashApi.getInstance().updateCustomInfo(customInfo);

崩潰回撥(自定義欄位)
java

//崩潰回撥
        UMCrash.registerUMCrashCallback(new UMCrashCallback(){
            @Override
            public String onCallback(){
                return "App程式崩潰了";
            }
        });

自定義異常介面
在開發中通常有自己異常try catch,那麼也可以將catch中的異常通過友盟的介面傳到後臺去,然後開發者去檢視。
java

try {
            // 丟擲異常的程式碼
        } catch (Exception e){
            UMCrash.generateCustomLog(e,"UmengException");
        }

使用

③ 自定義版本號
有時候線上可能有多個版本那麼,可以通過自定義版本號API,使SDK所上報資料繫結您指定的App版本資訊。
java

UMCrash.setAppVersion("1.0.0", "release", "0001");

使用

有了這些,你就可以等著程式報錯的日誌上傳到友盟上了,首先找到友盟上檢視日誌資訊的地方。這個地方確實不太好找

點選穩定性。

立即使用。

下面就可以進行相關的日誌檢視了,那麼先來寫一個bug吧。

比如我在App的啟動頁的onCreate中寫下這樣的程式碼。
java

 try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

然後執行,看AS的控制檯日誌

這個日誌就很全面了,不過這是友盟SDK的日誌列印,本地除錯確實可以使用,那麼它有沒有傳到平臺上呢?畢竟線上專案的錯誤資訊日誌才是關鍵。

很明顯,上傳了這個錯誤,再向下滑動去檢視這個錯誤的詳情。


點選左邊的藍色報錯處

這裡告訴你報錯的具體程式碼行,以及下方這裡有你報錯裝置的資訊,方便你去排查原因,再往下看。

這裡告訴你報錯的原因是什麼?主執行緒睡眠時間過長,導致的執行緒阻塞,程式無響應,ANR。
這裡右邊的行為日誌,也很不錯,可以讓你知道你在當前這個頁面是在做什麼。

這裡對應的是頁面瀏覽,合理,因為我確實還沒有做什麼。

而這個記憶體快照,就是方便你檢視報錯時的記憶體使用情況,可以酌情進行優化。

最後這個自定義欄位,很明顯就是在程式碼中寫的一個崩潰的回撥。

這說明我自定義欄位的日誌也上傳了。

通過介紹、接入、使用和日誌分析瞭解到友盟的強大,這對於線上專案來說是一個福音,提交的錯誤資訊能夠快速上傳,定位到,快速解決問題。

更多的功能使用需要開發者自行去摸索,這裡只是起到一個拋磚引玉的效果,山高水長,後會有期~

作者:李龍威

相關文章