安卓?jī)?nèi)存優(yōu)化案例一

安卓?jī)?nèi)存優(yōu)化是一個(gè)很重要的話題艳馒,有很多方面可以考慮诊笤,比如避免內(nèi)存泄漏兵迅、減少內(nèi)存抖動(dòng)幔虏、優(yōu)化圖片加載媚赖、使用緩存和對(duì)象池等矫限。下面我舉一些代碼案例催式,分別展示不合適的寫法和高性能的寫法徽曲。
歡迎評(píng)論區(qū)留言指正和補(bǔ)充临庇。

1. 避免使用枚舉類型反璃。

枚舉類型會(huì)占用更多的內(nèi)存昵慌,因?yàn)樗且粋€(gè)類對(duì)象,而不是一個(gè)基本類型淮蜈。如果需要定義一些常量斋攀,可以使用 static final int 或者 @IntDef 注解來代替。例如:

// 不合適的寫法
public enum Color {
    RED, GREEN, BLUE
}

// 高性能的寫法
public static final int RED = 0;
public static final int GREEN = 1;
public static final int BLUE = 2;

@IntDef({RED, GREEN, BLUE})
@Retention(RetentionPolicy.SOURCE)
public @interface Color {}

這樣做可以節(jié)省內(nèi)存空間梧田,因?yàn)槊杜e類型會(huì)占用至少4個(gè)字節(jié)淳蔼,而 int 類型只占用4個(gè)字節(jié)。另外裁眯,使用注解可以保證類型安全和編譯時(shí)檢查鹉梨。

測(cè)試枚舉、數(shù)組和整型數(shù)本身占用內(nèi)存的大小

//測(cè)試代碼
public int type_int_a = 16;

public int type_int_b = 20;

public int[] type_int_array_a = {1,2,2};

public enum TypeEnumD{
    first,
    second,
    third,
}

public TypeEnumD f = TypeEnumD.first;
public TypeEnumD s= TypeEnumD.second;
public TypeEnumD t = TypeEnumD.third;

測(cè)試結(jié)果


枚舉未状、數(shù)組和整型數(shù)本身大小.png

2. 避免在循環(huán)中創(chuàng)建對(duì)象俯画。

這會(huì)導(dǎo)致內(nèi)存抖動(dòng)和頻繁的GC,影響性能和用戶體驗(yàn)司草。如果需要在循環(huán)中使用對(duì)象艰垂,可以在循環(huán)外創(chuàng)建并復(fù)用,或者使用對(duì)象池來管理對(duì)象的生命周期埋虹。例如:

// 不合適的寫法
for (int i = 0; i < 100; i++) {
    String s = new String("Hello"); // 每次循環(huán)都會(huì)創(chuàng)建一個(gè)新的字符串對(duì)象
    // do something with s
}

// 高性能的寫法
String s = new String("Hello"); // 在循環(huán)外創(chuàng)建一個(gè)字符串對(duì)象
for (int i = 0; i < 100; i++) {
    // do something with s
}

這樣做可以減少內(nèi)存分配和回收的次數(shù)猜憎,提高性能。如果對(duì)象的創(chuàng)建和銷毀成本較高搔课,可以考慮使用對(duì)象池來緩存和復(fù)用對(duì)象胰柑,例如 BitmapPool

3. 避免使用 String 連接符 + 來拼接字符串。

這會(huì)產(chǎn)生很多臨時(shí)的字符串對(duì)象爬泥,占用內(nèi)存空間柬讨,并觸發(fā)GC。如果需要拼接字符串袍啡,可以使用 StringBuilder 或者 StringBuffer 來代替踩官。例如:

// 不合適的寫法
String s = "Hello" + "World" + "!" // 這會(huì)創(chuàng)建三個(gè)字符串對(duì)象

// 高性能的寫法
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append("World");
sb.append("!");
String s = sb.toString(); // 這只會(huì)創(chuàng)建一個(gè)字符串對(duì)象

這樣做可以避免不必要的字符串對(duì)象的創(chuàng)建,節(jié)省內(nèi)存空間境输,并提高字符串拼接的效率蔗牡。

4. 避免使用 System.gc() 來主動(dòng)觸發(fā)GC。

這會(huì)影響系統(tǒng)的自動(dòng)內(nèi)存管理機(jī)制嗅剖,并可能導(dǎo)致應(yīng)用卡頓或者OOM辩越。如果需要釋放內(nèi)存,可以通過合理地設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)和算法來減少內(nèi)存占用信粮,并及時(shí)釋放不再使用的對(duì)象的引用黔攒。例如:

// 不合適的寫法
System.gc(); // 強(qiáng)制調(diào)用GC

// 高性能的寫法
list.clear(); // 清空列表中的元素,并釋放引用
list = null; // 將列表對(duì)象置為null,讓GC自動(dòng)回收

這樣做可以讓系統(tǒng)根據(jù)內(nèi)存情況自動(dòng)調(diào)整GC策略亏钩,并避免不必要的GC開銷莲绰。

5. 避免在 onDraw() 方法中創(chuàng)建對(duì)象。

這會(huì)導(dǎo)致每次繪制都會(huì)分配內(nèi)存姑丑,造成內(nèi)存抖動(dòng)和GC蛤签。如果需要在 onDraw() 方法中使用對(duì)象,可以在構(gòu)造方法或者 onSizeChanged() 方法中創(chuàng)建并復(fù)用栅哀,或者使用靜態(tài)常量來代替震肮。例如:

// 不合適的寫法
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint(); // 每次繪制都會(huì)創(chuàng)建一個(gè)畫筆對(duì)象
    paint.setColor(Color.RED);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint);
}

// 高性能的寫法
private Paint paint; // 在類中聲明一個(gè)畫筆對(duì)象

public MyView(Context context) {
    super(context);
    paint = new Paint(); // 在構(gòu)造方法中創(chuàng)建畫筆對(duì)象,并設(shè)置顏色
    paint.setColor(Color.RED);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint); // 復(fù)用畫筆對(duì)象
}

6. 避免使用 HashMap 來存儲(chǔ)少量的鍵值對(duì)留拾。

HashMap 的內(nèi)部實(shí)現(xiàn)需要維護(hù)一個(gè)數(shù)組和一個(gè)鏈表戳晌,會(huì)占用較多的內(nèi)存空間,并且可能導(dǎo)致內(nèi)存碎片痴柔。如果只需要存儲(chǔ)少量的鍵值對(duì)沦偎,可以使用 ArrayMap 或者 SparseArray 來代替。例如:

// 不合適的寫法
HashMap<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);

// 高性能的寫法
ArrayMap<String, Integer> map = new ArrayMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);

這樣做可以節(jié)省內(nèi)存空間咳蔚,因?yàn)?ArrayMapSparseArray 的內(nèi)部實(shí)現(xiàn)是使用兩個(gè)數(shù)組來存儲(chǔ)鍵和值豪嚎,沒有額外的開銷。另外谈火,它們還可以避免 HashMap 的擴(kuò)容和哈希沖突的問題侈询。

7. 避免使用 setXxx() 方法來設(shè)置視圖的屬性。

這會(huì)導(dǎo)致視圖的重新布局和重繪糯耍,消耗CPU和內(nèi)存資源扔字,并可能導(dǎo)致卡頓。如果需要?jiǎng)討B(tài)改變視圖的屬性温技,可以使用屬性動(dòng)畫來實(shí)現(xiàn)革为。例如:

// 不合適的寫法
view.setAlpha(0.5f); // 設(shè)置視圖的透明度,會(huì)觸發(fā)視圖的重繪

// 高性能的寫法
ObjectAnimator.ofFloat(view, "alpha", 0.5f).start(); // 使用屬性動(dòng)畫來設(shè)置視圖的透明度舵鳞,不會(huì)觸發(fā)視圖的重繪

這樣做可以避免不必要的視圖更新篷角,提高動(dòng)畫效果和流暢度。

8. 避免在 onCreate() 方法中初始化不必要的對(duì)象系任。

這會(huì)導(dǎo)致應(yīng)用啟動(dòng)時(shí)間變長(zhǎng),影響用戶體驗(yàn)虐块,并可能導(dǎo)致ANR俩滥。如果有些對(duì)象不需要在啟動(dòng)時(shí)就初始化,可以延遲到使用時(shí)再初始化贺奠,或者放到子線程中初始化霜旧。例如:

// 不合適的寫法
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   OkHttpClient client = new OkHttpClient(); // 在啟動(dòng)時(shí)就創(chuàng)建一個(gè)網(wǎng)絡(luò)客戶端對(duì)象,占用內(nèi)存空間,并可能影響啟動(dòng)速度
}

// 高性能的寫法
private OkHttpClient client; // 在類中聲明一個(gè)網(wǎng)絡(luò)客戶端對(duì)象

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
}

private OkHttpClient getClient() {
   if (client == null) {
       client = new OkHttpClient(); // 在需要使用時(shí)才創(chuàng)建網(wǎng)絡(luò)客戶端對(duì)象挂据,節(jié)省內(nèi)存空間以清,并提高啟動(dòng)速度

9. 避免使用 findViewById() 方法來查找視圖。

這會(huì)導(dǎo)致每次查找都會(huì)遍歷視圖樹崎逃,消耗CPU和內(nèi)存資源掷倔,并可能導(dǎo)致卡頓。如果需要使用視圖个绍,可以在 onCreate() 方法中使用 findViewById() 方法來獲取并保存到變量中勒葱,或者使用 ViewBinding 或者 ButterKnife 等庫來自動(dòng)綁定視圖。例如:

// 不合適的寫法
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

@Override
protected void onResume() {
    super.onResume();
    TextView textView = findViewById(R.id.text_view); // 每次調(diào)用都會(huì)查找視圖樹巴柿,影響性能
    textView.setText("Hello World");
}

// 高性能的寫法
private TextView textView; // 在類中聲明一個(gè)視圖變量

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textView = findViewById(R.id.text_view); // 在啟動(dòng)時(shí)就獲取并保存視圖對(duì)象凛虽,避免重復(fù)查找
}

@Override
protected void onResume() {
    super.onResume();
    textView.setText("Hello World"); // 復(fù)用視圖對(duì)象
}

這樣做可以避免不必要的視圖查找,提高性能和流暢度广恢。

10. 避免使用 VectorDrawable 來顯示矢量圖形凯旋。

VectorDrawable 的內(nèi)部實(shí)現(xiàn)是使用 Path 來繪制矢量圖形,這會(huì)消耗較多的CPU和內(nèi)存資源钉迷,并可能導(dǎo)致卡頓至非。如果需要顯示矢量圖形,可以使用 SVG 或者 WebP 等格式來代替篷牌。例如:

// 不合適的寫法
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,4c4.41,0 8,3.59 8,8s-3.59,8 -8,8 -8,-3.59 -8,-8 3.59,-8 8,-8zM6.5,9L10,12.5l-3.5,3.5L8,16l5,-5 -5,-5L6.5,9zM14,13h4v-2h-4v2z" />
</vector>

// 高性能的寫法
<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_arrow_forward_24px.webp" /> // 使用WebP格式的圖片來顯示矢量圖形睡蟋,節(jié)省CPU和內(nèi)存資源,并提高繪制效率

這樣做可以避免不必要的矢量圖形繪制枷颊,提高性能和流暢度戳杀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市夭苗,隨后出現(xiàn)的幾起案子信卡,更是在濱河造成了極大的恐慌,老刑警劉巖题造,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件傍菇,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡界赔,警方通過查閱死者的電腦和手機(jī)丢习,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淮悼,“玉大人咐低,你說我怎么就攤上這事⊥嘈龋” “怎么了见擦?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我鲤屡,道長(zhǎng)损痰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任酒来,我火速辦了婚禮卢未,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘役首。我一直安慰自己尝丐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布衡奥。 她就那樣靜靜地躺著爹袁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矮固。 梳的紋絲不亂的頭發(fā)上失息,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音档址,去河邊找鬼盹兢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛守伸,可吹牛的內(nèi)容都是我干的绎秒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼尼摹,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼见芹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蠢涝,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤玄呛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后和二,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體徘铝,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年惯吕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惕它。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡废登,死狀恐怖怠缸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钳宪,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站吏颖,受9級(jí)特大地震影響搔体,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜半醉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一疚俱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缩多,春花似錦呆奕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至逊抡,卻和暖如春姆泻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冒嫡。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工拇勃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人孝凌。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓方咆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蟀架。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓣赂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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