性能优化实践一

2020-01-29 10:04 阅读 719 次 评论 0 条

一 内存泄露

常见的内存泄露场景有:

  1. 单例的getInstance()方法传入了一个对象,避免这样做;如果真的需要一个对象,考虑在成员方法传入
  2. MVP架构,P层持有上下文的引用,可能造成界面退出,DAO层还在加载数据;可以考虑在BasePresenter引入弱引用的方式解决
  3. 注册观察者模式,使用结束没有解注册,可能操作泄露;常见的场景,4. 匿名函数的使用,解决的思路,匿名函数避免做耗时或阻塞的操作
    内部类默认持有外部类的引用,检查是否将一个内部类实例赋值给外部类的静态实例或者在内部类执行了耗时或者阻塞操作
  4. Handler严格写成嵌套类的形式,检查代码不符合这个规则的代码,进行修改
  5. JobIntentService启动的方式入口是一个静态方法,而且一直在后台运行,替换成Application Context引用

造成内存泄露的一个原因,短生命周期的对象被长生命周期的对象持有,为了提供更优的内存使用,我们在测试的时候使用LeakCanary检测,将检测到的内存泄露信息写入本地文件,做法是利用DisplayLeakService#afterDefaultHandling(HeapDump,AnalysisResult,String)方法,考虑生产环境LeakCanary是关闭的,暂时不考虑将Dump出的Hprof文件上传

内存监控不推荐线上使用的主要原因,Dump出来的Hprof文件比较大、耗时,对app性能影响比较大,像微信开源的Resources Canary也不推荐线上使用,现阶段暂时考虑review为主吧!

指定自定义的Service

mRefWatcher = LeakCanary.refWatcher(this)
        .listenerServiceClass(SaveLeakService.class)
        .buildAndInstall();

重写DisplayLeakService的afterDefaultHandling()方法

/**
 * 自定义内存泄露相关信息保存本地
 *
 */
public class SaveLeakService extends DisplayLeakService {

    @Override
    protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
        File file = new File(getApplication().getExternalCacheDir() + "/temp/");
        if (file.exists()) {
            file.mkdirs();
        }
        file = new File(file, "SaveLeakService.info");
        saveByAsync(result, file);
    }

    /**
     * 将内存泄露的结果保存到文件中
     */
    private void saveByAsync(AnalysisResult result, File file) {
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            StringBuffer sb = new StringBuffer();
            sb.append("AnalysisResult{ ")
                    .append("leakFount=" + result.leakFound)
                    .append(",excludeLeak=" + result.excludedLeak)
                    .append(",className=" + result.className)
                    .append(",leakTrace=" + result.leakTrace)
                    .append(",retainedHeapSize=" + result.retainedHeapSize/1000+"KB")
                    .append(",analysisDurationMs=" + result.analysisDurationMs+" }")
                    .append("\r\n")
                    .append(format.format(new Date()).toCharArray())
                    .append("-----------------------------分割线----------------------------")
                    .append("\r\n");
            FtspLog.error("file path= " + file.getAbsolutePath() + "\n" + sb.toString());
            bw.write(sb.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bw != null)
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}

二 性能更优的集合或类

  1. 数据量少于1000,考虑使用ArrayMap代替HashMap;
  2. 键值对存储中,key为int类型的,考虑使用SparseArray,避免自动装箱/拆箱造成内存开销;
  3. 枚举的使用方便,能够确保类型的安全,不足性能开销是普通常量的好几倍,大量的枚举会增加更多的内存消耗,可以考虑使用注解加常量的方式替代。

三 其他性能优化的点

  1. 图片内存优化,控件展示图片,避免加载超过控件宽高很多的图片,建议显示前对图片进行压缩,使用Glide可以指定固定值;
  2. SharedPrefences在应用启动的时候,将整个xml文件加载到内存,检查xml文件中的内容,对于文本内容比较多的,是不是可以考虑用SQLite存储方式;
  3. 关于序列化,目的持久化保存本地,另一个组件之间的传递,组件间传递的数据避免超过1M,否则会造成TransactionTooLargeException;
  4. 本地图片加载的过程,同样可以考虑使用Glide进行压缩缓存处理,比起直接加载会更省内存;
  5. 关于PNG图片,可选格式是ARGB_8888/ARGB_4444/RGB_565,ARGB_8888一个像素占4个字节,ARGB_4444一个像素占2个字节,RGB_565一个像素占一字节,对不包含透明通道的PNG图片,考虑使用RGB_565格式会更省内存。
关注公众号 扫一扫二维码,加我QQ

如果文章对你有帮助,欢迎点击上方按钮关注作者

来源:TeachCourse每周一次,深入学习Android教程,关注(QQ158#9359$239或公众号TeachCourse)
转载请注明出处:https://www.teachcourse.cn/2789.html ,谢谢支持!
分类:Android 标签:
Genymotion启动提示”Unable to start the virtual device“ Genymotion启动提示”Unable t
Eclipse卸载已安装的Genymotion插件 Eclipse卸载已安装的Genymotio
浅谈json的封装和解析 浅谈json的封装和解析
APP签名的三种方式使用说明 APP签名的三种方式使用说明

发表评论

呲牙 憨笑 坏笑 偷笑 色 微笑 抓狂 睡觉 酷 流汗 鼓掌 大哭 可怜 疑问 晕 惊讶 得意 尴尬 发怒 奋斗 衰 骷髅 啤酒 吃饭 礼物 强 弱 握手 OK NO 勾引 拳头 差劲 爱你

表情