<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          APP冷啟動優(yōu)化:如何使用好工具

          共 6949字,需瀏覽 14分鐘

           ·

          2021-11-19 13:48

          和你一起終身學習,這里是程序員Android

          經(jīng)典好文推薦,通過閱讀本文,您將收獲以下知識點:

          一、冷啟動的定義與可優(yōu)化的點
          二、如何定位當前性能問題
          三、perfetto/systrace:大局與調度
          四、對于整體冷啟動優(yōu)化效果:用perfetto看比較直接

          APP的性能提升無非就是圍繞穩(wěn)定、流暢之類的指標做文章,在推動性能提升的時候,什么才是關鍵,熱情?能力 ?規(guī)范?,個人認為是工具,用好性能分析工具,性能提升就走完了一大半,就好比:”算數(shù)我比不過小王,但我找了個電子計算器“。以提升冷啟動速度為例,看看整體的性能優(yōu)化流程應該是什么樣子,而在這其中性能工具能帶來什么。

          一、冷啟動的定義與可優(yōu)化的點

          如何衡量當前的性能指標,個人感覺,性能的衡量分三步:?指標制-> 指標采集 -> 性能基線與優(yōu)劣評級, 以上三塊組成性能量化工具,有了量化工具,就可以說APP性能是好是壞,以冷啟動為例,冷啟動指標如何制定?單從技術上說感覺可以定義如下:

          冷啟動耗時 = 從APP進程創(chuàng)建到第一個有效頁面幀[閃屏]

          具體到實現(xiàn)上,涉及哪些環(huán)節(jié),會怎樣影響冷啟動速度呢?

          冷啟動->系統(tǒng)會啟動一個StartWindow占位-> 啟動進程->創(chuàng)建Application-?>Application中初始化全局配置->啟動第一個Activity->Create->Start->Resume->AddWindow->UI測量繪制[performTraversals]->首幀可見

          image

          冷啟動的時候,系統(tǒng)一般會先啟動一個占位Window,默認是個白屏窗口,復用的是第一個啟動Activity配置,在體感上,主要下面的Activity配置

              <item name="android:windowBackground">@drawable/xxxitem>

          它一般是SplashActivity的配置,用品牌圖做個中轉,這個圖最好要限制下尺寸,否則在解析上影響啟動速度。隨后系統(tǒng)會啟動進程加載SplashActivity,啟動進程主要是Application中可能有些APP全局初始化操作,盡量輕,或者延后處理,當然,也會有一些ContentProvider與Receiver影響啟動這些都可以通過工具查看。

          public class LabApplication extends Application {
          @Override
          public void onCreate() {
          super.onCreate()
          <!--UI中不要處理耗時操作-->
          }

          之后便是Activity的創(chuàng)建與啟動流程 :

          image

          可以看到上圖的Activity啟動流程都是被動消息的處理,主要是受控AMS指揮,代碼中設置View及顯示的流程也就上圖的幾個點,比如onCreate中設置Layout并inflater,當然,這不是必須的,即使不主動setContentView,在后面的wm.addView中也會創(chuàng)建頂層DecorView。

          @Override
          public void setContentView(int resId) {
          ensureSubDecor();
          ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
          contentParent.removeAllViews();
          <!--可能影響耗時-->
          LayoutInflater.from(mContext).inflate(resId, contentParent);
          mAppCompatWindowCallback.getWrapped().onContentChanged();
          }

          而setContentView的inflate可能是影響耗時的一個點。之后handleResumeActivity中,會想WMS添加窗口View

          @Override
          public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
          String reason) {
          ...
          final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
          ...
          r.window = r.activity.getWindow();
          View decor = r.window.getDecorView();
          if (!a.mWindowAdded) {
          a.mWindowAdded = true;
          <!--添加Window-->
          wm.addView(decor, l);
          } else {

          @Override
          public final View getDecorView() {
          if (mDecor == null || mForceDecorInstall) {
          installDecor();
          }
          return mDecor;
          }

          可以看到 getDecorView會兜底處理Activity 的頂層窗口創(chuàng)建邏輯。addView會調用WindowManagerGlobal的addView,進而創(chuàng)建ViewRootImpl,利用ViewRootImpl進一步添加Window

             public void addView(View view, ViewGroup.LayoutParams params,
          Display display, Window parentWindow) {

          root = new ViewRootImpl(view.getContext(), display);

          view.setLayoutParams(wparams);


          root.setView(view, wparams, panelParentView);

          }
          }

          而ViewRootImpl接管流程之后,所以View相關的操作都將在ViewRootImpl處理,而最終由其requestLayout觸發(fā)測量、布局、繪制的動作

          public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
          synchronized (this) {
          if (mView == null) {


          requestLayout();


          mOrigWindowType = mWindowAttributes.type;
          mAttachInfo.mRecomputeGlobalAttributes = true;
          collectViewAttributes();
          res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
          getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
          mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
          mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);

          }
          }
          }

          requestLayout會scheduleTraversals預先占位一個異步消息,用于接收并doScheduleCallback觸發(fā)的VSYNC信號,這樣可以保證之后插入的消息都被延期處理,從而Window被添加后,UI繪制任務第一時間執(zhí)行。

          void scheduleTraversals() {
          if (!mTraversalScheduled) {
          mTraversalScheduled = true;
          <!--異步消息-->
          mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
          <!--插入繪制請求,觸發(fā)VSYNC-->
          mChoreographer.postCallback(
          Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

          }

          等待VSYNC消息回來后,撤離異步消息柵欄,第一時間處理UI繪制:

          image

          可以看到,這個邏輯基本上是挨著addView執(zhí)行。所以可以認為是Resume之后第一個比較靠前的系統(tǒng)調度異步消息,那么只要在onResume之后插入插入一條消息,其實就可以監(jiān)控到首幀渲染,如下圖所示。

          image

          網(wǎng)上也有一些其他的實現(xiàn),比如onAttachedToWindow或者OnWindowFocusChange后發(fā)消息,或者直接監(jiān)聽,原理差不多。

          image

          綜上所述,可以認為在Resume之后插入一個消息即可。有了指標,那是否達標?如何采集?基線呢?可以參考業(yè)界做法,采集方式可以無入侵打點,而優(yōu)秀基線可以認為:

          優(yōu)秀=秒開

          如果發(fā)現(xiàn)不達標,接下來要做的就是定位+優(yōu)化,這個時候就體現(xiàn)分析工具的重要性,其實上述的原理分析就已經(jīng)借助Studio自帶的Profiler工具,在理解流程上事半功倍。

          二、如何定位當前性能問題

          冷啟動每個階段的耗時可以通過多種工具、方式來定位:可以用的有Debug.startMethodTracing跟蹤,也可以利用perfetto/systrace來查看,甚至還可以用Studio自身的Profiler跟蹤,每種方式都有自己的優(yōu)勢,可配合選擇使用。

          Debug.startMethodTracing 適合查看UI線程的耗時函數(shù)

          Debug.startMethodTracing是通過應用插樁來生成跟蹤日志,做到對方法的跟蹤。但是啟用剖析功能后,應用的運行速度會減慢,所以,不應使用剖析數(shù)據(jù)確定絕對時間,最大的作用是用在對比上,可以對比之前,或者對比周圍函數(shù)。具體用法:

          private void startTrace() {
          File file = new File(getApplication().getExternalFilesDir("android"), "methods.trace");
          Debug.startMethodTracing(file.getAbsolutePath(), 100 * 1024 * 1024);
          }
          <!-注意配對使用-->
          private void stopTrace() {
          Debug.stopMethodTracing();
          }

          對于冷啟動:進程啟動時開啟監(jiān)聽,在合適節(jié)點配對停止即可,之后導出.trace文件在Studio中分析,可以看到關鍵函數(shù)耗,Studio提供了多種模式,F(xiàn)lame Chart、Top Down、Bottom Up、Event,不同的模式側重點不同。定向分析的時候,可以分段鎖定范圍,比如冷啟動可分幾個階段排查,進程創(chuàng)建、Application初始化、Activity的創(chuàng)建、create、resume、draw等,先選定Main線程,然后將范圍限制定Application階段,如下下:

          • Flame Chart:更側重直觀反映函數(shù)耗時嚴重程度

          image

          比如上圖,淺黃色部分其實就是需要重點關注的部分,耗時最多的函數(shù),會最先展示,更加方便定位嚴重問題,大致定位問題后,就可以用Top Down 進一步看細節(jié)。

          • Top Down:更側重自頂向下詳細排查

          利用Top-Down模式可以更精確觀察函數(shù)耗時與調用堆棧,更加清晰,如下在Application初始化階段,可以清醒看到函數(shù)調用順序、耗時等,

          image

          對于冷啟動,重點排查耗時函數(shù),嘗試將非核心邏輯從UI線程中移除。同理對于閃屏Activity的onCreate跟onResume階段所做的處理類似

          image

          從圖中就很容下發(fā)現(xiàn),有些Flutterboost、埋點Json解析類的耗時操作被不小心關聯(lián)進了Activit的啟動流程中,拖慢了冷啟動速度,那就可以放到非UI線程中處理,或者延后處理。

          • Bottom Up:一種平鋪的模式,

          image

          這個模式個人用的不多,羅列的函數(shù)太多,沒有層次,可能單獨看排名靠前的幾個有些收益。

          依賴profiler基本能定位哪些函數(shù)導致了冷啟動速度慢,但是這些函數(shù)可能并非自己耗時嚴重,也許是會因為調度或者鎖的原因導致慢,這個時候perfetto/systrace會提供更多幫助。

          三、perfetto/systrace:大局與調度

          perfetto地址及使用文檔

          perfetto/systrace是官方提供另一種性能分析工具,其中perfetto可以看做是systrace的升級版。相比MethodTracing代碼插樁,無法具體到每個方法,但可以提供全局性能概覽,可以更快定位問題范圍,而且perfetto/systrace在全局任務調度、系統(tǒng)調用上更具優(yōu)勢,MethodTracing多少對于性能有些影響,而perfetto/systrace借助系統(tǒng)本身lOG,可以降低自身帶來的影響,用perfetto看一下冷啟動的流程,如下:

          image

          如圖,首先你就能直觀的看到那些階段的耗時比較嚴重,然后定向分析即可,將時間段收縮,放大觀察:

          image

          可以直觀看出Activity啟動時藍色標記的資源解析耗時過長,定向排查后發(fā)現(xiàn)圖大

          Name    res/BKC.xml
          Category null
          Start time 1s 309ms 568us 459ns
          Duration 42ms 35us 682ns
          Slice ID 2465

          適當將圖縮小,降低加載成本,經(jīng)過優(yōu)化縮短到8ms

          Name    res/BKC.xml
          Category null
          Start time 964ms 602us 749ns
          Duration 8ms 260us 261ns
          Slice ID 11851
          type internal_slice

          再比如,對于有些階段,UI線程莫名的睡眠,其實可以比較方便的查看是什么因素導致的,如下:

          image

          如上所示,在當前階段,UI線程因為沒有獲取鎖進入了睡眠,之后,被另一個線程喚起了,這就可以排查到底是哪個地方有問題,是否可以避免,大概的使用方式就是如此。

          四、對于整體冷啟動優(yōu)化效果:用perfetto看比較直接

          優(yōu)化前:1261ms

          image

          優(yōu)化后:439ms

          image

          所用的優(yōu)化除了上面的措施還有部分如下措施等:

          • 延遲非必要receiver的注冊

          • 閃屏廣告Layout布局按需加載

          • 鎖優(yōu)化,進程線程間阻塞優(yōu)化

          所用的優(yōu)化除了上面的措施還有部分如下措施等:核心原則 UI線程不做耗時操作

          • 延遲非必要receiver的注冊

          • 閃屏廣告Layout布局按需加載

          • 鎖優(yōu)化,進程線程間阻塞優(yōu)化

          總結

          BUG是必然的,優(yōu)化是持久的,如何用好工具是關鍵的。

          作者:看書的小蝸牛
          鏈接:https://www.jianshu.com/p/867b7c45828c

          友情推薦:

          Android 開發(fā)干貨集錦

          至此,本篇已結束。轉載網(wǎng)絡的文章,小編覺得很優(yōu)秀,歡迎點擊閱讀原文,支持原創(chuàng)作者,如有侵權,懇請聯(lián)系小編刪除,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!

          點擊閱讀原文,為大佬點贊!

          瀏覽 75
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产真实野外在线 | 国内毛片毛片毛片毛片毛片毛片 | 强开小嫩苞一区二区三区视频 | 人妻日日夜夜 | 免费在线观看小视频黄 |