首页 > 系统相关 >Android内存优化案例——不合适和高性能的写法(一)

Android内存优化案例——不合适和高性能的写法(一)

时间:2023-09-06 14:05:37浏览次数:50  
标签:对象 视图 内存 使用 Android onCreate 写法

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

1. 避免使用枚举类型。

枚举类型会占用更多的内存,因为它是一个类对象,而不是一个基本类型。如果需要定义一些常量,可以使用 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 {}

这样做可以节省内存空间,因为枚举类型会占用至少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);

这样做可以节省内存空间,因为 ArrayMapSparseArray 的内部实现是使用两个数组来存储键和值,没有额外的开销。另外,它们还可以避免 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和内存资源,并提高绘制效率

这样做可以避免不必要的矢量图形绘制,提高性能和流畅度。

标签:对象,视图,内存,使用,Android,onCreate,写法
From: https://blog.51cto.com/u_16175630/7386421

相关文章

  • Android官方资料--OTA Updates
    OTAUpdatesINTHISDOCUMENTAndroiddevicelayoutLifeofanOTAupdateMigratingfromPreviousReleasesAndroiddevicesinthefieldcanreceiveandinstallover-the-air(OTA)updatestothesystemandapplicationsoftware.Deviceshaveaspecialrecove......
  • 在Android中用e.printStackTrace()
    IsitabadideatouseprintStackTrace()inAndroidExceptions?upvote35downvotefavorite7IsitabadideatouseprintStackTrace()inAndroidExceptionslikethis?}catch(Exceptione){e.printStackTrace();}}catch......
  • Android OTA升级之升级包生成脚本ota_from_target_files
    我们在这里主要分析OTA升级过程中对ota_from_target_files.py脚本的调用,这个脚本主的作用是生成各种OTA完整包和增量包。 以生成完整OTA包为例,假设我们的命令为:./build/tools/releasetools/ota_from_target_files-n input_file_dir/target_file.zip output_file_dir/outp......
  • android调试工具--dumpsys
    dumpsys执行格式为:dumpsys[option] 其中option参数可以为service和acitivity,参数service可以是系统中任意一个服务,比如meminfo服务显示内存信息,cpuinfo现实cpu信息参数acitivity可以现实所有的activity信息......
  • Android原生库和架构层通信的socket
    Android为原生库和架构层之间的通信提供了socket机制,在系统中为其提供了/dev/socket节点。/dev/socket节点是在init.rc文件初始化时生成的, $ls/dev/socketadbdcnddnsproxyddpmddpmwrapperfwmarkdims_datadims_qmidims_rtpdinstalldipacm_log_filelmkdlogdlogdrlo......
  • Android官方资料--Adoptable Storage
    AdoptableStorageINTHISDOCUMENTSecurityPerformanceandstabilityAndroidhasalwayssupportedexternalstorageaccessories(suchasSDcards),buttheseaccessorieswerehistoricallylimitedtosimplefilestorage,duetotheirexpectedimpermanence......
  • Android官方资料--OTA Package Tools
    OTAPackageToolsINTHISDOCUMENTFullupdatesIncrementalupdatesUpdatepackagesThe ota_from_target_files toolprovidedinbuild/tools/releasetools canbuildtwotypesofpackage: full andincremental.Thetooltakesthe target-files .zipfileproduc......
  • android编译报错:error: ro.build.fingerprint cannot exceed 91 bytes
    http://stackoverflow.com/questions/28776970/android-build-error-ro-build-fingerprint-cannot-exceed-91-bytesbuild/tools/post_process_props.py.Changelinesasfollows:PROP_NAME_MAX=31#PROP_VALUE_MAX=91PROP_VALUE_MAX=128PROP_NAME_MAX=31#PROP_V......
  • Android官方资料--Reducing OTA Size
    ReducingOTASizeINTHISDOCUMENTThebuilddifftoolChangestoreduceOTAsizeFileOrderBuildDirectoryTimestampsVersionStringsConsistentbuildtoolsAcommonproblemwithAndroidover-the-airupdates(OTAs)isthattheycontainchangedfilesthatdo......
  • Android Recovery UI浅析1——概览
    最近在作一个在recovery中显示文字的工作,所以对这块研究较多,现在把研究的一点新的结果分享出来,如果有什么错误也欢迎大家在下面评论。 Android的Recovery中,利用 boottable/recovery下的minui库作为基础,采用的是直接存取framebuffer的方式,来完成recovery中所需的各种UI的绘制。......