美文网首页
Android移动性能实战 汇总

Android移动性能实战 汇总

作者: lewis_v | 来源:发表于2020-02-03 11:05 被阅读0次

磁盘

写入放大

磁盘一页为4k,一块为128页有的是64页。
在ssd删除一页数据时,会将其标记为删除,并不会真正的删除,所以数据才有恢复一说。在下次写入到这页的时候才会进行删除,但是ssd的最小删除单位是块,所以就造成了写入放大。在一个新的ssd中是很小几率出现写入放大的,但使用时间长了,ssd中的页都被使用过了就很容易出现。

写入放大的过程

  • 写入4k数据
  • 检查到写入ssd的这一块数据满了但是其中有1页数据标记为删除
  • 将整个块拷贝出来(512k)
  • 修改数据(4k)
  • ssd擦除整个块(512k)
  • 将之前复制出来修改的数据写入ssd(512k)

在写入过程中,虽然只是写入4k数据,但是由于写入放大,多次操作了512k大小的数据(128倍)

压缩文件

Android中压缩解压zip有zipfile和zipinputstream

  • 首先两这个实际都是调用到native层进行压缩解压的(zlib库)
  • zipfile是在native层进行文件读取然后处理完返回给Java,一次处理的数据为1-64k
  • zipinputstream是在Java层准备好数据,再传给native层,每次固定为512字节

zipfile读写文件次数更少,Java层与native层的交互次数更少

由此可见zipfile的效率高于zipinputstream,所以文件压缩操作首选使用zipfile,但是zipinputstream也有使用场景

  • 需要边接收数据边解压,例如网络传输
  • zip中的目录central directory损坏了,此时只能通过zipinputstream进行顺序解压

数据库的AUTOINCREMENT,主键的自增长

sqlite每行都有一个64位的行号,默认从1开始,插入数据时,如果没有指定行号,会使用当前最大行号+1,如果达到了最大值,会回去找没有使用的行号,找不到就SQLITE_FULL,所以普通的行号是不一定保证递增的。但如果添加AUTOINCREMENT,SQLite会保证当前的行号一定是递增的,如果达到最大值就会直接SQLITE_FULL,AUTOINCREMENT的实现是新建了一个sqlite_sequence表进行最大行号记录的,所以更新插入的操作都需要去维护这个表,导致一次操作读取多个表进行了多次的文件操作

所以尽量减少使用AUTOINCREMENT的使用,这个在SQLite官网也是这么建议的

bitmap的decode

bitmap的decode我们经常使用的有decodeFile,decodeStream,decodeResource,decodeResourceStream等,这些方法最终都是掉到native层的dodecode方法进行解码,但是他们在java层有不一样的操作。(android4.3及之前没什么差别)

public static Bitmap decodeFile(String pathName, Options opts) {
...
 stream = new FileInputStream(pathName);
 bm = decodeStream(stream, null, opts);
 ...
 }

从decodeFile代码可以看出,在生成一个FileInputStream后就调用了decodeStream方法。问题就是处在了FileInputStream,这个流是一个字节流,读取是按字节读取,所以每次读的少导致了读的次数增加和很多。而我们在使用decodeStream时都会传入一个BufferInputStream,这个流有每次读取会经可能的读多一些,默认是8k,这回使读取的次数大大的减少提高读取效率。

前面也说到4.3的android不会有此问题,因为在decodeStream中如果不是buffer会生成一个buffer

public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
...
if (!is.markSupported()) {
            is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
        }
...
}

所以开发中建议使用decodeStream,同理建议使用decodeResourceStream
同理在读取文件时尽量使用带有缓冲的流,除非有特殊的操作

其他建议

  • 尽量减少文件操作,能一次做完的不要分两次
  • 尽量不要在主线程做文件读写操作
  • 读写文件时我们一般会用一个buffer来装一次读取的数据,而这个buffer大小应该为4k,或者8k,Java默认为8k
    -数据库打开是耗时的操作,尽量打开一次后不要关闭

内存

Java内部类的泄漏

class a{
    class b{
        
    }
}

内部类b中,我们是可以直接使用this来访问a类中的属性,所以b类中会有对a类的持有,所以内部类最好加个静态static或者在a类释放的时候将b的也释放了

系统服务

Android开发中我们经常会是使用到一些系统服务,例如audiomanager wifimanager等。使用这些服务时我们都会使用getSystemService方法来获取,这个方法在context接口中,如果我们使用activity的来获取时,由于内部的实现会默认将这个context传给service,当服务中某些不确定情况下,service内部出现异常,或hold住传入的context,此情况不是必现,在很多次调用中才会偶尔出现。所以建议获取服务的context使用applicationContext,当然少数service是需要使用activity来获取的

activity.getSystemService
activity.getApplicationContext().getSystemService

postDelay

在activity中,我们经常会使用到postDelay的达到延时执行的效果,但是postDelay之后,activity有可能退出或者销毁,此时会出现内存泄露。所以使用post之类的方法时,在activity销毁的时候需要将post的任务都取消掉

bitmap像素数据格式

一般使用的像素数据格式:

  • ARGB_8888:每像素4字节
  • RGB_565:每像素2字节,但是不支持透明
    在生成bitmap中,android默认使用的是ARGB_8888,这会使得bitmap占用内存增大,所以如果图片是没有透明通道的可以使用rgb565
    另外在api26中,android新增了一种模式HARDWARE,这种模式的bitmap内存会存储在显存中不占用java堆栈内存,但是使用这个模式的bitmap生成了之后不可以在这上面画东西

bitmap的重用

在BitmapFactory.Options中有一个inBitmap属性,这个属性的类型为Bitmap,是用于位图重用的,当传入了inBitmap,在decode的时候会将解码后的数据放到inBitmap中并将inBitmap中之前的数据清除掉,这样就可以重复利用一个bitmap内存了。在使用时,需要注意inBitmap需要是可变的,且在解码的图片大于inbitmap时,图片会被裁剪。所以在bitmap可重复利用的情况下,最好使用inbitmap来重用,例如列表

资源目录

Android工程中的资源目录经常会有x,xx,xxx的标识,这些表示是用来放置不同分辨率下的资源,例如xx目录下存放了xx分辨率的图片,在xx分辨率的手机下就会在这个目录下找图片,找不到再去x和xxx中找并进行相应的缩放,以此达到不同分辨率的适配,如果资源放错了有可能会导致内存使用的增大,若将xx的图片放到x中,当xx在x目录下找图片时会将图片放大x倍导致占用内存增大
资源文件要根据分辨率放到相应的文件夹中

其他建议

  • 减少大内存的使用,避免oom
  • 减少对象的频繁创建,避免过多gc
  • 减少常驻内存,避免内存泄漏
  • 有些业务需要context,但是这些业务在activity销毁后有可能还在执行,此时可以使用弱引用持有。个人认为这可以作为临时做法,因为这做法太过隐晦了

网络

图片格式

在网络传输中,我们经常会遇到加载图片,而图片一般来说都比较大所以也更加占用网络资源。一般使用到的图片格式有jpg,png,webp,其中,webp的体积最小支持透明通道,png体积最大色彩最丰富,jpg不支持透明通道,所以在在正常情况下我们可以使用webp来代替png和jpg,以减小网络传输的成本。这里需要注意的是,ios不兼容webp(可以使用其他库来实现兼容),webp的体积虽然小但是其压缩和解压比jpg慢了4-8倍。所以图片格式的替换需要根据实际的使用场景来定。

下载分片大小

我们下载资源一般都会进行分片下载,而每次分片的大小不宜太低,专项组对qq进行分析时,发现其分片大小为8K,此时和微信进行对比,其网络传输速度低于微信,在将分片大小设置成32K是,QQ的传输速度远高于微信。个人认为,对于网络传输的分片最好不要太小但也不要太大,太小了就无法充分使用网络的带宽,太大了占用的内存就大了,所以最理想的是根据机器来定分片的大小,此处的32K只是来验证增大分片可以是网络传输变快,但个人认为这不是一个实际使用的值。

其他建议

  • 前台网络的IO最好小于60K/s,大于60对用户的体验不好
  • 下载或网络请求,在失败重试的机制中必须要要有明确的结束条件,不能出现有无限重试的情况,否则会消耗大量流量和电量

相关文章

  • Android移动性能实战 汇总

    磁盘 写入放大 磁盘一页为4k,一块为128页有的是64页。在ssd删除一页数据时,会将其标记为删除,并不会真正的...

  • Android进阶书籍分享

    本分享用于交流学习,禁止用于商业用途,如有侵权,联系博主删除。 Android移动性能实战 android应用性能...

  • Android性能优化建议

    最近看了一本腾讯测试同学写的书 :《Android移动性能实战》,书中介绍了很多如何检测Android性能问题的工...

  • Android优化

    Android性能优化汇总

  • Gradle 打包

    Gradle实战:Android多渠道打包方案汇总

  • Android性能分析工具整理汇总

    Android性能分析工具整理汇总 把做Android开发以来碰到的一些不错的性能分析工具做个整理汇总... De...

  • Android 移动性能实战摘要

    参照《Android移动性能实战》做的摘要。 性能优化的首要任务就是数据监控统计,是后续测试发现问题和针对优化的基...

  • Android UI性能优化

    Ui性能优化 参考博客:Android UI性能优化实战 识别绘制中的性能问题Android UI性能优化详解 1...

  • Android 《Android移动性能实战》学习笔记

    一、简介二、《Android移动性能实战》笔记三、内容推荐 一、简介 最近忙着吸收营养都没什么时间乱搞了。不过想想...

  • 性能优化

    Android UI性能优化实战 识别绘制中的性能问题性能优化(二) UI 绘制优化 通过Hierarchy Vi...

网友评论

      本文标题:Android移动性能实战 汇总

      本文链接:https://www.haomeiwen.com/subject/tkqhxhtx.html