程序員自我修養(yǎng)之性能優(yōu)化篇

一、卡頓優(yōu)化

1、UI優(yōu)化方式 :?

布局復雜、層級過深、自定義view防止過度繪制、ViewStub的方式去占位布局

2、冷啟動優(yōu)化方式 :

?異步(IntentService)、懶加載(Handler處理)、延遲(IdleHandler,cpu空閑時間加載數據)

3、響應優(yōu)化方式 :?

滑動過程中不加載數據、預加載、懶加載

4.繪制界面分為兩個步驟:

? ? ? ? 加載布局、圖像繪制

5.統(tǒng)計啟動時間的方法:

? ? ? ? a. TraceCompat.beginSection();

? ? ? ? ? ? TraceCompat.endSection();?

? ??????wall time : 是代碼執(zhí)行完花費的時間(包含cpu和IO的讀寫操作)

  cpu time : 是cpu花費多少時間執(zhí)行完這段代碼(這里才是作為主要的優(yōu)化時間點)

? ? ? ? 執(zhí)行systrace.py這個參數,就可以把你加的日志生成一個html文件進行查看,它也可以底層的執(zhí)行時間

? ? ? ? b.? 使用AOP切面編程:代碼無侵略性

? ? ? ? c.? AS的profiler這個工具也可以查看耗時

?6.UI常見問題以及優(yōu)化

? ? ? ? 常見問題 : 過度繪制、布局復雜、層級過深

? ? ? ? 解決辦法:? 減少從xml文件創(chuàng)建為對象、減少過度繪制操作

? ? ? ? 1).xml文件上解決過度繪制 : 使用android:backgroud會增加一次過度繪制次數

? ? ? ? 2).自定義view過度繪制:

clipRect : 識別可見區(qū)域繪制,非可見部分就不進行繪制,俗稱裁剪。注意:在調用cilpRect、scale(旋轉)、translate(平移)、縮放等這些操作之前,需要調用canvas.save方法

? ? ? ? 3).可以采用ViewStub的方式去占位布局,使用時再去渲染

4).查看代碼性能問題:AS->Analyze->Inspect Code?查看關鍵字Performance、Xml文件

? ? ? ? 5).常見的異步操作的手段

?AsyncTask :UI線程和工作線程之間快速切換提供的簡單機制

? ? ? ? ?適用場景:立即需要啟動,但是異步執(zhí)行的生命周期短暫

HanderThread :?為某些回調方法或者等待某些任務執(zhí)行設置一個專屬線程

ThreadPool :?把任務分解到各個不同的線程上,進行同時并發(fā)處理

IntentService :?適合執(zhí)行由UI觸發(fā)的后臺service,可以把后臺執(zhí)行任務反饋給UI(適合在Application 的onCrate里面適用,操作簡單,且自己可以銷毀對象,其他方式小題大做)

? ? ? ?6).ListView性能優(yōu)化

? ? ? ? ????a.復用converView

? ? ? ? ????b.復用viewHolder

? ? ? ? ????c.getView少做耗時的操作

? ? ? ? ????d.滑動停止時候再去加載圖片

? ? ? ? ? ? e.使用異步線程加載圖片

滑動時候為什么不會發(fā)生OOM的原因(RecycleBin的復用機制)

? ? ? ? ?RecycleBin里面有兩個存數據的數組,一個是ActivtyView數組,這個是給用戶展示的view,另一個是ScrapView數組,它是不需要展示view,但是已經創(chuàng)建,為了二次展示作為復用來使用。展示的view會先從ScrapView數組去遍歷查找是否存在,如果存在直接在復用給ActivityView來使用,如果不存在創(chuàng)建之后給ActivityView來展示。

?7.Systrace去檢查UI卡頓問題

? ? 在android-sdk/platform-tools/systrace/執(zhí)行systrace.py

? ? 執(zhí)行python systrace.py --time=10 -o mynewtrace.html 命令生成.html文件

W:界面放大

S:界面縮小

A:界面左移

D:界面右移



二、內存優(yōu)化-------------***************------------

? ? 1.OOM容易混淆的概念

? ??????內存溢出:我們申請的內存已經超出虛擬機的最大內存的限度就會拋出Out? Of? memory

????????內存抖動 :? 短時間大量的對象被創(chuàng)建然后就釋放,觸發(fā)的GC機制,嚴重占用內存區(qū)。

????????內存泄露 :? 我們要回收的對象無法進行GC的回收操作

? ? ? ? 造成OOM的原因:

? ? ? ? Java堆內存溢出、無足夠的連續(xù)空間、虛擬內存不足、線程數量超出限制

? ? 2.內存泄漏的常見案例

????????1).單例:長生命周期的對象(Application的對象)被短生命周期的對象持有

????????2). handler :?非靜態(tài)內部類持有外部類的引用,導致于無法釋放外部類的對象---->解決方法將handler至于為static的對象,然后將外部類改為弱引用來使用

? ? ? ? 3).線程引起: AsyncTask和Runnable使用匿名內部類,因為非靜態(tài)內部類持有外部類的引用,和handler類似

? ? ? ? 4).文件的讀寫、數據庫網絡啟用后沒有關閉、三方的框架沒有銷毀

? ? ? ? 5).注冊廣播以及使用三方的庫沒有再銷毀時候關閉

? ? ?3.不同內存的類型

? ??內存的類別如下:

????????Java:從 Java 或 Kotlin 代碼分配的對象的內存。

????????Native:從 C 或 C++ 代碼分配的對象的內存。

????????Graphics:圖形緩沖區(qū)隊列向屏幕顯示像素(包括 GL 表面、GL 紋理等等)所使用的內存。

? ? ????Stack:您的應用中的原生堆棧和 Java 堆棧使用的內存

????????Code:您的應用用于處理代碼和資源(如 dex 字節(jié)碼、經過優(yōu)化或編譯的 dex 代碼、.so 庫和字體)的內存。

????????Others:您的應用使用的系統(tǒng)不確定如何分類的內存。

?4.內存優(yōu)化方向

?????1).BitMap的圖片優(yōu)化,避免濫用Bitmap導致內存的浪費,使用完及時回收

? ? ?2).減少對象重復創(chuàng)建,多采用復用方式,避免內存碎片化

? ? ?3).包體的優(yōu)化 :?混淆、刪除多余文件和圖片、壓縮圖片、語言和cpu架構的配置

? ? ?4).使用適當的數據結構去存儲數據

?5.內存優(yōu)化之垃圾對象的處理

?FinalizerReference就會越來越大原因:

一旦用戶創(chuàng)建對象(重寫了finalize函數)的速度快于finalize隊列移除各元素的速度,finalize里面回收的是弱引用和虛引用,虛引用主要記錄對象的銷毀,也因為JVM的垃圾回收、內存管理都是守護線程,他們優(yōu)先級比用戶線程低很多。設置守護線程方法是setDaemon(true)的方法。

解決FinalizerReference過大的辦法:

????1.不要重寫finalize()方法,實在要釋放資源,到destroy一類函數中處理

????2.重復利用資源,可以采用對象池方式處理,防止發(fā)生內存抖動,避免反復創(chuàng)建

????3.調用System.runFinalization()回收

?System.gc()和System.runFinalization()區(qū)別:

? ? System.gc():告訴垃圾收集器打算進行垃圾收集,而垃圾收集器進不進行收集是不確定的

? ? System.runFinalization():強制調用已經失去引用的對象的finalize方法

?6.內存優(yōu)化之碎片化 (內存抖動)

?Linux內存管理:(做為了解,后續(xù)再補充)

? ? 優(yōu)質的網址:https://www.kancloud.cn/zhangyi8928/kernel/531016

VSS?: 虛擬內存,預計要占用的內存但并沒有實際占用。

RSS?: 一個進程在RAM中實際使用內存。該進程獨享內存(USS)+共享內存

PSS :??一個進程的PSS=USS+該進程所占共享內存。所有進程的PSS相加即為系統(tǒng)占用內存總和。

USS?: 一個進程獨享的內存。該進程被殺死后USS會被系統(tǒng)回收

? ??解決方式:

? ?共享元設計模式對象池去處理?: 創(chuàng)建一個讀對象之后就不會再創(chuàng)建了,只是里面的數據會被清空,對象不會被銷毀

? ? 對象池線程不安全的

????Pools.SimplePool<Integer> pool = new Pools.SimplePool(10);

? ? 對象池線程安全的

? ? Pools.SynchronizedPool<Integer> pool1 = new Pools.SynchronizedPool<>(10);? ??

?java內存的大小計算 :?shallow size、retained size

? ??shallow size : 自身占用的內存的大小

? ??retained size :該對象能直接或間接訪問到對象的shallow size之和

? ?案例講解:

從obj1入手,上圖中藍色節(jié)點代表僅僅只有通過obj1才能直接或間接訪問的對象。因為可以通過GC Roots訪問,所以左圖的obj3不是藍色節(jié)點;而在右圖卻是藍色,因為它已經被包含在retained集合內。

? ? 對于左圖,obj1的retained size是obj1、obj2、obj4的shallow size總和;右圖的retained size是obj1、obj2、obj3、obj4的shallow size總和。

????對于obj2,它的retained size是:在左圖中,是obj2和obj4的shallow size的和;在右圖中,是obj2、obj3和obj4的shallow size的和。

?7.內存優(yōu)化之圖片優(yōu)化

? ? ?1). 一張圖片加載到內存的計算

? ? ? ? 加載分辨率為1080*452的圖片,文件本身大小是56kb

? ? ? ? 1080*452*4B =? 1952640B -----> 1.86MB? 這樣計算會有問題

? ? ? ? 正確計算方式:

??????1080*452*4B*(設備的dpi(240)/目錄的dpi(1.5))-------->這個就可以被放在對應目錄下的正確的大小

? ? 2).BitMap的幾種像素格式

????????ALPHA_8:每個像素都需要1(8位)個字節(jié)的內存,只存儲位圖的透明度,沒有顏色信息

????????ARGB_4444:A(Alpha)占4位的精度,R(Red)占4位的精度,G(Green)占4位的精度,B(Blue)占4位的精度,加起來一共是16位的精度,折合是2個字節(jié),也就是一個像素占兩個字節(jié)的內存,同時存儲位圖的透明度和顏色信息。不過由于該精度的位圖質量較差,官方不推薦使用

????????ARGB_8888:這個類型的跟ARGB_4444的原理是一樣的,只是A,R,G,B各占8個位的精度,所以一個像素占4個字節(jié)的內存。由于該類型的位圖質量較好,官方特別推薦使用。但是,如果一個480*800的位圖設置了此類型,那個它占用的內存空間是:480*800*4/(1024*1024)=1.5M

????????RGB_565:同理,R占5位精度,G占6位精度,B占5位精度,一共是16位精度,折合兩個字節(jié)。這里注意的時,這個類型存儲的只是顏色信息,沒有透明度信息

? ??3).圖片優(yōu)化的方式 : 降低分辨率、減少每個像素點大小

? ??????如何去加載一個大圖的方式:根本原因是降低圖片的像素

????????圖片的優(yōu)化行為:

? ??????質量壓縮 :? 可以降低圖片質量,不能降低圖片的像素,不能起到圖片優(yōu)化效果

????????采樣率壓縮 :? 可以降低圖片的像素options.inSample 這個參數,起到圖片優(yōu)化效果

? ? ? ? 尺寸壓縮 : 通過壓縮圖片寬、高來降低圖片的像素,起到圖片優(yōu)化效果

? ? ? ? 改變圖片的格式,從RGBA_8888(默認)轉變?yōu)槠渌袷剑@種不推薦使用

? ?4).? 圖像的開辟和銷毀

? ??????圖像開辟:

? ??????圖片需要縮放,都是開辟在native層的,如果不需要縮放,8.0以前是在java層開辟的

? ??????8.0以下放在 java的內存之中

? ??????8.0以上是放在native內存之中,有2G內存供使用

? ??????圖片銷毀:

? ??????bitMap.recycle() : 回收是像素數據,只是在8.0以及以上可以使用

? ? ? ? 6.0以上是通過監(jiān)聽GC觸發(fā)機制去傳遞給底層使用,然后進行銷毀,監(jiān)聽方式是ReferenceQueue隊列去監(jiān)聽

? ? ?5).BitMap的BitmapFactory.Options參數講解

? ??????Scale(轉換率) =? inTargetDensity /? inDensity

????????BitmapFactory.Options options =new BitmapFactory.Options();

? ? ? ? //會根據drawable文件夾的分辨率來賦值

????????options.inDensity

? ? ? ? //會根據屏幕的像素密度來賦值

????????options.inTargetDensity

? ??????//對圖片進行壓縮,對圖片寬和高各壓縮2倍

????????options.inSampleSize = 2

? ??????//設置圖片是否會被壓縮,它的參數就是由Scale(轉換率)去計算

????????options.inScaled? ? ? ? ? ? ? ? ?

? ??????//為true不會得到bitMap的對象,而是可以的高寬、高的信息

????????options.inJustDecodeBounds

? ??????//代表資源圖片的的寬

????????options.outWidth;

? ??????//代表資源圖片的的高

????????options.outHeight

? ??????//代表BitMap可以復用

????????options.inMutable

? ??????//設置圖片

????????options.inBitmap

????Bitmap bitmap = BitmapFactory.decodeResource(getResources(),? ? ? ? ? ? ? ? ????R.drawable.ic_launcher_background, options);

? 8.內存優(yōu)化之檢測系統(tǒng)接口

? ??OnLowMemory:在系統(tǒng)內存不足,所有后臺程序(優(yōu)先級為background的進程,不是指后臺運行的進程)都被殺死時,系統(tǒng)會調用OnLowMemory

????OnTrimMemory:系統(tǒng)會根據不同的內存狀態(tài)來回調。系統(tǒng)提供的回調有:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Application/Activity/Fragement/Service/ContentProvider? ? ??

? ??TRIM_MEMORY_COMPLETE:內存不足,并且該進程在后臺進程列表最后一個,馬上就要被清理

????TRIM_MEMORY_MODERATE:內存不足,并且該進程在后臺進程列表的中部。

????TRIM_MEMORY_BACKGROUND:內存不足,并且該進程是后臺進程。

????TRIM_MEMORY_UI_HIDDEN:內存不足,并且該進程的UI已經不可見了。? ? ??

????TRIM_MEMORY_RUNNING_CRITICAL:內存不足(后臺進程不足3個),并且該進程優(yōu)先級比較高,需要清理內存

? ??TRIM_MEMORY_RUNNING_LOW:內存不足(后臺進程不足5個),并且該進程優(yōu)先級比較高,需要清理內存

????TRIM_MEMORY_RUNNING_MODERATE:內存不足(后臺進程超過5個),并且該進程

OnLowMemory和OnTrimMemory的比較

? a.OnLowMemory被回調時,已經沒有后臺進程;而onTrimMemory被回調時,還有后臺進程。

? b.OnLowMemory是在最后一個后臺進程被殺時調用,一般情況是low memory killer 殺進程后觸發(fā);而OnTrimMemory的觸發(fā)更頻繁,每次計算進程優(yōu)先級時,只要滿足條件,都會觸發(fā)。

? c.通過一鍵清理后,OnLowMemory不會被觸發(fā),而OnTrimMemory會被觸發(fā)一次。



三、性能優(yōu)化--------------*************------------

冷啟動優(yōu)化方式:異步(IntentService)、懶加載(Handler處理)、延遲(IdleHandler,cpu空閑時間加載數據)

1.異步方式:

 ????a.Thread : 不容易復用,頻繁創(chuàng)建及開銷大

  b.HandlerThread : 自帶消息循環(huán),適用于長時間運行,不斷從隊列中獲取任務

? ? ? ?c.IntentService :? 繼承Service在內部創(chuàng)建一個HandlerThread ,異步,不占用主線程,優(yōu)先級較高,不容易被系統(tǒng)kill

? ? ? ?d. AsyncTask : 無需處理線程切換,使用簡單

? ? ? ? ????onPreExecute():異步任務開啟之前回調,在主線程中執(zhí)行

?  ????doInBackground():執(zhí)行異步任務,在線程池中執(zhí)行

   ????onProgressUpdate():在主線程中執(zhí)行

   ????onPostExecute():在異步任務執(zhí)行之后回調,在主線程中執(zhí)行

  ? ????onCancelled():在異步任務被取消時回調

? ? ? ? ?e.線程池:容易復用,減少頻繁創(chuàng)建、銷毀時間,? 定時、任務隊列、并發(fā)數控制

2.設置優(yōu)先級

 Process.setThreadPriority() --------->這個可以設置優(yōu)先級的高低

3.?采用IntentService啟動。它是在異步線程中執(zhí)行的,用完后它會自動釋放對象的,它繼承Serice也不容易被殺死。IntentService = Handler+HandlerThread,它的執(zhí)行是有順序的

4.BaseClassLoader里面有個Element的數組,里面就是所有的.dex的文件,它需要通過dexElement.findClass遍歷整個數組,找到對應的.class才能啟動,把我們要先啟動的.class放在最前面,這樣就可以加快啟動速度。

5.SharedPreference的函數講解

? ? ? ? 1).apply和commit的區(qū)別? ? ? ??

? ? ? ? ? ? apply是沒有返回值的,在往磁盤寫入時候,它是開啟子線程不需要等待返回結果

? ? ? ? ? ? commit是會返回boolean的,主線程一直等往磁盤寫入成功后,才會去執(zhí)行

? ? ? ? 2).SharedPreference是存儲在xml文件中,它線程安全性。

? ? ? ? ? ? 有緩存機制原因,高并發(fā)情況襲多進程是不安全安全

? ? ? ? 3).fileobserver多進程安全的原因

? ??????????startWatching和stopWatching里面都加了鎖,并且鎖的對象是修改的文件路徑



四、ANR的產生和解決辦法--------*******--------------

?? ? 1、卡頓原理

? ? ? ? 產生原因:主線程做耗時操作就會產生卡頓,卡頓超過閾值,觸發(fā)ANR。

卡頓產生因素:UI層級嵌套過深、Handler處理消息太耗時(barrier-->?msg.target == null,同步屏障消息)

? ? ? 2、卡頓監(jiān)控

? ? ? ? ? 處理方式:Gradle Plugin+ASM,目前微信的Matrix 使用的卡頓監(jiān)控方案就是字節(jié)碼插樁

? ? ? ?3、ANR 原理

? ? ? ? ? ?1)、觸發(fā)ANR的場景

? ??????????輸入事件分發(fā)超時5s,包括按鍵和觸摸事件

? ??????????比如前臺廣播在10s內未執(zhí)行完成

? ? ? ? ? ? servive后臺在20s內沒有完成

? ? ? ? ? ?2)、servive、broadcast原理實現

? ? ? ? ? ? a)、Service的onCreate方法被調用時候調用mHandler.sendMessageDelayed發(fā)送延遲時間來處理事情

? ? ? ? ? ? b)、在任務完成前會調用 :mHandler.removeMessages把消息移除,這樣劇不會產生ANR

? ? ? ? ? ? c)、任務沒有前就會調用mAppErrors.appNotResponding,來告訴產生的原因,彈ANR Dialog

? ? ? ? ? ?3)、input原理實現

input來說即便某次事件執(zhí)行時間超過timeout時長,只要用戶后續(xù)在沒有再生成輸入事件,則不會觸發(fā)ANR。

? ? ? ?4、 ANR檢測工具

? ????????一般來說有四種,分別為BlockCanay、ANR-WatchDog、SafeLooper和FileObserver

????????ANRWatchDog? ? ? ? ? ? ? ? ?

? ? ? ? ? ?原理: 開啟一個子線程mThreadRunnable,每隔1s會執(zhí)行一次mThreadRunnable,單獨現場向主線程發(fā)送一個變量+1操作,休眠過后判斷變量是否+1完成,如果未完成則警告

? ??????FileObserver

? ??????Android手機發(fā)生ANR后,會把信息存儲在/data/anr/traces.txt文件,我們只需要監(jiān)聽這個文件的變化就可以知道是否發(fā)生了ANR。


五、APP瘦身優(yōu)化

????1.刪:刪除無用的代碼和資源

? ? 2.移:實在不行就抽離出,動態(tài)加載,動態(tài)下發(fā)。

? ? 3.壓:壓縮代碼和資源

buildTypes {

? ? ? ? release {

? ? ? ? ? ? // 源代碼混淆開啟

? ? ? ? ? ? minifyEnabled true

? ? ? ? ? ? // 啟動資源壓縮

? ? ? ? ? ? shrinkResources true

? ? ? ? ? ? proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

? ? ? ? }

? ? }

? ? 4.? 圖片優(yōu)化 : 除了用PNG格式外,圖片可以使用SVG(縮圖),在gradle需要配置

? ??????defaultConfig {

........

// 使用support-v7兼容5.0以上

????????????vectorDrawables.useSupportLibrary = true

.........

? ? ????}

SVG官方介紹:https://developer.android.google.cn/studio/write/vector-asset-studio#about

? ? 5. 資源配置打包優(yōu)化

defaultConfig {

????????????........

????????????// 只保留指定和默認資源

? ? ? ? ????resConfigs('zh-rCN', 'ko')

// 配置so庫架構(真機:arm,模擬器:x86)

?abiFilters('armeabi',armeabi-v7a')

????????????.........

? ? ????}







-----------------------------下面以后還需要再好好總結-----------------------------------------------------

????RecycleView的優(yōu)化之回收機制

1.一級優(yōu)化,優(yōu)先優(yōu)化item的列表,不需要重新綁定ViewHodler

? ? 里面有兩個ArryList的鏈表,一個是mAttachScrap,另一個是mChangedScrap,當調用notifyItemChanged方法,通知有新的item時候,之前顯示保存在mAttachScrap里面會直接復用ViewHolder,數據不需要重新綁定,新加入放在mChangedScrap里面再數據綁定

2.二級和四級優(yōu)化,Item列表優(yōu)化緩存,mCacheView可以保存劃出列表的item,調用setItemCacheSize方法可以確定緩存多少數據,超出這個數據會放在四級優(yōu)化的recyclerPool這個緩存池里


OnLowMemory()是Android提供的API,在系統(tǒng)內存不足,所有后臺程序(優(yōu)先級為background的進程,不是指后臺運行的進程)都被殺死時,系統(tǒng)會調用OnLowMemory


一般來說有四種,分別為BlockCanay、ANR-WatchDog、SafeLooper和FileObserver

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容