Una lista exhaustiva de casos de optimización de memoria de Android

La optimización de la memoria de Android es un tema muy importante y hay muchos aspectos a considerar, como evitar pérdidas de memoria, reducir la fluctuación de la memoria, optimizar la carga de imágenes, usar caché y grupos de objetos, etc. A continuación, daré algunos ejemplos de código para mostrar métodos de escritura inapropiados y métodos de escritura de alto rendimiento.
Bienvenido a dejar correcciones y adiciones en el área de comentarios.

1. Evite el uso de tipos de enumeración.

El tipo de enumeración ocupa más memoria porque es un objeto de clase en lugar de un tipo primitivo. Si necesita definir algunas constantes, puede utilizar la anotación estática final int o @IntDef. Por ejemplo:

// 不合适的写法
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 {}

Hacer esto puede ahorrar espacio en la memoria, porque el tipo de enumeración ocupará al menos 4 bytes, mientras que el tipo int solo ocupa 2 bytes. Además, el uso de anotaciones puede garantizar la seguridad de tipos y la verificación en tiempo de compilación.

2. Evite crear objetos en bucles.

Esto provocará problemas de memoria y GC frecuentes, lo que afectará el rendimiento y la experiencia del usuario. Si necesita utilizar objetos en un bucle, puede crearlos y reutilizarlos fuera del bucle, o utilizar un grupo de objetos para gestionar el ciclo de vida del objeto. Por ejemplo:

// 不合适的写法
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
}

Hacerlo puede reducir la cantidad de tiempos de reciclaje y asignación de memoria y mejorar el rendimiento. Si el costo de creación y destrucción de objetos es alto, puede considerar usar un grupo de objetos para almacenar en caché y reutilizar objetos, como BitmapPool.

3. Evite utilizar el concatenador de cadenas + para concatenar cadenas.

Esto generará una gran cantidad de objetos de cadena temporales, ocupará espacio de memoria y activará GC. Si necesita concatenar cadenas, puede utilizar StringBuilder o StringBuffer en su lugar. Por ejemplo:

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

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

Hacerlo puede evitar la creación innecesaria de objetos de cadena, ahorrar espacio en la memoria y mejorar la eficiencia de la concatenación de cadenas.

4. Evite utilizar System.gc() para activar GC de forma activa.

Esto afectará el mecanismo de administración automática de memoria del sistema y puede causar retrasos en la aplicación o OOM. Si necesita liberar memoria, puede reducir su uso diseñando correctamente estructuras de datos y algoritmos, y liberando rápidamente referencias a objetos que ya no se utilizan. Por ejemplo:

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

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

Hacerlo permite que el sistema ajuste automáticamente la estrategia de GC en función de las condiciones de la memoria y evite una sobrecarga innecesaria de GC.

5. Evite crear objetos en el método onDraw().

Esto hará que se asigne memoria cada vez que dibuje, lo que provocará una destrucción de la memoria y GC. Si necesita utilizar un objeto en el método onDraw(), puede crearlo y reutilizarlo en el constructor o en el método onSizeChanged(), o utilizar constantes estáticas en su lugar. Por ejemplo:

// 不合适的写法
@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); // 复用画笔对象
}

Esto puede evitar la creación y el reciclaje frecuentes de objetos durante el proceso de dibujo y mejorar la eficiencia y la suavidad del dibujo.

6. Evite el uso de HashMap para almacenar una pequeña cantidad de pares clave-valor.

La implementación interna de HashMap requiere mantener una matriz y una lista vinculada, lo que ocupa más espacio de memoria y puede causar fragmentación de la memoria. Si solo necesita almacenar una pequeña cantidad de pares clave-valor, puede usar ArrayMap o SparseArray en su lugar. Por ejemplo:

// 不合适的写法
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);

Hacer esto ahorra espacio en la memoria porque la implementación interna de ArrayMap y SparseArray utiliza dos matrices para almacenar claves y valores, sin gastos generales adicionales. Además, pueden evitar la expansión de HashMap y los problemas de colisión de hash.

7. Evite utilizar el método setXxx() para establecer las propiedades de la vista.

Esto hará que la vista se vuelva a diseñar y dibujar, consumiendo recursos de CPU y memoria, y puede causar retrasos. Si necesita cambiar dinámicamente las propiedades de una vista, puede utilizar la animación de propiedades para lograrlo. Por ejemplo:

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

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

Hacer esto puede evitar actualizaciones de vista innecesarias y mejorar los efectos y la suavidad de la animación.

8. Evite inicializar objetos innecesarios en el método onCreate().

Esto hará que la aplicación tarde más en iniciarse, afectará la experiencia del usuario y puede provocar ANR. Si algunos objetos no necesitan inicializarse al inicio, se pueden retrasar hasta su uso o inicializarse en un subproceso secundario. Por ejemplo:

// 不合适的写法
@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. Evite utilizar el método findViewById() para buscar vistas.

Esto hará que cada búsqueda atraviese el árbol de vista, consumiendo recursos de CPU y memoria, y puede provocar retrasos. Si necesita usar una vista, puede usar el método findViewById() en el método onCreate() para obtenerla y guardarla en una variable, o usar una biblioteca como ViewBinding o ButterKnife para vincular automáticamente la vista. Por ejemplo:

// 不合适的写法
@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"); // 复用视图对象
}

Al hacerlo, se evitan búsquedas de vistas innecesarias y se mejora el rendimiento y la fluidez.
### 10. Evite el uso de VectorDrawable para mostrar gráficos vectoriales.
La implementación interna de VectorDrawable utiliza Path para dibujar gráficos vectoriales, lo que consume más recursos de CPU y memoria y puede provocar retrasos. Si necesita mostrar gráficos vectoriales, puede utilizar formatos como SVG o WebP. Por ejemplo:

// 不合适的写法
<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和内存资源,并提高绘制效率

Hacerlo puede evitar dibujos innecesarios de gráficos vectoriales y mejorar el rendimiento y la fluidez.

11. Evite utilizar AsyncTask para realizar tareas asincrónicas.

La implementación interna de AsyncTask utiliza un grupo de subprocesos y una cola de mensajes para administrar tareas, lo que ocupa espacio en la memoria y puede provocar pérdidas de memoria y problemas de concurrencia. Si necesita realizar tareas asincrónicas, puede utilizar bibliotecas como RxJava o Coroutine. Por ejemplo:

// 不合适的写法
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
        }
    }
}

Al hacerlo, se evita la asignación y desasignación de memoria innecesaria, se evitan pérdidas de memoria y problemas de concurrencia, y se mejora la gestión y programación de tareas asincrónicas.

### 12. Evite utilizar BitmapFactory para cargar imágenes.
La implementación interna de BitmapFactory utiliza el método nativoDecodeStream para decodificar imágenes, lo que consume más espacio de memoria y puede provocar OOM. Si necesita cargar imágenes, puede utilizar bibliotecas como Glide o Picasso. Por ejemplo:

// 不合适的写法
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

Hacerlo puede evitar la creación de objetos de mapa de bits innecesarios, ahorrar espacio en la memoria y mejorar la eficiencia y calidad de la carga de imágenes.

13. Evite utilizar la interfaz Serializable para implementar la serialización.

La implementación interna de la interfaz Serializable utiliza el mecanismo de reflexión para serializar y deserializar objetos, lo que consume más recursos de CPU y memoria y puede provocar una degradación del rendimiento. Si necesita implementar la serialización, puede utilizar la interfaz Parcelable o bibliotecas como ProtoBuf. Por ejemplo:

// 不合适的写法
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];
        }
    };
}

Hacerlo puede evitar operaciones de reflexión innecesarias, ahorrar recursos de CPU y memoria y mejorar la eficiencia de la serialización y deserialización.

14. Evite el uso de LinkedList para almacenar datos.

La implementación interna de LinkedList utiliza una lista doblemente vinculada para almacenar datos, lo que ocupa más espacio de memoria y puede causar fragmentación de la memoria. Si necesita almacenar datos, puede utilizar ArrayList o ArrayDeque en su lugar. Por ejemplo:

// 不合适的写法
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");

Hacer esto ahorra espacio en la memoria porque la implementación interna de ArrayList y ArrayDeque utiliza una matriz para almacenar datos, sin gastos generales adicionales. Además, proporcionan un acceso aleatorio y un rendimiento de iteración más rápidos.

15. Evite usar StringTokenizer para dividir cadenas.

La implementación interna de StringTokenizer utiliza una matriz de caracteres para almacenar cadenas, y cada vez que se llama al método nextToken (), se crea un nuevo objeto de cadena, que consume más recursos de CPU y memoria y puede provocar GC. Si necesita dividir la cadena, puede utilizar el método split() o la clase Scanner en su lugar. Por ejemplo:

// 不合适的写法
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
}

Hacerlo puede evitar la creación innecesaria de objetos de cadena, ahorrar recursos de CPU y memoria y mejorar la eficiencia de la división de cadenas.

16. Evite el uso de SimpleDateFormat para formatear fechas y horas.

La implementación interna de SimpleDateFormat utiliza un objeto Calendario para almacenar la fecha y la hora, y cada vez que se llama al método format(), se crea un nuevo objeto Fecha, que consume más recursos de CPU y memoria y puede causar GC. Si necesita formatear fechas y horas, puede utilizar bibliotecas como DateTimeFormatter o FastDateFormat. Por ejemplo:

// 不合适的写法
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); // 这不会创建任何新的对象

Al hacerlo, se evita la creación de objetos de fecha innecesarios, se ahorran recursos de CPU y memoria y se mejora la eficiencia del formateo de fecha y hora.

17. Evite el uso de SparseIntArray para almacenar matrices dispersas.

La implementación interna de SparseIntArray utiliza dos matrices para almacenar claves y valores, lo que ocupa más espacio de memoria y puede provocar la expansión y copia de la matriz. Si necesita almacenar matrices dispersas, puede utilizar bibliotecas como SparseMatrix o EJML. Por ejemplo:

// 不合适的写法
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);

Hacerlo puede ahorrar espacio en la memoria, porque la implementación interna de SparseMatrix y EJML utiliza una lista vinculada o una tabla hash para almacenar elementos distintos de cero, sin sobrecarga adicional. Además, proporcionan un rendimiento más rápido para operaciones matriciales y transposiciones.

18. Evite el uso de JSONObject para analizar cadenas JSON.

La implementación interna de JSONObject utiliza un HashMap para almacenar pares clave-valor, lo que ocupa más espacio de memoria y puede provocar conflictos de hash y expansión. Si necesita analizar cadenas JSON, puede utilizar bibliotecas como Gson o Moshi. Por ejemplo:

// 不合适的写法
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();

Hacerlo puede evitar la creación innecesaria de objetos de tabla hash, ahorrar espacio en la memoria y mejorar la eficiencia y calidad del análisis JSON.

19. Evite utilizar la clase Random para generar números aleatorios.

La implementación interna de la clase Random utiliza un generador congruente lineal para generar números aleatorios, lo que da como resultado números aleatorios de baja calidad y puede provocar problemas de concurrencia. Si necesita generar números aleatorios, puede utilizar clases como ThreadLocalRandom o SecureRandom. Por ejemplo:

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

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

Esto mejora la calidad y seguridad de los números aleatorios y evita problemas de simultaneidad.

20. Evite utilizar la clase Log para imprimir registros.

La implementación interna de la clase Log utiliza un objeto PrintStream para enviar registros a la consola o archivo, lo que consume más recursos de CPU y memoria y puede causar bloqueo de IO y degradación del rendimiento. Si necesita imprimir registros, puede utilizar bibliotecas como Timber o Logger. Por ejemplo:

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

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

Hacerlo puede evitar operaciones de E/S innecesarias, ahorrar recursos de CPU y memoria y mejorar la eficiencia y la calidad de la salida de registros.

Utilice clases internas estáticas o referencias débiles para evitar que las clases internas no estáticas contengan referencias a clases externas, lo que provoca pérdidas de memoria.

// 不合适的写法
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;
        }
    }
}

Hacer esto puede evitar pérdidas de memoria, porque si MainActivity se destruye mientras MyTask todavía se está ejecutando en segundo plano, las clases internas no estáticas harán que MainActivity no se recicle, pero las clases internas estáticas o las referencias débiles no lo harán. Esto ahorra espacio en la memoria y mejora el rendimiento.

Cuando utilice el modo singleton, tenga cuidado de utilizar el contexto de la aplicación en lugar del contexto de la actividad para evitar que la actividad se recicle.

// 不合适的写法
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;
    }
}

Hacer esto puede evitar pérdidas de memoria, porque si la Actividad se destruye mientras MySingleton todavía usa su Contexto, la Actividad no se puede reciclar, pero el Contexto de la Aplicación no. Esto ahorra espacio en la memoria y mejora el rendimiento.

Utilice herramientas como Proguard o R8 para ofuscar y comprimir código, reduciendo la cantidad de métodos y el tamaño del código de bytes. Por ejemplo:

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

Hacerlo puede reducir el tamaño del APK y mejorar la seguridad y la eficiencia operativa de la aplicación.

Utilice la herramienta Lint para detectar y eliminar archivos de recursos inútiles para reducir el tamaño de su APK. Por ejemplo:

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

Hacerlo puede reducir el tamaño del APK y mejorar la eficiencia operativa y la calidad de la aplicación.

Utilice la opción inBitmap para reutilizar el espacio de memoria del mapa de bits y reducir la asignación de memoria. Por ejemplo:

// 不合适的写法
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);

Hacerlo puede reducir la cantidad de tiempos de reciclaje y asignación de memoria y mejorar el rendimiento y la fluidez.

Utilice la opción inSampleSize para escalar la imagen proporcionalmente y evitar cargar imágenes demasiado grandes. Por ejemplo:

// 不合适的写法
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);

Hacer esto puede evitar cargar imágenes demasiado grandes, ahorrar espacio en la memoria y mejorar la eficiencia y calidad de la carga de imágenes.

Optimice los archivos de diseño, reduzca los niveles de diseño y los controles redundantes, y utilice incluir, fusionar, ViewStub y otras etiquetas para reutilizar y retrasar la carga de diseños. Por ejemplo:

<!-- 不合适的写法 -->
<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>

Esto puede reducir los niveles de diseño y los controles redundantes, y mejorar la eficiencia y la fluidez de la carga y representación del diseño. Si desea reutilizar y cargar diseños de forma diferida, puede usar incluir, fusionar, ViewStub y otras etiquetas para lograrlo.

notas de estudio de Android

Artículo de optimización del rendimiento de Android: https://qr18.cn/FVlo89
Artículo de principios subyacentes del marco de trabajo de Android: https://qr18.cn/AQpN4J
Artículo de vehículo de Android: https://qr18.cn/F05ZCM
Notas de estudio de seguridad inversa de Android: https://qr18.cn/CQ5TcL
Artículo de audio y video de Android: https://qr18.cn/Ei3VPD
Artículo del grupo de la familia Jetpack (incluido Compose): https://qr18.cn/A0gajp
Notas de análisis del código fuente de OkHttp: https://qr18.cn/Cw0pBD
Artículo de Kotlin: Artículo https://qr18.cn/CdjtAF
de Gradle: Artículo de https://qr18.cn/DzrmMB
Flutter : https://qr18.cn/DIvKma
Ocho cuerpos de conocimiento de Android: https://qr18.cn/CyxarU
Notas principales de Android: https://qr21.cn/CaZQLo
Preguntas de la entrevista de Android de años anteriores: https://qr18.cn/CKV8OZ
Las últimas preguntas de la entrevista de Android en 2023: https://qr18.cn/CgxrRy
Ejercicios de entrevista para el puesto de desarrollo de vehículos de Android: https://qr18.cn/FTlyCJ
Preguntas de la entrevista en audio y video:https://qr18.cn/AcV6Ap

Supongo que te gusta

Origin blog.csdn.net/weixin_61845324/article/details/132887667
Recomendado
Clasificación