Android のメモリ最適化ケースの網羅的なリスト

Android のメモリの最適化は非常に重要なトピックであり、メモリ リークの回避、メモリ ジッターの削減、画像読み込みの最適化、キャッシュとオブジェクト プールの使用など、考慮すべき多くの側面があります。以下に、不適切な記述方法と高パフォーマンスの記述方法を示すコード例をいくつか示します。
修正や追加をコメント欄に残していただければ幸いです。

1. 列挙型の使用を避けます。

列挙型はプリミティブ型ではなくクラス オブジェクトであるため、より多くのメモリを消費します。いくつかの定数を定義する必要がある場合は、代わりに静的な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 {}

これを行うと、列挙型は少なくとも 4 バイトを占有するのに対し、int 型は 2 バイトしか占有しないため、メモリ領域を節約できます。さらに、アノテーションを使用すると、型の安全性とコンパイル時のチェックを保証できます。

2. ループ内でオブジェクトを作成しないようにします。

これにより、メモリのスラッシングや頻繁な GC が発生し、パフォーマンスとユーザー エクスペリエンスに影響を与えます。ループ内でオブジェクトを使用する必要がある場合は、ループの外でオブジェクトを作成して再利用するか、オブジェクト プールを使用してオブジェクトのライフ サイクルを管理できます。例えば:

// 不合适的写法
for (int i = 0; i < 100; i++) {
    String s = new String("Hello"); // 每次循环都会创建一个新的字符串对象
    // do something with s
}

// 高性能的写法
String s = new String("Hello"); // 在循环外创建一个字符串对象
for (int i = 0; i < 100; i++) {
    // do something with s
}

そうすることで、メモリの割り当てとリサイクルの回数が減り、パフォーマンスが向上します。オブジェクトの作成と破棄のコストが高い場合は、BitmapPool などのオブジェクト プールを使用してオブジェクトをキャッシュして再利用することを検討できます。

3. 文字列を連結するために String 連結子 + を使用しないでください。

これにより、大量の一時文字列オブジェクトが生成され、メモリ領域が占有され、GC がトリガーされます。文字列を連結する必要がある場合は、代わりに StringBuilder または StringBuffer を使用できます。例えば:

// 不合适的写法
String s = "Hello" + "World" + "!" // 这会创建三个字符串对象

// 高性能的写法
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append("World");
sb.append("!");
String s = sb.toString(); // 这只会创建一个字符串对象

そうすることで、文字列オブジェクトの不必要な作成を回避し、メモリ領域を節約し、文字列連結の効率を向上させることができます。

4. System.gc() を使用して GC をアクティブにトリガーすることは避けてください。

これはシステムの自動メモリ管理メカニズムに影響を与え、アプリケーションの遅延や OOM を引き起こす可能性があります。メモリを解放する必要がある場合は、データ構造とアルゴリズムを適切に設計することでメモリの使用量を削減し、使用されなくなったオブジェクトへの参照を速やかに解放できます。例えば:

// 不合适的写法
System.gc(); // 强制调用GC

// 高性能的写法
list.clear(); // 清空列表中的元素,并释放引用
list = null; // 将列表对象置为null,让GC自动回收

これにより、システムはメモリ状況に基づいて GC 戦略を自動的に調整し、不必要な GC オーバーヘッドを回避できます。

5. onDraw() メソッドでオブジェクトを作成しないようにします。

これにより、描画するたびにメモリが割り当てられ、メモリ スラッシングや GC が発生します。onDraw() メソッドでオブジェクトを使用する必要がある場合は、コンストラクターまたは onSizeChanged() メソッドでオブジェクトを作成して再利用するか、代わりに静的定数を使用できます。例えば:

// 不合适的写法
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint(); // 每次绘制都会创建一个画笔对象
    paint.setColor(Color.RED);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint);
}

// 高性能的写法
private Paint paint; // 在类中声明一个画笔对象

public MyView(Context context) {
    super(context);
    paint = new Paint(); // 在构造方法中创建画笔对象,并设置颜色
    paint.setColor(Color.RED);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint); // 复用画笔对象
}

これにより、描画プロセス中にオブジェクトの頻繁な作成とリサイクルが回避され、描画の効率と滑らかさが向上します。

6. 少数のキーと値のペアを保存するために HashMap を使用することは避けてください。

HashMap の内部実装では配列とリンク リストを維持する必要があるため、より多くのメモリ領域が必要になり、メモリの断片化が発生する可能性があります。少数のキーと値のペアのみを保存する必要がある場合は、代わりに 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);

これを行うと、ArrayMap と SparseArray の内部実装が 2 つの配列を使用してキーと値を格納し、追加のオーバーヘッドが発生しないため、メモリ領域が節約されます。さらに、HashMap の拡張やハッシュの衝突の問題を回避できます。

7. ビューのプロパティを設定するために setXxx() メソッドを使用することは避けてください。

これにより、ビューの再レイアウトと再描画が発生し、CPU とメモリのリソースが消費され、遅延が発生する可能性があります。ビューのプロパティを動的に変更する必要がある場合は、プロパティ アニメーションを使用してこれを実現できます。例えば:

// 不合适的写法
view.setAlpha(0.5f); // 设置视图的透明度,会触发视图的重绘

// 高性能的写法
ObjectAnimator.ofFloat(view, "alpha", 0.5f).start(); // 使用属性动画来设置视图的透明度,不会触发视图的重绘

これにより、不必要なビューの更新が回避され、アニメーションの効果と滑らかさが向上します。

8. onCreate() メソッドで不要なオブジェクトを初期化しないようにします。

これにより、アプリケーションの起動に時間がかかり、ユーザー エクスペリエンスに影響を及ぼし、ANR が発生する可能性があります。一部のオブジェクトを起動時に初期化する必要がない場合は、子スレッドで使用または初期化されるまで遅延させることができます。例えば:

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

   OkHttpClient client = new OkHttpClient(); // 在启动时就创建一个网络客户端对象,占用内存空间,并可能影响启动速度
}

// 高性能的写法
private OkHttpClient client; // 在类中声明一个网络客户端对象

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

private OkHttpClient getClient() {
   if (client == null) {
       client = new OkHttpClient(); // 在需要使用时才创建网络客户端对象,节省内存空间,并提高启动速度

9. ビューを検索するために findViewById() メソッドを使用することは避けてください。

これにより、各ルックアップがビュー ツリーを横断することになり、CPU とメモリ リソースが消費され、遅延が発生する可能性があります。ビューを使用する必要がある場合は、onCreate() メソッドの findViewById() メソッドを使用してビューを取得して変数に保存するか、ViewBinding や ButterKnife などのライブラリを使用してビューを自動的にバインドします。例えば:

// 不合适的写法
@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); // 每次调用都会查找视图树,影响性能
    textView.setText("Hello World");
}

// 高性能的写法
private TextView textView; // 在类中声明一个视图变量

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textView = findViewById(R.id.text_view); // 在启动时就获取并保存视图对象,避免重复查找
}

@Override
protected void onResume() {
    super.onResume();
    textView.setText("Hello World"); // 复用视图对象
}

そうすることで、不必要なビューの検索が回避され、パフォーマンスとスムーズさが向上します。
###10. ベクター グラフィックを表示するために VectorDrawable を使用することは避けてください。
VectorDrawable の内部実装では、Path を使用してベクター グラフィックスを描画します。これにより、より多くの CPU リソースとメモリ リソースが消費され、遅延が発生する可能性があります。ベクター グラフィックを表示する必要がある場合は、代わりに 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格式的图片来显示矢量图形,节省CPU和内存资源,并提高绘制效率

そうすることで、ベクター グラフィックスの不要な描画を回避し、パフォーマンスと滑らかさを向上させることができます。

11. 非同期タスクを実行するために AsyncTask を使用することは避けてください。

AsyncTask の内部実装では、スレッド プールとメッセージ キューを使用してタスクを管理します。これによりメモリ領域が占有され、メモリ リークや同時実行の問題が発生する可能性があります。非同期タスクを実行する必要がある場合は、代わりに RxJava や Coroutine などのライブラリを使用できます。例えば:

// 不合适的写法
private class MyTask extends AsyncTask<Void, Void, String> {

    private WeakReference<Context> contextRef;

    public MyTask(Context context) {
        contextRef = new WeakReference<>(context);
    }

    @Override
    protected String doInBackground(Void... params) {
        // do some background work
        return "result";
    }

    @Override
    protected void onPostExecute(String result) {
        Context context = contextRef.get();
        if (context != null) {
            // do something with result and context
        }
    }
}

// 高性能的写法
private fun doAsyncTask(context: Context) {
    CoroutineScope(Dispatchers.IO).launch {
        // do some background work
        val result = "result"
        withContext(Dispatchers.Main) {
            // do something with result and context
        }
    }
}

そうすることで、不必要なメモリの割り当てと割り当て解除が回避され、メモリ リークと同時実行性の問題が回避され、非同期タスクの管理とスケジューリングが改善されます。

###12. 画像の読み込みに BitmapFactory を使用することは避けてください。
BitmapFactory の内部実装は、nativeDecodeStream メソッドを使用して画像をデコードします。これにより、より多くのメモリ領域が消費され、OOM が発生する可能性があります。画像を読み込む必要がある場合は、代わりに Glide や Picasso などのライブラリを使用できます。例えば:

// 不合适的写法
ImageView imageView = findViewById(R.id.image_view);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image); // 这会创建一个原始大小的位图对象,占用内存空间,并可能导致OOM
imageView.setImageBitmap(bitmap);

// 高性能的写法
ImageView imageView = findViewById(R.id.image_view);
Glide.with(this).load(R.drawable.image).into(imageView); // 这会根据视图的大小和屏幕密度来加载合适大小的位图对象,节省内存空间,并避免OOM

そうすることで、不要なビットマップ オブジェクトの作成を回避し、メモリ領域を節約し、画像読み込みの効率と品質を向上させることができます。

13. シリアル化を実装するために Serializable インターフェイスを使用することは避けてください。

Serializable インターフェイスの内部実装では、リフレクション メカニズムを使用してオブジェクトをシリアル化および逆シリアル化します。これにより、より多くの CPU リソースとメモリ リソースが消費され、パフォーマンスの低下が発生する可能性があります。シリアル化を実装する必要がある場合は、代わりに Parcelable インターフェイスまたは ProtoBuf などのライブラリを使用できます。例えば:

// 不合适的写法
public class User implements Serializable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getter and setter methods
}

// 高性能的写法
public class User implements Parcelable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // getter and setter methods

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }

    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {

        @Override
        public User createFromParcel(Parcel source) {
            return new User(source.readString(), source.readInt());
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}

そうすることで、不必要なリフレクション操作を回避し、CPU とメモリのリソースを節約し、シリアル化と逆シリアル化の効率を向上させることができます。

14. データの保存に LinkedList を使用しないでください。

LinkedList の内部実装では、二重リンク リストを使用してデータを保存します。これにより、より多くのメモリ領域が占有され、メモリの断片化が発生する可能性があります。データを保存する必要がある場合は、代わりに ArrayList または ArrayDeque を使用できます。例えば:

// 不合适的写法
LinkedList<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add("c");

// 高性能的写法
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");

これを行うと、ArrayList と ArrayDeque の内部実装が配列を使用してデータを保存するため、追加のオーバーヘッドが発生せずにメモリ領域が節約されます。さらに、より高速なランダム アクセスと反復パフォーマンスが提供されます。

15. StringTokenizer を使用して文字列を分割することは避けてください。

StringTokenizer の内部実装は文字配列を使用して文字列を格納し、 nextToken() メソッドが呼び出されるたびに新しい文字列オブジェクトが作成され、より多くの CPU リソースとメモリ リソースを消費し、GC が発生する可能性があります。文字列を分割する必要がある場合は、代わりに Split() メソッドまたは Scanner クラスを使用できます。例えば:

// 不合适的写法
StringTokenizer st = new StringTokenizer("Hello World!");
while (st.hasMoreTokens()) {
    String token = st.nextToken(); // 每次调用都会创建一个新的字符串对象
    // do something with token
}

// 高性能的写法
String[] tokens = "Hello World!".split(" "); // 这只会创建一个字符串数组对象
for (String token : tokens) {
    // do something with token
}

そうすることで、文字列オブジェクトの不必要な作成を回避し、CPU リソースとメモリ リソースを節約し、文字列分割の効率を向上させることができます。

16. 日付と時刻の書式設定に SimpleDateFormat を使用しないでください。

SimpleDateFormat の内部実装では、Calendar オブジェクトを使用して日付と時刻を保存します。format() メソッドが呼び出されるたびに、新しい Date オブジェクトが作成されます。これにより、より多くの CPU リソースとメモリ リソースが消費され、GC が発生する可能性があります。日付と時刻をフォーマットする必要がある場合は、代わりに DateTimeFormatter や FastDateFormat などのライブラリを使用できます。例えば:

// 不合适的写法
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String formattedDate = sdf.format(date); // 每次调用都会创建一个新的日期对象

// 高性能的写法
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime date = LocalDateTime.now();
String formattedDate = dtf.format(date); // 这不会创建任何新的对象

これにより、不要な日付オブジェクトの作成が回避され、CPU とメモリのリソースが節約され、日付と時刻の書式設定の効率が向上します。

17. スパース行列の保存に SparseIntArray を使用することは避けてください。

SparseIntArray の内部実装では、2 つの配列を使用してキーと値を格納します。これにより、より多くのメモリ領域が占有され、配列の拡張やコピーが発生する可能性があります。スパース行列を保存する必要がある場合は、代わりに SparseMatrix や EJML などのライブラリを使用できます。例えば:

// 不合适的写法
SparseIntArray matrix = new SparseIntArray();
matrix.put(0, 1);
matrix.put(1, 2);
matrix.put(2, 3);

// 高性能的写法
SparseMatrix matrix = new SparseMatrix(3, 3);
matrix.set(0, 0, 1);
matrix.set(1, 1, 2);
matrix.set(2, 2, 3);

そうすることで、SparseMatrix と EJML の内部実装ではリンク リストまたはハッシュ テーブルを使用して非ゼロ要素を格納し、追加のオーバーヘッドが発生しないため、メモリ領域を節約できます。さらに、行列演算と転置のパフォーマンスが向上します。

18. JSON 文字列の解析に JSONObject を使用しないようにします。

JSONObject の内部実装では、HashMap を使用してキーと値のペアを保存します。これにより、より多くのメモリ領域が占有され、ハッシュの競合や拡張が発生する可能性があります。JSON 文字列を解析する必要がある場合は、代わりに Gson や Moshi などのライブラリを使用できます。例えば:

// 不合适的写法
String json = "{\"name\":\"Alice\",\"age\":18}";
JSONObject jsonObject = new JSONObject(json); // 这会创建一个哈希表对象,占用内存空间,并可能导致哈希冲突和扩容
String name = jsonObject.getString("name");
int age = jsonObject.getInt("age");

// 高性能的写法
String json = "{\"name\":\"Alice\",\"age\":18}";
Gson gson = new Gson();
User user = gson.fromJson(json, User.class); // 这会直接创建一个用户对象,节省内存空间,并提高JSON解析的效率
String name = user.getName();
int age = user.getAge();

そうすることで、ハッシュ テーブル オブジェクトの不必要な作成を回避し、メモリ領域を節約し、JSON 解析の効率と品質を向上させることができます。

19. 乱数を生成するために Random クラスを使用しないでください。

Random クラスの内部実装では、線形合同生成器を使用して乱数を生成します。その結果、乱数の品質が低くなり、同時実行性の問題が発生する可能性があります。乱数を生成する必要がある場合は、代わりに ThreadLocalRandom や SecureRandom などのクラスを使用できます。例えば:

// 不合适的写法
Random random = new Random();
int n = random.nextInt(10); // 这会生成一个不太随机的整数,并且可能导致并发问题

// 高性能的写法
int n = ThreadLocalRandom.current().nextInt(10); // 这会生成一个更随机的整数,并且避免并发问题

これにより、乱数の品質と安全性が向上し、同時実行性の問題が回避されます。

20. ログを出力するために Log クラスを使用しないでください。

Log クラスの内部実装では、PrintStream オブジェクトを使用してコンソールまたはファイルにログを出力します。これにより、より多くの CPU リソースとメモリ リソースが消費され、IO ブロックやパフォーマンスの低下が発生する可能性があります。ログを出力する必要がある場合は、代わりに Timber や Logger などのライブラリを使用できます。例えば:

// 不合适的写法
Log.d("TAG", "Hello World!"); // 这会输出一条日志到控制台或者文件,消耗CPU和内存资源,并且可能导致IO阻塞和性能下降

// 高性能的写法
Timber.d("Hello World!"); // 这会输出一条日志到控制台或者文件,节省CPU和内存资源,并提高日志输出的效率和质量

これにより、不必要な IO 操作が回避され、CPU とメモリのリソースが節約され、ログ出力の効率と品質が向上します。

静的内部クラスまたは弱い参照を使用して、非静的内部クラスが外部クラスへの参照を保持してメモリ リークが発生するのを回避します。

// 不合适的写法
public class MainActivity extends AppCompatActivity {

    private MyTask task;

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

        task = new MyTask();
        task.execute();
    }

    private class MyTask extends AsyncTask<Void, Void, Void> {
        // 这是一个非静态内部类,它会隐式地持有外部类的引用
        @Override
        protected Void doInBackground(Void... params) {
            // do some background work
            return null;
        }
    }
}

// 高性能的写法
public class MainActivity extends AppCompatActivity {

    private MyTask task;

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

        task = new MyTask(this);
        task.execute();
    }

    private static class MyTask extends AsyncTask<Void, Void, Void> {
        // 这是一个静态内部类,它不会持有外部类的引用
        private WeakReference<MainActivity> activityRef;

        public MyTask(MainActivity activity) {
            activityRef = new WeakReference<>(activity);
        }

        @Override
        protected Void doInBackground(Void... params) {
            // do some background work
            return null;
        }
    }
}

これを行うとメモリ リークを回避できます。これは、MyTask がバックグラウンドでまだ実行されているときに MainActivity が破棄された場合、非静的内部クラスでは MainActivity がリサイクルされませんが、静的内部クラスまたは弱い参照ではリサイクルされないためです。これによりメモリ領域が節約され、パフォーマンスが向上します。

シングルトン モードを使用する場合は、アクティビティがリサイクルされないように、アクティビティのコンテキストではなくアプリケーションのコンテキストを使用するように注意してください。

// 不合适的写法
public class MySingleton {

    private static MySingleton instance;
    private Context context;

    private MySingleton(Context context) {
        this.context = context;
    }

    public static MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context); // 这里使用了Activity的Context,会导致Activity无法被回收
        }
        return instance;
    }
}

// 高性能的写法
public class MySingleton {

    private static MySingleton instance;
    private Context context;

    private MySingleton(Context context) {
        this.context = context.getApplicationContext(); // 这里使用了Application的Context,不会导致Activity无法被回收
    }

    public static MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context);
        }
        return instance;
    }
}

これを行うと、MySingleton がまだコンテキストを使用している間にアクティビティが破棄された場合、アクティビティはリサイクルできませんが、アプリケーションのコンテキストはリサイクルできないため、メモリ リークを回避できます。これによりメモリ領域が節約され、パフォーマンスが向上します。

Proguard や R8 などのツールを使用してコードを難読化および圧縮し、メソッドの数とバイトコード サイズを削減します。例えば:

android {
  buildTypes {
    release {
      minifyEnabled true // 这里开启了代码混淆和压缩
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
      shrinkResources true // 这里开启了资源文件压缩
    }
  }
}

そうすることで、APK のサイズが削減され、アプリケーションのセキュリティと操作効率が向上します。

Lint ツールを使用して不要なリソース ファイルを検出して削除し、APK のサイズを削減します。例えば:

android {
  lintOptions {
    checkReleaseBuilds true // 这里开启了Lint检查
    abortOnError true // 这里设置了如果发现错误就终止编译
  }
}

そうすることで、APK のサイズが削減され、アプリケーションの操作効率と品質が向上します。

inBitmap オプションを使用すると、ビットマップ メモリ空間を再利用し、メモリ割り当てを削減できます。例えば:

// 不合适的写法
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options); // 这里没有使用inBitmap选项,会导致每次都分配新的内存空间

// 高性能的写法
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inBitmap = reusableBitmap; // 这里使用了inBitmap选项,会复用已有的内存空间,reusableBitmap是一个合适大小的位图对象
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);

そうすることで、メモリの割り当てとリサイクルの回数が減り、パフォーマンスと流暢性が向上します。

inSampleSize オプションを使用して画像を比例的に拡大縮小し、大きすぎる画像の読み込みを回避します。例えば:

// 不合适的写法
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options); // 这里没有使用inSampleSize选项,会加载原始大小的图片,占用内存空间,并可能导致OOM

// 高性能的写法
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.image, options); // 这里先获取图片的原始宽高,不加载图片到内存中
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1; // 这里根据需要计算一个合适的缩放比例,例如根据视图的大小和屏幕密度等因素
options.inJustDecodeBounds = false;
options.inSampleSize = inSampleSize; // 这里使用inSampleSize选项,会按比例缩放图片,节省内存空间,并避免OOM
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);

これにより、大きすぎる画像のロードを回避し、メモリ領域を節約し、画像のロード効率と品質を向上させることができます。

レイアウト ファイルを最適化し、レイアウト レベルと冗長なコントロールを削減し、include、merge、ViewStub、その他のタグを使用してレイアウトの再利用と読み込みの遅延を行います。例えば:

<!-- 不合适的写法 -->
<LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title" />

    <LinearLayout android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/icon" />

        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Content" />

    </LinearLayout>

</LinearLayout>

<!-- 高性能的写法 -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title" />

    <ImageView android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/icon" />

    <TextView android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Content" />

</merge>

これにより、レイアウト レベルと冗長なコントロールが削減され、レイアウトの読み込みとレンダリングの効率とスムーズさが向上します。レイアウトを再利用して遅延読み込みしたい場合は、include、merge、ViewStub、およびその他のタグを使用してこれを実現できます。

Androidの勉強メモ

Android パフォーマンス最適化の記事: https://qr18.cn/FVlo89
Android Framework の基本原則の記事: https://qr18.cn/AQpN4J
Android 車両の記事: https://qr18.cn/F05ZCM
Android リバース セキュリティ研究ノート: https://qr18.cn/CQ5TcL
Android オーディオとビデオの記事: https://qr18.cn/Ei3VPD
Jetpack ファミリー バケットの記事 (Compose を含む): https://qr18.cn/A0gajp
OkHttp ソース コード分析のメモ: https://qr18.cn/Cw0pBD
Kotlin の記事: https://qr18.cn/CdjtAF
Gradle の記事: https://qr18.cn/DzrmMB
Flutter の記事: https://qr18.cn/DIvKma
Android の 8 つの知識体系: https://qr18.cn/CyxarU
Android コア ノート:https://qr21.cn/CaZQLo
過去の Android 面接の質問: https://qr18.cn/CKV8OZ
2023 年の最新の Android 面接の質問: https://qr18.cn/CgxrRy
Android 車両開発職の面接演習:https://qr18.cn/FTlyCJ
音声およびビデオの面接の質問:https://qr18.cn/AcV6Ap

おすすめ

転載: blog.csdn.net/weixin_61845324/article/details/132887667
おすすめ