Android性能優(yōu)化

目錄

1罪裹、內(nèi)存優(yōu)化
2、ui優(yōu)化
3运挫、網(wǎng)絡優(yōu)化
4状共、啟動優(yōu)化

1、內(nèi)存優(yōu)化

1.1谁帕、解決所有的內(nèi)存泄漏

1.1.1峡继、內(nèi)存泄漏:

堆上分配的對象已經(jīng)不會再使用,但是GC收集器無法對其進行回收匈挖,此對象被強應用所引用 碾牌。

1.1.2、GC收集器原理:

可達性算法:從GCRoot對象為起點儡循,向下搜索舶吗,可到達的對象是稱為GC可達,GC收集器不會回收择膝,不可到達的對象稱為不GC不可達誓琼,是GC收集器回收的對象。

GCRoot對象:
(1)、虛擬機棧(棧幀找那個的局部變量表)中的對象腹侣;
(2)叔收、方法區(qū)中類靜態(tài)變量引用的對象;
(3)傲隶、方法區(qū)中常量引用的對象饺律。

1.1.3、常見的內(nèi)存泄漏實例

(1)伦籍、單例造成的內(nèi)存泄漏


單例造成內(nèi)存泄漏

單例類的實例是靜態(tài)的蓝晒,如果需要其構(gòu)造方法需要傳遞一個context,如果傳入的是 Activity 的 Context帖鸦,當傳入 Activity 就會造成泄漏了芝薇。
解決方法:this.context = context.getApplicationContext();// 使用Application 的context。

(2)作儿、內(nèi)部類造成的內(nèi)存泄漏


內(nèi)部類創(chuàng)建靜態(tài)對象導致內(nèi)存泄漏

在Activity中有一個內(nèi)部類洛二,如果創(chuàng)建了這個內(nèi)部類的靜態(tài)對象,或者這個內(nèi)部類被一個靜態(tài)對象所引用攻锰,Activity關(guān)閉的時候晾嘶,由于內(nèi)部類會持有外部類的引用,內(nèi)部類靜態(tài)對象會持有外部類Activity的引用娶吞,導致Activity發(fā)生內(nèi)存泄漏垒迂。
解決方法:將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類,如果該內(nèi)部類需要持有外部類的引用妒蛇,則使用軟引用/弱引用机断,在使用外部類的引用之前需進行空判斷。

(3)绣夺、異步線程造成的內(nèi)存泄漏


異步線程造成內(nèi)存泄漏

在Activity中有一個開啟一個線程執(zhí)行一個runnable吏奸,這個runnable是內(nèi)部類就會持有外部類Activity的引用,如果線程的生命周期比Activity的生命周期長陶耍,就不會導致Activity內(nèi)存泄漏奋蔚。
解決方法:同上

(4)、Handler 造成的內(nèi)存泄漏


handler造成內(nèi)存泄漏

在Activity中聲明一個內(nèi)部類handler烈钞,當使用這個handler發(fā)送一個延遲消息時泊碑,此消息執(zhí)行前,Activity關(guān)閉會造成內(nèi)存泄漏毯欣。
解決方法:同上

(5)資源未關(guān)閉造成內(nèi)存泄漏
對于使用了BraodcastReceiver馒过,ContentObserver,F(xiàn)ile仪媒,游標 Cursor沉桌,Stream谢鹊,Bitmap等資源的使用,應該在Activity銷毀時及時關(guān)閉或者注銷留凭,否則這些資源將不會被回收佃扼,造成內(nèi)存泄漏。

造成內(nèi)存泄漏的總結(jié):

造成內(nèi)存泄漏的的本質(zhì)是長生命周期的對象引用短生命中期的對象蔼夜。
比如說
1兼耀、靜態(tài)變量引用一個activity,最常見的就是單例模式造成的內(nèi)存泄漏求冷;
2瘤运、另外內(nèi)部類也是造成內(nèi)存泄漏的一個常見的點,內(nèi)部類會持有外部類的引用匠题,activity中聲明一個內(nèi)部類拯坟,如果這個內(nèi)部類被聲明成一個靜態(tài)變量或者被一個靜態(tài)變量持有,那么activity關(guān)閉的時候就會發(fā)生內(nèi)存泄漏韭山。
3郁季、內(nèi)部類造成的內(nèi)存泄漏還有一些其他情況,比如說钱磅,activity中聲明一個runnable或者handler的內(nèi)部類梦裂,如果這個runnable的生命周期比activity長,或者handler發(fā)送的延時消息的生命周期比activity的生命周期長盖淡,那么就會造成內(nèi)存泄漏年柠。
4、另外我遇到一個比較特殊的是webview的內(nèi)存泄漏褪迟,webview在attach的時候會(使用mcontext冗恨,對應activity)注冊一個mComponentCallbacks的回調(diào),(以便監(jiān)聽配置改變或者系統(tǒng)剩余內(nèi)存較低)牵咙,在Detach的時候會反注冊派近,反注冊的前提是webviwe沒有destory攀唯,所有我們在調(diào)用webviwe的destory方法前要先將webview從其父容器移除洁桌。

(高版本在webviwe沒有destory方法中會先檢查onDetachedFromWindow方法是否執(zhí)行,沒有就先執(zhí)行onDetachedFromWindow方法侯嘀。)

Activity onPause -> onStop -> onDestroy ---->
View調(diào)用onDetachedFromWindow方法

public interface ComponentCallbacks {
void onConfigurationChanged(Configuration newConfig);
void onLowMemory();
}

5另凌、另外資源未關(guān)閉也是造成內(nèi)存泄漏的一個原因,比如說bitmap資源戒幔,steam資源等吠谢。

1.1.4、內(nèi)存泄漏檢測方案

LeakCanary原理:

  1. LeakCanary在一個Fragment或者Activity onDestory的時候诗茎,創(chuàng)建一個弱引用 KeyedWeakReference 到要被監(jiān)控的對象工坊。

  2. 然后在后臺線程檢查引用是否被清除献汗,如果沒有,調(diào)用GC王污。

  3. 如果引用還是未被清除罢吃,把 heap 內(nèi)存 dump 到 APP 對應的文件系統(tǒng)中的一個 .hprof 文件中。

  4. 在另外一個進程中的 HeapAnalyzerService 有一個 HeapAnalyzer 使用HAHA 解析這個文件昭齐。

  5. 得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference尿招,定位內(nèi)存泄露。

  6. HeapAnalyzer 計算 到 GC roots 的最短強引用路徑阱驾,并確定是否是泄露就谜。如果是的話,建立導致泄露的引用鏈里覆。

簡單版:

1丧荐、LeakCanary在一個Fragment或者Activity onDestory的時候,創(chuàng)建一個弱引用到要被監(jiān)控的對象喧枷。
2篮奄、然后在后臺線程檢查引用是否被清除,如果沒有割去,調(diào)用GC窟却。
3、如果引用還是未被清除呻逆,把堆 內(nèi)存 取出來夸赫,在另外一個進程中使用一個haha的庫進行分析,確定是否內(nèi)存泄漏咖城,如果泄漏就建立泄漏對象到 GC roots 的最短強引用路徑并輸出茬腿。

1.2、避免內(nèi)存抖動

內(nèi)存抖動代表頻繁GC宜雀,會占用程序執(zhí)行時間切平,造成頁面卡頓等性能問題。

1.2.1辐董、避免內(nèi)存抖動的方式:

(1)悴品、避免在onDraw中創(chuàng)建Paint、Bitmap對象等简烘;
(2)苔严、避免在循環(huán)中創(chuàng)建臨時對象;
(3)孤澎、避免在scrollListener中創(chuàng)建對象届氢。

1.3、內(nèi)存抖動檢測方法

(1)覆旭、使用Android profiler 進行觀察退子,如果發(fā)下內(nèi)存劇增劇減則是GC時間岖妄,可查看GC時間段的內(nèi)存,找出重復創(chuàng)建的大量對象寂祥,進行優(yōu)化(例子:在recycleview中獲取距離屏幕的距離時衣吠,創(chuàng)建大量對象)。

1.3壤靶、圖片優(yōu)化

1.3.1 一些結(jié)論

1缚俏,memorySize ≈ 分辨率* 每個像素需要的字節(jié)數(shù)

2,像素點大小就幾種情況:ARGB_8888(4B)贮乳、RGB_565(2B) 等等忧换。

3,jpg向拆、png 只是圖片的容器亚茬,圖片文件本身的大小與它所占用的內(nèi)存大小沒有什么關(guān)系。
使用Tinypng深度壓縮浓恳,使用web格式代替png刹缝、jpeg格式。都不能減少內(nèi)存颈将,只能縮減包大小或者提升網(wǎng)絡傳輸速度梢夯,網(wǎng)絡圖片:使用網(wǎng)絡裁剪服務,獲取適當?shù)膱D片加載晴圾。

4颂砸,圖片來源是 res 內(nèi)的不同資源目錄時,系統(tǒng)會根據(jù)設(shè)備當前的 dpi 值以及資源目錄所對應的 dpi 值死姚,做一次分辨率轉(zhuǎn)換人乓,規(guī)則如下:新分辨率 = 原圖橫向分辨率 * (設(shè)備的 dpi / 目錄對應的 dpi ) * 原圖縱向分辨率 * (設(shè)備的 dpi / 目錄對應的 dpi )。其他圖片的來源都毒,如磁盤色罚,文件,流等账劲,均按照原圖的分辨率來進行計算圖片的內(nèi)存大小戳护。

5,當使用 Glide 時涤垫,如果有設(shè)置圖片顯示的控件姑尺,那么會自動按照控件的大小竟终,降低圖片的分辨率加載蝠猬。圖片來源是 res 的分辨率轉(zhuǎn)換規(guī)則對它也無效。

1.3.2 優(yōu)化策略

(1)统捶、降低分辨率
降低分辨率一般使用 Glide去加載圖片就好了榆芦,它會按照控件大小降低圖片分辨率柄粹。其內(nèi)部也是使用了inSampleSize。
(2)降低圖片像素點大小匆绣,沒有透明通道驻右,可使用RGB_565(2B)
(3)drawable適配
在做圖片的兼容性時,如果只想使用一張圖片崎淳,則應使用3倍甚至4倍的圖片(3倍是主流機型堪夭,但在4倍手機上會被放大,圖片可能失真)拣凹,這樣在低分辨率的手機上森爽,不僅顯示清晰,而且系統(tǒng)會自動進行縮放嚣镜,從而確保占用較小的內(nèi)存爬迟。
(4) 配合onTrimMemory或者onLowMemory,在系統(tǒng)內(nèi)存緊張的時候使用glide清理圖片緩存

1.3.2 inSampleSize

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {
    // 先將inJustDecodeBounds設(shè)置為true來獲取圖片的長寬屬性
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // 計算inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // 加載壓縮版圖片
    options.inJustDecodeBounds = false;
    // 根據(jù)具體情況選擇具體的解碼方法
    return BitmapFactory.decodeResource(res, resId, options);
}

1菊匿、定義一個BitmapFactory.Options的一個變量付呕,然后把這個變量的inJustDecodeBounds屬性設(shè)置為true,去加載圖片跌捆,這個時候不會真正去加載圖片徽职,但是可以獲取到圖片的寬高;
2佩厚、然后結(jié)合我們需要的寬高活箕,計算一個采樣率;
3可款、最后將inJustDecodeBounds設(shè)置為false育韩,按照采樣率去真正的加載圖片。

1.4闺鲸、內(nèi)存對象的重復利用

1)資源復用:通用的字符串筋讨、顏色定義、簡單頁面布局的復用摸恍。
2)視圖復用:ListView/RecycleView替代scrollview悉罕。
3)Bitmap對象的復用,http://www.reibang.com/p/45fbef7a58ba
4)使用memory profiler 去檢測內(nèi)存中是否可重復利用的對象立镶;
(使用memory profiler 觀察內(nèi)存的使用情況壁袄,在內(nèi)存上升和抖動的時間段可能會有大量重復對象的創(chuàng)建,可取出對應時間段的內(nèi)存進行查找是否有大量重復對象的創(chuàng)建媚媒,并可結(jié)合代碼分析是否可是重復利用這些對象)

1.5嗜逻、使用優(yōu)化的數(shù)據(jù)結(jié)構(gòu)(內(nèi)存和速度平衡)

1.6、線程池管理線程

1.7缭召、多進程栈顷,比如拍攝模塊

2逆日、ui優(yōu)化

2.1、分析布局萄凤,減少布局嵌套或者替換消耗性能少的布局(FrameLayout室抽,constraintlayout)

2.2、使用include+merge減少布局層級

2.3靡努、使用viewstub提供按需加載

2.4坪圾、復用系統(tǒng)自帶的資源 winow background

3、網(wǎng)絡優(yōu)化

3.1惑朦、網(wǎng)絡速度:

正常一條網(wǎng)絡請求需要經(jīng)過的流程是這樣:
(1)神年、DNS 解析,請求DNS服務器行嗤,獲取域名對應的 IP 地址已日;
(2)、與服務端建立連接栅屏,包括 tcp 三次握手飘千,安全協(xié)議同步流程;
(3)栈雳、連接建立完成护奈,發(fā)送和接收數(shù)據(jù),解碼數(shù)據(jù)哥纫。

這里有明顯的三個優(yōu)化點:

3.1霉旗、IP直連或者HTTPDNS;
3.2蛀骇、開啟 keep-alive進行連接復用厌秒;
3.3、減少請求和返回數(shù)據(jù)的大猩勉尽鸵闪;

(1)、請求和返回數(shù)據(jù)做Gzip壓縮暑诸;
(2)蚌讼、精簡數(shù)據(jù)格式;
(3)个榕、圖片:使用webp圖片格式代替png篡石、jpeg;按需加載圖片(圖片裁剪西采,也可按網(wǎng)絡狀態(tài)返回不同分辨率的圖片)凰萨;

3.4、數(shù)據(jù)緩存(h5和na共享作品數(shù)據(jù),視頻緩存)沟蔑;

3.2湿诊、網(wǎng)絡優(yōu)化補充:

3.4狱杰、斷點續(xù)傳瘦材。
3.5、請求打包仿畸;(如埋點統(tǒng)計)
3.6食棕、協(xié)議優(yōu)化;(如使用優(yōu)化的的更好的http2错沽,請求頭壓縮)

對請求數(shù)據(jù)進行Gzip壓縮

 static class GzipRequestInterceptor implements Interceptor {
  @Override public Response intercept(Chain chain) throws IOException {
      Request originalRequest = chain.request();
      if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
        return chain.proceed(originalRequest);
      }

      Request compressedRequest = originalRequest.newBuilder()
          .header("Content-Encoding", "gzip")
          .method(originalRequest.method(), gzip(originalRequest.body()))
          .build();
      return chain.proceed(compressedRequest);
    }

    private RequestBody gzip(final RequestBody body) {
      return new RequestBody() {
        @Override public MediaType contentType() {
          return body.contentType();
        }

        @Override public long contentLength() {
          return -1; // 無法知道壓縮后的數(shù)據(jù)大小
        }

        @Override public void writeTo(BufferedSink sink) throws IOException {
          BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
          body.writeTo(gzipSink);
          gzipSink.close();
        }
      };
    }
  }

4簿晓、啟動優(yōu)化

4.1、異步加載一:Application中加入異步線程

4.2千埃、異步加載二:MainActivity中加入異步線程

4.3憔儿、 延遲加載功能:首屏繪制完成之后加載

4.4、動態(tài)加載布局:主布局文件優(yōu)化

4.5放可、主布局文件深度優(yōu)化

4.6谒臼、 功能代碼深度優(yōu)化

4.7、一個重點是天降紅包(離線插件+數(shù)據(jù)預加載)

5.參考
1耀里,https://juejin.cn/post/6901487965562732551
2蜈缤,https://juejin.cn/post/6844903693230276616
3,http://www.reibang.com/p/a9d861732445
4冯挎,https://juejin.cn/post/6844904093786308622
5底哥,http://www.reibang.com/p/9c897b41893d
6,啟動優(yōu)化房官,https://juejin.cn/post/6844904093786308622#heading-148
7趾徽,https://www.cnblogs.com/yongdaimi/p/11120590.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市翰守,隨后出現(xiàn)的幾起案子附较,更是在濱河造成了極大的恐慌,老刑警劉巖潦俺,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拒课,死亡現(xiàn)場離奇詭異,居然都是意外死亡事示,警方通過查閱死者的電腦和手機早像,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肖爵,“玉大人卢鹦,你說我怎么就攤上這事。” “怎么了冀自?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵揉稚,是天一觀的道長。 經(jīng)常有香客問我熬粗,道長搀玖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任驻呐,我火速辦了婚禮灌诅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘含末。我一直安慰自己猜拾,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布佣盒。 她就那樣靜靜地躺著挎袜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肥惭。 梳的紋絲不亂的頭發(fā)上盯仪,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音务豺,去河邊找鬼磨总。 笑死,一個胖子當著我的面吹牛笼沥,可吹牛的內(nèi)容都是我干的蚪燕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奔浅,長吁一口氣:“原來是場噩夢啊……” “哼馆纳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汹桦,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鲁驶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后舞骆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钥弯,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年督禽,在試婚紗的時候發(fā)現(xiàn)自己被綠了脆霎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡狈惫,死狀恐怖睛蛛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤忆肾,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布荸频,位于F島的核電站,受9級特大地震影響客冈,放射性物質(zhì)發(fā)生泄漏旭从。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一郊酒、第九天 我趴在偏房一處隱蔽的房頂上張望遇绞。 院中可真熱鬧键袱,春花似錦燎窘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至澜汤,卻和暖如春蚜迅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俊抵。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工谁不, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人徽诲。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓刹帕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谎替。 傳聞我的和親對象是個殘疾皇子偷溺,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容