玩安卓必须要掌握的性能优化之内存优化

两盒软妹~` 提交于 2020-04-14 13:26:30

【推荐阅读】微服务还能火多久?>>>

前言

这是我写的性能优化的第三篇文章,下面是另外两篇文章:

玩安卓必须要掌握的性能优化之内存泄漏

玩安卓必须要掌握的性能优化之APK极限压缩

正文

本篇文章准备详细说一下编码中需要注意的地方,养成良好的编码习惯。

第一点 数据类型

尽量不要使用比需求更占空间的基本数据类型,比如能用 int 就不使用 long,能用 float 就不使用 double,可以减少内存的开销,虽然几乎对程序产生不了太大影响,但是还是要尽量避免。

还有就是自动装箱尽量少用。

第二点 循环

循环尽量使用增强for循环(for each),少用迭代器(iterator)

第三点 数据结构与算法

这一点是最重要的,但也是最难的,其实也不难,关键看能不能坚持下来。每天看一道算法题,半年基本上就能提升非常大的一截。数组、链表、树、栈、图。。。。

还有一点是咱们经常使用的HashMap,写的时候非常自然,直接就写出来了,但是占用内存较大,Android官方为安卓开发者提供了SparseArray(key为整数),当数据量在千级以下可以使用,它的性能虽然不如HashMap但节约内存;千级以上还是使用我们熟悉的HashMap吧。

第四点 枚举优化

曾几何时,因为安卓官方二点几的一句话(图在下方),几乎所有的安卓性能优化中都会有这么一句,但我想说的是,随着硬件的提升,之后的官方文档中早已经找不见这句话了,但不知道为什么2020年了还在流行着这句话,希望大家不要在下面喷我。感谢。

虽然说现在可以用枚举,但是性能优化嘛,本来也就是鸡蛋里挑骨头,咱们可以将枚举替换成常量,并用注解进行个数的限制。下面是代码样例:

public class Constants {
    public static final String TYPE_A = "1";
    public static final String TYPE_B = "2";
    public static final String TYPE_C = "3";
    public static final int TYPEDES_A = 1;
    public static final int TYPEDES_B = 2;
    public static final int TYPEDES_C = 3;
}

//String类型的注解 @StringDef() 
@StringDef({Constants.TYPE_A,Constants.TYPE_B,Constants.TYPE_C})
public @interface FieldMata {
}
//int类型的注解
@IntDef({Constants.TYPEDES_A,Constants.TYPEDES_B,Constants.TYPEDES_C})
public @interface TypeDes {
}

代码很简单,使用也不难,下面是使用样例:

    //在需要限定的方法内的参数写上先前定义的限定值
    private void printType(@FieldMata String type) {
        System.out.println(type);
    }
 
    private void printType(@TypeDes int des) {
        System.out.println(des);
    }
 
    public static void main(String[] args) {
        DemoTest demoTest = new DemoTest();
        demoTest.printType(Constants.TYPE_A);
        demoTest.printType(Constants.TYPEDES_A);
    }

第五点 static   static final的问题

static会由编译器调用clinit方法进行初始化
static final不需要进行初始化工作,打包在dex文件中可以直接调用,并不会在类初始化申请内存
所以基本数据类型的成员,可以全写成static final

第六点 字符串的连接尽量少用加号(+)

这一点大家可能听的耳朵都起茧了,但是不得不说,几次拼接可能无所谓,但是千万不要在循环中还使用加号进行拼接,对内存的消耗真的挺大,尽量还是使用StringBuffer和StringBuilder吧。

第七点 重复申请内存问题

尽量不要在同一个方法多次调用,如递归函数 ,回调函数中new对象,读流直接在循环中new对象等
也不要在onMeause() 、onLayout()、onDraw()  中去刷新UI(requestLayout)

第八点 避免GC回收将来要重用的对象

比如咱们最常用的图片加载框架Glide,它就做的非常好,既实现了LifeCycle,还使用Lru算法,还有三级缓存,这样大大避免了GC回收需要重复使用的图片,可以参考Glide源码的实现。

第九点 尽量使用IntentService,而不是Service

原因很简单,IntentService是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题。

第十点 Activity组件泄漏

  1. 非业务需要不要把activity的上下文做参数传递,可以传递application的上下文
  2. 千万不要把和Activity有关联的对象写成static   如private static Button btn;   private static Drawable drawable;除了调用比较方便,没有一点好处
  3. 非静态内部类和匿名内部内会持有activity引用。比如咱们经常写的回调接口,不要使用匿名内部类来实现,尽量使用实现接口的方式来实现,在接收的使用也尽量不要接收Activity,最好使用弱引用(WeakReference),但是别忘了在使用前要进行判空
  4. 单例模式持有activity引用,这里也是,如果需要Activity尽量使用弱引用
  5. handler.postDelayed(),如果开启的线程需要传入参数,用弱引接收可解决问题。还有就是handler记得清除removeCallbacksAndMessages(null)

总结

其实上面所说的只是很多编码习惯中比较重要的一些,当然还有很多没有列举出来,如果文中描述有误,欢迎大家留言指正,不胜感激。

发布了100 篇原创文章 · 获赞 272 · 访问量 15万+
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!