iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
0

嗨大家今天過得好嗎?在挑戰賽一開始就講 Paging 的機制好像有點跳太大,但為了在聽 Paging 講座的分享前先提前預習,我才會在前幾天就先安排 Paging 的主題,但如果有跟我一樣消化不良的狀況,可以之後再花一些時間把前面略過的實作補齊,聽完分享後我也會再把不錯的重點補充到文章中。

為什麼要了解 startup time?

回到這次挑戰賽的主題是「打造一個厲害的普通 Android App - 使用者優化體驗」,一個使用者體驗好的 Android App 應該要在開啟 App 的一開始就快速反應,但其實打開 App 的主畫面通常是功能最多、View 的構造最複雜的一個頁面,要繪製 View 也需要一定時間。還記得 Activity、Fragment 的 Lifecycle 是在何時將畫面顯示到螢幕上嗎?沒錯 Activity 和 Fragment 都是在進到 onStart 的階段後才會顯示到螢幕上,之後進到 onResume 使用者才可以操作 App。那如果在進到 onStart 之前做了很多耗時的操作 ( 不是指需要非同步處理的那種耗時操作,而是像繪製 View 這種當下就要完成但是會佔用 CPU 資源的操作 ),就會讓使用者等待的時間拉得更長。

所以有些 App 會加上 launch screen,在 App 顯示到螢幕前先出現畫面 ( 大部分是 logo ) 提示使用者 App 正在開啟中,但更根本的問題是為什麼 App 的啟動時間會慢?是不是用了太複雜的 View?或是過早初始化一開始還用不上的元件?這些都是影響 startup time 的重要原因,應該要去了解 App 在 startup time 到底做了哪些事情,如果都不去了解而只有加上 launch screen 的話也只是在掩耳盜鈴而已。

這篇文章主要參考自官方 App startup time 的文件,寫得非常詳細,這篇文章是我簡單截取重點和用白話再翻譯過的結果。

有溫度的 start

start 又分為 cold start、warm start 和 hot start 三種,cold start 的啟動方式是 App 會從無到有從頭開始建 Application 和繪製畫面,但 warm start 和 hot start 兩種則是把背景的 App 召喚到前景。不難理解 cold start 的 startup time 為什麼是最久的,因為從頭開始啟動要做最多事、花的時間就最長( 果然萬事起頭難?),但 cold start 的 startup time 經過優化的話 warm start 和 hot start 的時間也會跟著縮短喔!

啟動 cold start 的流程如下:

  • 載入和啟動 app
  • 顯示起始視窗 ( 設定 launch screen 的位置 )
  • 建立 App process:
    • 建立 App 物件
    • 啟動主線程
    • 建立 MainActivity
    • Inflating views
    • Laying out the screen
    • Performing the initial draw
    • 顯示 Layout,隱藏起始視窗

App 要到 initial draw 完成後才會顯示畫面和隱藏起始視窗,如果沒有設定 launch screen 的話使用者在 App 在顯示到螢幕前只能看著一片白牆真的真的沒有畫面,因此要注意 Activity 在 onCreate 時有沒有初始化一些還用不到的 value,有的話是否可以用 lateinit 的方式在後續要使用時才初始化就好,還有 constructor 是否有塞一些很大的物件,也會影響到 startup 的時間。

而 hot start 和 cold start 的流程差不多,只是 App 從後台被喚起到前台,由於記憶體還保留大部分 Activity 的物件或是 View 的狀態,因此就不用再次繪製畫面或是初始化物件,只有少數被記憶體移除的物件需要重建而已,hot start 的時間就相較 cold start 來得短。

那什麼是 warm start 呢?介於 cold start 從無到有和 hot start 萬事俱備只欠東風之間,warm start 就是需要經過重建但又有小開外掛,例如使用者退出 App 後又點選 launcher 重新開啟 App,對於 App 來說會經過再次重建的過程,但因為本來 Activity 的物件還存在記憶體所以啟動的時間又會縮短。

檢測方式

  1. Android vitals:在 Google Play console 上可以看到各個 start 是否超過標準的啟動數據。
  2. ActivityManager:查看 logcat 可以看到 ActivityManager 打印出來的 Activity StartupTiming;另外帶有 total 的時間是表示從開啟 App 到真的顯示在螢幕上的時間。
  3. reportFullyDrawn():當 loading 完初始畫面所需的 layout 後呼叫這個 method,主動告知系統 initial draw 已經完成可以顯示畫面囉,非一開始就需要的畫面就留到之後再處理,加快初始化的時間。
  4. Profiler:Android Studio 內建的工具可以條列出各個階段分別用掉多少時間。

疏通瓶頸

透過上述檢測方式確認 startup time 需要花的時間後,可以清楚地知道是哪些事件拖累了 App 的 startup time,不外乎就是繪製太過複雜的 View 階層或元件、或是在 Main thread 上初始化太多非一開始就要用到的物件,那如何解決呢?

  1. 減法原則:可以用 RelativeLayout 的不用 ConstraintLayout,可以用 LinearLayout 的不用 RelativeLayout,View 的階層越少層越好,View 的種類越單純越好。
  2. ViewStub:不是一開始就用到的 layout 可以用 ViewStub 代替,等到需要時再 render 就好。
  3. 初始化這種粗活不要通通擠在主線程,透過 lazy 的方式可以分到其他線程進行。
  4. 先顯示簡配版的畫面,標配版的可以在畫面出現後再慢慢補、慢慢更新就好。

萬法不離其宗

研究 App startup time 到頭來還是在講很基本的 lifecyle,其實開發的每個決定都是很神聖的!每次初始化物件都要思考最適合的初始化時機,在這個時機點初始化會不會有太早或太晚的問題?每個 View 的元件要用哪個?架構要幾層也需要經過謹慎的考慮,因為每個決定都會決定對使用者體驗是加分還是扣分!希望看完文章還有想到其他可以優化 startup time 的邦友也可以在下面留言討論,那明天會繼續介紹讓 App 看起來載入快速的黑魔法 - launch screen,讓我們繼續看下去。


上一篇
換頁 Paging (3):新同學 Paging3
下一篇
黑魔法 launch screen
系列文
打造一個厲害的普通 Android App - 使用者體驗優化16
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言