安卓内存优化案例穷举

安卓内存优化是一个很重要的话题,有很多方面可以考虑,比如避免内存泄漏、减少内存抖动、优化图片加载、使用缓存和对象池等。下面我举一些代码案例,分别展示不合适的写法和高性能的写法。 欢迎评论区留言指正和补充。

###1. 避免使用枚举类型。 枚举类型会占用更多的内存,因为它是一个类对象,而不是一个基本类型。如果需要定义一些常量,可以使用 static final int 或者 @IntDef 注解来代替。例如:

Java 复制代码
// 不合适的写法
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,影响性能和用户体验。如果需要在循环中使用对象,可以在循环外创建并复用,或者使用对象池来管理对象的生命周期。例如:

Java 复制代码
// 不合适的写法
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 来代替。例如:

Java 复制代码
// 不合适的写法
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。如果需要释放内存,可以通过合理地设计数据结构和算法来减少内存占用,并及时释放不再使用的对象的引用。例如:

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

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

这样做可以让系统根据内存情况自动调整GC策略,并避免不必要的GC开销。

###5. 避免在 onDraw() 方法中创建对象。 这会导致每次绘制都会分配内存,造成内存抖动和GC。如果需要在 onDraw() 方法中使用对象,可以在构造方法或者 onSizeChanged() 方法中创建并复用,或者使用静态常量来代替。例如:

Java 复制代码
// 不合适的写法
@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 来代替。例如:

Java 复制代码
// 不合适的写法
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);

这样做可以节省内存空间,因为 ArrayMapSparseArray 的内部实现是使用两个数组来存储键和值,没有额外的开销。另外,它们还可以避免 HashMap 的扩容和哈希冲突的问题。

###7. 避免使用 setXxx() 方法来设置视图的属性。 这会导致视图的重新布局和重绘,消耗CPU和内存资源,并可能导致卡顿。如果需要动态改变视图的属性,可以使用属性动画来实现。例如:

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

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

这样做可以避免不必要的视图更新,提高动画效果和流畅度。

###8. 避免在 onCreate() 方法中初始化不必要的对象。 这会导致应用启动时间变长,影响用户体验,并可能导致ANR。如果有些对象不需要在启动时就初始化,可以延迟到使用时再初始化,或者放到子线程中初始化。例如:

Java 复制代码
// 不合适的写法
@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 等库来自动绑定视图。例如:

java 复制代码
// 不合适的写法
@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 等格式来代替。例如:

xml 复制代码
// 不合适的写法
<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 等库来代替。例如:

java 复制代码
// 不合适的写法
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 等库来代替。例如:

java 复制代码
// 不合适的写法
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 等库来代替。例如:

java 复制代码
// 不合适的写法
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 来代替。例如:

java 复制代码
// 不合适的写法
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");

这样做可以节省内存空间,因为 ArrayListArrayDeque 的内部实现是使用一个数组来存储数据,没有额外的开销。另外,它们还可以提供更快的随机访问和迭代的性能。

###15. 避免使用 StringTokenizer 来分割字符串。 StringTokenizer 的内部实现是使用一个字符数组来存储字符串,并且每次调用 nextToken() 方法都会创建一个新的字符串对象,这会消耗较多的CPU和内存资源,并可能导致GC。如果需要分割字符串,可以使用 split() 方法或者 Scanner 类来代替。例如:

java 复制代码
// 不合适的写法
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 等库来代替。例如:

java 复制代码
// 不合适的写法
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 的内部实现是使用两个数组来存储键和值,这会占用较多的内存空间,并且可能导致数组扩容和复制。如果需要存储稀疏矩阵,可以使用 SparseMatrix 或者 EJML 等库来代替。例如:

java 复制代码
// 不合适的写法
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);

这样做可以节省内存空间,因为 SparseMatrixEJML 的内部实现是使用一个链表或者一个哈希表来存储非零元素,没有额外的开销。另外,它们还可以提供更快的矩阵运算和转置的性能。

###18. 避免使用 JSONObject 来解析JSON字符串。 JSONObject 的内部实现是使用一个 HashMap 来存储键值对,这会占用较多的内存空间,并且可能导致哈希冲突和扩容。如果需要解析JSON字符串,可以使用 Gson 或者 Moshi 等库来代替。例如:

java 复制代码
// 不合适的写法
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 等类来代替。例如:

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

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

这样做可以提高随机数的质量和安全性,并避免并发问题。

###20. 避免使用 Log 类来打印日志。 Log 类的内部实现是使用一个 PrintStream 对象来输出日志到控制台或者文件,这会消耗较多的CPU和内存资源,并且可能导致IO阻塞和性能下降。如果需要打印日志,可以使用 Timber 或者 Logger 等库来代替。例如:

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

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

这样做可以避免不必要的IO操作,节省CPU和内存资源,并提高日志输出的效率和质量。

###使用静态内部类或者弱引用来避免非静态内部类持有外部类的引用,造成内存泄漏。

java 复制代码
// 不合适的写法
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;
        }
    }
}

这样做可以避免内存泄漏,因为如果MainActivity被销毁,而MyTask还在后台运行,那么非静态内部类会导致MainActivity无法被回收,而静态内部类或者弱引用则不会。这样可以节省内存空间,并提高性能。

###使用单例模式时,注意使用Application的Context,而不是Activity的Context,避免Activity无法被回收。

java 复制代码
// 不合适的写法
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;
    }
}

这样做可以避免内存泄漏,因为如果Activity被销毁,而MySingleton还在使用它的Context,那么Activity无法被回收,而Application的Context则不会。这样可以节省内存空间,并提高性能。

使用Proguard或者R8等工具来混淆和压缩代码,减少方法数和字节码大小。例如:

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

这样做可以减少APK的体积,提高应用的安全性和运行效率。

###使用Lint工具来检测和移除无用的资源文件,减少APK的体积。例如:

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

这样做可以减少APK的体积,提高应用的运行效率和质量。

###使用inBitmap选项来复用Bitmap的内存空间,减少内存分配。例如:

java 复制代码
// 不合适的写法
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选项来按比例缩放图片,避免加载过大的图片。例如:

java 复制代码
// 不合适的写法
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等标签来复用和延迟加载布局。例如:

xml 复制代码
<!-- 不合适的写法 -->
<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等标签来实现。

相关推荐
编程洪同学3 小时前
Spring Boot 中实现自定义注解记录接口日志功能
android·java·spring boot·后端
氤氲息5 小时前
Android 底部tab,使用recycleview实现
android
Clockwiseee5 小时前
PHP之伪协议
android·开发语言·php
小林爱6 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
Bigger6 小时前
Tauri(三)—— 先搞定窗口配置
前端·app·客户端
小何开发7 小时前
Android Studio 安装教程
android·ide·android studio
开发者阿伟7 小时前
Android Jetpack LiveData源码解析
android·android jetpack
weixin_438150997 小时前
广州大彩串口屏安卓/linux触摸屏四路CVBS输入实现同时显示!
android·单片机
CheungChunChiu8 小时前
Android10 rk3399 以太网接入流程分析
android·framework·以太网·eth·net·netd
木头没有瓜8 小时前
ruoyi 请求参数类型不匹配,参数[giftId]要求类型为:‘java.lang.Long‘,但输入值为:‘orderGiftUnionList
android·java·okhttp