日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java线程太多卡顿_性能优化之卡顿延迟

發布時間:2023/12/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java线程太多卡顿_性能优化之卡顿延迟 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

本篇文章主要介紹 Android 開發中的部分知識點,通過閱讀本篇文章,您將收獲以下內容:

1.UI 渲染簡介

2.識別延遲

3.Visual inspection

4.Systrace

5.Custom performance monitoring

6.fix 延遲問題

7.常見延遲問題的來源

8.渲染性能問題

9.線程調度延遲

10.分配對象和垃圾回收機制

1.UI 渲染簡介

UI渲染是從應用程序生成并將其顯示在屏幕上的動作。為確保用戶與應用程序的交互順暢,您的應用程序應在16毫秒內完成渲染 ,從而達到每秒60幀(為什么要60fps?的效果要求。如果您的應用程序的UI呈現速度較慢,則系統將被迫跳過幀,這樣用戶會感覺到您的應用程序出現了卡頓現象,我們稱這個延遲。

為了幫助您提高應用程序質量,Android會自動監視您的應用程序是否存在卡頓渲染慢的問題,并在Android vitals儀表板中顯示相關信息。有關如何收集數據的信息,請參閱Play控制臺文檔。

如果您的應用程序出現卡頓渲染問題,此頁面將提供有關診斷和解決問題的指南。

Android vitals 儀表板和 Android 系統會跟蹤使用 UI Toolkit 的應用程序的渲染時間統計信息(從中Canvas 或 View 層次結構中繪制應用程序的用戶可見部分)。如果您的應用程序不使用UI工具包(使用Vulkan,Unity, Unreal或 OpenGL構建的應用程序就是這種情況) ,則Android Vitals儀表板中不提供渲染時間統計信息。您可以通過運行確定設備是否正在記錄應用的呈現時間指標:

adb shell dumpsys gfxinfo

2. 識別延遲

在應用程序中查明導致延遲的代碼可能很困難。本節介紹三種識別延遲的方法:

Visual inspection 使您可以在幾分鐘內快速遍歷應用程序中的所有用例,但是它提供的信息不如Systrace那么多。

Systrace提供了更多詳細信息,但是如果您為應用程序中的所有用例運行了Systrace,則會被大量數據淹沒,以至于難以分析。

Visual inspection 和systrace都可以檢測本地設備上的延遲問題。

Custom performance monitoring自定義性能監控,如果您的延遲問題無法在本地設備上復制,則可以使用自定義性能監控來衡量在現場運行的設備上應用程序的特定部分。

3.Visual inspection

視覺檢查可幫助您識別產生延遲的部分。要執行外觀檢查,請打開您的應用程序,然后手動瀏覽應用程序的不同部分,并注意用戶界面是否卡頓。以下是進行外觀檢查時的一些技巧:

運行您的應用。

為了支持調試功能,ART運行時會禁用一些重要的優化,因此請確保您正在查看的內容與用戶看到的內容相似。

Profile GPU渲染在屏幕上顯示條形,可以快速直觀地表示渲染UI窗口的幀相對于每幀16毫秒基準所花費的時間。每個條都有彩色的組件,這些組件映射到渲染管線中的某個階段,因此您可以查看哪個部分占用的時間最長。例如,如果框架花費大量時間處理輸入,則應查看處理用戶輸入的應用程序代碼。

注意某些組件

例如RecyclerView 這個是常見的卡頓延遲問題的來源。如果您的應用程序使用了這些組件,那么最好遍歷應用程序的這些部分。

有時,冷啟動該應用程序后,也可以復現延遲問題 。

嘗試在速度較慢的設備上運行您的應用,以加劇該問題。

一旦找到產生卡頓延遲代碼的地方,您可能對導致應用程序卡頓延遲原因有了一個很好的了解。但是,如果您需要更多信息,則可以使用Systrace進一步深入研究。

4.Systrace

Systrace是顯示整個設備運行狀況的工具,它對識別應用程序中的卡頓延遲代碼很有用。Systrace系統開銷很小,因此你可以通過Systrace 儀器檢測實時延遲的問題。

在設備上執行復雜的用例時,使用Systrace記錄跟蹤。有關如何使用Systrace的說明,請參閱Systrace演練。systrace被進程和線程分解。在Systrace中查找應用程序的過程,該過程應類似于圖1。

systrace

圖1中的systrace包含以下信息,用于標識垃圾郵件:

Systrace顯示何時繪制每幀,并對每幀進行顏色編碼以突出顯示較慢的渲染時間。與視覺檢查相比,這可以幫助您更準確地找到單個渲染卡頓幀。有關更多信息,請參見 Framework 框架。

Systrace可以檢測您的應用程序中的問題,并在單獨的框架和 警報面板中顯示警報。最好按照警報中的指示進行操作。

Android框架和庫的某些部分(例如 RecyclerView )包含Systrace跟蹤標記。因此, systrace時間線顯示了這些方法何時在UI線程上執行以及它們執行多長時間。

在查看systrace輸出之后,您的應用程序中可能存在一些可能導致卡頓延遲的方法。例如,如果時間軸顯示幀 RecyclerView 時間過長導致幀緩慢,則可以將Trace標記添加到相關代碼中,然后重新運行systrace以獲取更多信息。在新的systrace中,時間線將顯示您的應用程序的方法何時被調用以及它們執行的時間。

如果systrace并未向您顯示有關UI線程為什么需要花費很長時間的詳細信息,那么您將需要使用Android CPU Profiler來記錄采樣或檢測方法跟蹤。通常,方法跟蹤不利于識別延遲代碼,因為由于開銷大,它們會產生假性延遲,并且它們看不到線程何時運行與阻塞。但是,方法跟蹤可以幫助您確定應用程序中花費時間最多的方法。識別完這些方法后,添加跟蹤標記并重新運行systrace以查看這些方法是否造成了麻煩。

記錄systrace時,每個跟蹤標記(執行的開始和結束對)會增加大約10μs的開銷。為避免假卡頓延遲,請不要在一幀中被調用數十次或短于200us的方法中添加跟蹤標記。

有關更多信息,請參見了解Systrace。

5.Custom performance monitoring

如果您無法在本地設備上復現卡頓延遲問題,則可以在應用程序中內置自定義性能監控,以幫助在現場識別設備上延遲的來源。

6.fix 延遲問題

要修復延遲問題,請檢查16.7ms 內哪些幀未完成,然后查找出問題所在。記錄View#draw時間異常長的一些幀,或者Layout有關問題,請參見下面常見的卡頓延遲來源。

為了避免出現卡頓延遲,建議長時間運行的任務應在UI線程之外異步運行。

如果您的應用具有復雜且重要的主用戶界面(可能是滾動列表),請考慮編寫自動檢測工具,記錄較慢渲染時間并頻繁運行測試以防止出現卡頓延遲的問題。有關更多信息,請參見自動化性能測試Codelab。

7.常見延遲問題的來源

以下各節介紹了應用中常見的卡頓延遲問題的來源以及解決這些問題的最佳做法。

Scrollable lists

ListView 特別是RecyclerView通常用于復雜滾動列表,這也是最容易受卡頓延遲影響的地方。它們都包含Systrace標記,因此您可以使用Systrace來確定它們是否對應用程序的卡頓延遲有所貢獻。嘗試使用命令行參數-a 以顯示RecyclerView中的跟蹤部分(以及您添加的任何跟蹤標記)。如果有問題,請遵循systrace輸出中生成的警報的指導 。在Systrace中,您可以單擊RecyclerView跟蹤的部分以查看有關RecyclerView正在進行的工作的說明。

RecyclerView: notifyDataSetChanged

如果你的項目中 RecyclerView 存在反復使用(并因此重新布局和重新繪制)一個框架,確保你不會調用 notifyDataSetChanged() , setAdapter(Adapter) 或 swapAdapter(Adapter, boolean) 對于小更新。這些方法表明整個列表內容已更改,并將在Systrace中顯示為RV FullInvalidate。而是使用 SortedList 或 DiffUtil 在內容更改或添加時生成最少的更新。

例如,考慮一個從服務器接收新聞內容列表的新版本的應用程序。當您將該信息發布到適配器時,可以 notifyDataSetChanged() 如下所示進行調用:

void onNewDataArrived(List news) {

myAdapter.setNews(news);

myAdapter.notifyDataSetChanged();

}

但這帶來了很大的缺點-如果這是微不足道的更改(也許在頂部添加了單個項目), RecyclerView 則不知道-告訴它刪除所有緩存的項目狀態,因此需要重新綁定所有內容。

最好使用 DiffUtil ,它將為您計算和分發最少的更新。

void onNewDataArrived(List news) {

List oldNews = myAdapter.getItems();

DiffResult result = DiffUtil.calculateDiff(new MyCallback(oldNews, news));

myAdapter.setNews(news);

result.dispatchUpdatesTo(myAdapter);

}

只需將MyCallback定義為 DiffUtil.Callback 實現即可告知 DiffUtil 如何檢查列表。

RecyclerView:嵌套的RecyclerViews

嵌套 RecyclerView 是很常見的,尤其是在水平滾動列表的垂直列表中(例如Play Store主頁上的應用程序網格)。這可以很好地工作,但是也有很多觀點在移動。如果您在第一次向下滾動頁面時看到很多內部項目膨脹,則可能要檢查是否 RecyclerView.RecycledViewPool 在內部(水平)RecyclerViews之間共享。默認情況下,每個RecyclerView都有自己的項目池。如果同時顯示十二個itemViews屏幕itemViews,那么如果所有行都顯示相似類型的視圖,則無法由不同的水平列表共享時會出現問題。

class OuterAdapter extends RecyclerView.Adapter {

RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();

...

@Override

public void onCreateViewHolder(ViewGroup parent, int viewType) {

// inflate inner item, find innerRecyclerView by ID…

LinearLayoutManager innerLLM = new LinearLayoutManager(parent.getContext(),

LinearLayoutManager.HORIZONTAL);

innerRv.setLayoutManager(innerLLM);

//

innerRv.setRecycledViewPool(sharedPool);

return new OuterAdapter.ViewHolder(innerRv);

}

...

如果您想進一步優化,還可以調用 setInitialPrefetchItemCount(int) 內部的RecyclerView LinearLayoutManager 。例如,如果您總是連續可見3.5個項目,請致電 innerLLM.setInitialItemPrefetchCount(4);。這將向RecyclerView發出信號,當屏幕上將出現水平行時,如果UI線程上有空閑時間,則應嘗試預取其中的項目。

RecyclerView: inflation過多,導致創建時間過長

RecyclerView 在大多數情況下,通過預取功能,UI線程處于空閑狀態時,可以提前完成工作,從而有助于解決通貨膨脹的問題。如果您在一幀中看到通貨膨脹(而不是在標記為RV Prefetch的部分中未看到),請確保在最新設備(當前僅Android 5.0 API Level 21和更高版本支持Prefetch)上進行測試,并使用最新版本的該支持庫。

如果您經常在屏幕上看到新項目引起的通貨膨脹引起的刺痛,請確認您沒有所需的視圖類型過多。RecyclerView內容中的視圖類型越少,當新的項目類型出現在屏幕上時,需要進行的充氣就越少。如果可能,請在合理的情況下合并視圖類型-如果類型之間僅圖標,顏色或文本發生更改,則可以在綁定時進行更改,并避免膨脹(同時減少應用程序的內存占用)。

如果您的視圖類型看起來不錯,請考慮降低通脹成本。減少不必要的容器視圖和結構視圖可能會有所幫助–考慮itemViews使用 ConstraintLayout進行構建,這可以輕松地減少結構視圖。如果您想真正地優化性能,您的項目層次結構很簡單,并且不需要復雜的主題和樣式功能,請考慮自己調用構造函數-但是請注意,失去該版本的簡單性和功能通常不值得進行權衡XML。

RecyclerView: Bind 花費時間過長

綁定(即 onBindViewHolder(VH, int) )應該非常簡單,除最復雜的項目外,其他所有項目的花費都不到一毫秒。它僅應從適配器的內部項目數據中獲取POJO項目,并在ViewHolder中的視圖上調用設置程序。如果RV OnBindView花費很長時間,請確認您在綁定代碼中所做的工作最少。

如果您使用簡單的POJO對象將數據保存在適配器中,則可以完全避免使用數據綁定庫在onBindViewHolder中編寫綁定代碼 。

RecyclerView或ListView:布局/繪制花費的時間太長

有關繪制和布局的問題,請參見“ 布局和 渲染性能部分”。

ListView inflation過多

ListView 如果您不小心,很容易意外禁用回收。如果您每次在屏幕上看到一個項目時都看到inflation,請檢查您的實現是否 Adapter.getView() 正在使用,重新綁定并返回該convertView參數。如果您的 getView()實現總是inflation,那么您的應用程序將無法從ListView中獲得回收的好處。您的結構getView()幾乎總是與以下實現類似:

View getView(int position, View convertView, ViewGroup parent) {

if (convertView == null) {

// only inflate if no convertView passed

convertView = layoutInflater.inflate(R.layout.my_layout, parent, false)

}

// … bind content from position to convertView …

return convertView;

}

Layout 性能問題

如果Systrace顯示Choreographer#doFrame的Layout部分做太多的工作,或者做得太頻繁,則意味著您遇到了布局性能問題。應用程序的布局性能取決于View層次結構中那個具有變化的布局參數或輸入的部分。

Layout 嵌套過多

如果段長于幾毫秒,則可能是RelativeLayouts或 weighted-LinearLayouts達到最壞情況的嵌套性能 。這些布局中的每一個都可以觸發其子級的多次測量/布局遍歷,因此嵌套它們可以導致嵌套深度為O(n ^ 2)。嘗試在層次結構中除最低葉節點之外的所有節點中避免使用RelativeLayout或LinearLayout的權重功能。有幾種方法可以做到這一點:

您可以重新組織結構視圖。

您可以定義自定義布局邏輯。有關特定示例,請參見優化布局指南。

您可以嘗試轉換為 ConstraintLayout,它提供了相似的功能,而沒有性能上的缺點。

Layout 更新頻率

當新內容出現在屏幕上(例如,當新項目滾動到中的視圖中)時,預計會發生布局 RecyclerView 。如果每個幀上都發生大量布局,則可能是在使布局動畫化,這很可能導致丟幀。一般情況下,應該動畫上繪制的性能下運行 View (例如setTranslationX/Y/Z(), setRotation(),setAlpha()等...)。與布局屬性(例如填充或邊距)相比,所有這些更改的成本都低得多。更改視圖的繪圖屬性通常也很便宜,通常是通過調用setter來觸發 invalidate() ,然后 draw(Canvas) 在下一幀中觸發 。這將為無效的視圖重新記錄繪圖操作,并且通常也比布局便宜。

8. 渲染性能問題

Android UI確實分兩個階段工作- 在UI線程上記錄View#draw和在RenderThread 上記錄****DrawFrame。第一個 draw(Canvas) 在每個無效的上運行 View ,并且可能將調用調用到自定義視圖或您的代碼中。第二個在本機RenderThread上運行,但將基于Record View#draw階段生成的工作進行操作。

渲染性能:UI線程

如果Record View#draw花費很長時間,則通常在UI線程上繪制位圖。繪制到位圖使用CPU渲染,因此通常應盡可能避免這種情況。您可以將方法跟蹤與 Android CPU Profiler一起使用,以查看是否存在此問題。

當應用要在顯示位圖之前對其進行裝飾時,通常會繪制到位圖。有時會像添加圓角這樣的裝飾:

Canvas bitmapCanvas = new Canvas(roundedOutputBitmap);

Paint paint = new Paint();

paint.setAntiAlias(true);

// draw a round rect to define shape:

bitmapCanvas.drawRoundRect(0, 0,

roundedOutputBitmap.getWidth(), roundedOutputBitmap.getHeight(), 20, 20, paint);

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));

// multiply content on top, to make it rounded

bitmapCanvas.drawBitmap(sourceBitmap, 0, 0, paint);

bitmapCanvas.setBitmap(null);

// now roundedOutputBitmap has sourceBitmap inside, but as a circle

如果這是您在UI線程上所做的工作,則可以在后臺在解碼線程上執行此操作。在這種情況下,您甚至可以在繪制時完成工作,因此,如果您的Drawableor View代碼看起來像這樣:

void setBitmap(Bitmap bitmap) {

mBitmap = bitmap;

invalidate();

}

void onDraw(Canvas canvas) {

canvas.drawBitmap(mBitmap, null, paint);

}

您可以將其替換為:

void setBitmap(Bitmap bitmap) {

shaderPaint.setShader(

new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP));

invalidate();

}

void onDraw(Canvas canvas) {

canvas.drawRoundRect(0, 0, width, height, 20, 20, shaderPaint);

}

請注意,通常還可以執行此操作以進行背景保護(在位圖上方繪制漸變)和圖像過濾(使用 ColorMatrixColorFilter ),另外兩個常見的操作是修改位圖。

如果您正在繪制成位圖的另一個原因,可能使用為高速緩存,嘗試和借鑒,以加速畫布硬件傳遞到您的視圖或直接繪制對象,并在必要時,考慮調用 setLayerType() 與 LAYER_TYPE_HARDWARE 緩存復雜的渲染輸出,而且還利用GPU渲染。

渲染性能: RenderThread

有些畫布操作記錄起來很便宜,但是會在RenderThread上觸發昂貴的計算。Systrace通常會通過警報將其調出。

Canvas.saveLayer()

避免 Canvas.saveLayer() -它會觸發每幀昂貴,未緩存的屏幕外渲染。盡管在Android 6.0中提高了性能(進行了優化以避免在GPU上切換渲染目標時),但如果可能的話,最好避免使用這種昂貴的API,或者至少確保您傳遞了 Canvas.CLIP_TO_LAYER_SAVE_FLAG (或調用一個 不帶標志)。

Canvas path

在 Canvas.drawPath() 傳遞給Views的硬件加速Canvas上調用時,Android首先在CPU上繪制這些路徑,然后將其上傳到GPU。如果路徑較大,請避免逐幀編輯它們,以便可以有效地緩存和繪制它們。drawPoints(),drawLines()和 drawRect/Circle/Oval/RoundRect()更有效–即使最終使用了更多繪制調用,也最好使用它們。

Canvas.clipPath

clipPath(Path) 觸發昂貴的剪裁行為,通常應避免。如果可能,請選擇繪制形狀,而不是剪切成非矩形。它的性能更好,并支持抗鋸齒。例如,以下clipPath調用:

canvas.save();

canvas.clipPath(circlePath);

canvas.drawBitmap(bitmap, 0f, 0f, paint);

canvas.restore();

可以被替代的方法如下:

// one time init:

paint.setShader(new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP));

// at draw time:

canvas.drawPath(circlePath, mPaint);

Android將位圖顯示為OpenGL紋理,并且在幀中首次顯示位圖時,會將其上載到GPU。您可以在Systrace中看到它為Upload width x height Texture。這可能要花費幾毫秒的時間(請參見圖2),但是必須使用GPU顯示圖像。

如果要花費很長時間,請首先檢查軌跡中的寬度和高度數字。確保所顯示的位圖沒有明顯大于其在屏幕上顯示的區域。如果是,則會浪費上傳時間和內存。通常,位圖加載庫提供了請求適當大小的位圖的簡便方法。

在Android 7.0中,可以調用位圖加載代碼(通常由庫完成) prepareToDraw() ,以在需要之前提早觸發上傳。這樣,上傳就可以在RenderThread空閑時提早進行。只要知道位圖,就可以在解碼后或將位圖綁定到View時完成。理想情況下,位圖加載庫將為您執行此操作,但是如果您要管理自己的圖像,或者想確保自己不會在較新的設備上點擊上傳文件,則可以調用 prepareToDraw() 自己的代碼。

圖2:一個應用在一個框架上花費了10毫秒以上的時間來上傳一個1.8兆像素的位圖。減小其大小,或在使用解碼時提早觸發它prepareToDraw()。

9. 線程調度延遲

線程調度程序是Android操作系統的一部分,負責確定系統中的哪些線程應運行,何時運行以及持續多長時間。有時,由于您的應用程序的UI線程被阻止或未運行,因此出現了垃圾郵件。Systrace使用不同的顏色(見圖3),當一個線程來表示 睡眠(灰色),Runnable接口(藍色:它可以運行,但調度還沒有采摘它尚未運行),積極跑動(綠),或在不間斷睡眠(紅色或橙色)。這對于調試由線程調度延遲引起的棘手問題非常有用。

注意:較舊版本的Android經常會遇到調度問題,而這并不是應用程序的問題。在此方面已進行了持續改進,因此請考慮在最新的OS版本上調試線程調度問題,在這種情況下,調度線程很可能是應用程序的錯誤。

圖3:突出顯示了UI線程正在休眠的時間段。

注意:當UI線程或RenderThread不能運行時,框架的某些部分。例如,在syncFrameState運行RenderThread的過程中阻止了UI線程,并上傳了位圖–這樣,RenderThread可以安全地復制UI線程使用的數據。另一個示例是,當RenderThread使用IPC進行以下操作時,它可以被阻止:在幀的開頭獲取緩沖區,從幀中查詢信息或使用eglSwapBuffers將緩沖區傳遞回合成器。

通常,應用程序執行過程中的長時間停頓是由binder調用(Android上的進程間通信(IPC)機制)引起的。在最新版本的Android上,這是UI線程停止運行的最常見原因之一。通常,解決方法是避免調用進行聯編程序調用的函數。如果不可避免,則應緩存該值,或將工作移至后臺線程。隨著代碼庫的擴大,如果您不小心,很容易通過調用一些低級方法意外地添加了一個binder調用,但是通過跟蹤查找和修復它們同樣容易。

如果您有binder事務,則可以使用以下adb命令捕獲其調用堆棧:

$ adb shell am trace-ipc start

… use the app - scroll/animate ...

$ adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc-trace.txt

$ adb pull /data/local/tmp/ipc-trace.txt

有時,諸如此類的無用調用可能 getRefreshRate() 會觸發binder事務,并在頻繁調用它們時引起大問題。定期跟蹤可以幫助您快速發現并解決這些問題,因為這些問題會不斷出現。

圖4:顯示了由于RV檔案中的綁定器事務而導致的UI線程休眠。保持綁定邏輯簡單,并用于 trace-ipc跟蹤和刪除綁定程序調用

如果您沒有看到binder activity,但仍然沒有看到UI線程運行,請確保您沒有在等待來自另一個線程的某些鎖定或其他操作。通常,UI線程不必等待其他線程的結果–其他線程應向其發布信息。

10. 分配對象和垃圾回收機制

自從將ART作為Android 5.0中的默認運行時引入對象以來,對象分配和垃圾回收(GC)的問題已大大減少,但是仍然可以通過這些額外的工作來減輕線程的負擔。可以對很少發生的事件進行分配,這種事件不會每秒發生多次(例如用戶單擊按鈕),但是請記住,每次分配都需要付出一定的代價。如果處于經常調用的緊密循環中,請考慮避免分配以減輕GC的負擔。

Systrace將向您顯示GC是否經常運行,而 Android Memory Profiler可以向您顯示分配的來源。如果在可能的情況下避免分配,尤其是在緊密循環中,則應該沒有問題。

圖5:在HeapTaskDaemon線程上顯示了一個94毫秒的GC

在最新版本的Android上,GC通常在名為HeapTaskDaemon的后臺線程上運行 。請注意,大量分配可能意味著更多的CPU資源花費在GC上,如圖5所示。

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

總結

以上是生活随笔為你收集整理的java线程太多卡顿_性能优化之卡顿延迟的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。