Android性能优化建议

磁盘

SharedPreferences

我们知道SharedPreferences底层是使用xml文件来实现的。所以对于SharedPreferences的操作其实是I/O操作,是耗时操作。

commit

每一次commit的调用都会对应一次文件的打开和关闭。commit是同步操作,apply是异步操作。

最佳实践

减少commit的次数; 在一个逻辑操作(方法)中不要多次commitcommit应放在最后。或者使用缓存来保存多次写入的数据,最后提交commit

如果对数据的实时性没有要求,可以使用apply来代替commit

flag的保存与使用

我们通常会使用SharedPreferences来保存一些flag。但是要注意不要随意使用。比如你在一个Recycleview的卡片中,根据一个flag来做不同UI的判断:

class SimpleCard : LinearLayout, AdapterItem{
    fun bindData(...){
        if(Sp.getBoolean(xxx)){
            ...
        }    
    }
}

因为SimpleCardRecycleview中频繁被bindData, 因此SP.getBoolean(xxx)会被频繁调用,前面已经说了SP的读或写是io操作。这种写法势必会造成UI卡顿。

最佳实践

flag一般来说在app启动的时候就已经确定了,所有我们只需要获取一次。可以提前初始化这些flag:

//比如在某个Activity的onCreate

    runOnIoThread({
        FlagCenter.initAllFlag()
    })

//SimpleCard.bindData

 fun bindData(){
        if(FlagCenter.useStyle1){
            ...
        }
    }

ObjectOutputStream

利用它我们可以把对象保存到磁盘中。不过它有一个特点:在保存对象的时候,每个数据成员会带来一次I/O操作。因此如果你对象很多、属性很复杂时,ObjectOutputStreamI/O操作会异常凶猛。

最佳实践

最好在ObjectOutputStream上再封装一个输出流,比如BufferedOutputStream。先把对象写入到这个流中,然后再使用ObjectOutputStream保存到磁盘。

合理设置buffer

在读一个文件我们一般会设置一个buffer。即先把文件读到buffer中,然后再读取buffer的数据。所以: 真正对文件的次数 = 文件大小 / buffer大小 。 所以如果你的buffer比较小的话,那么读取文件的次数会非常多。当然在写文件时buffer是一样道理的。

很多同学会喜欢设置1KB的buffer,比如byte buffer[] = new byte[1024]。如果要读取的文件有20KB, 那么根据这个buffer的大小,这个文件要被读取20次才能读完。

最佳实践 -> buffer应该设置多大呢?

java默认的buffer为8KB,最少应该为4KB。那么如何更智能的确定buffer大小呢?

  1. buffer的大小不能大于文件的大小。
  2. buffer的大小可以根据文件保存所挂载的目录的block size, 什么意思呢? 来看一下SQLiteGlobal.java是如何确定buffer大小的 :
public static int getDefaultPageSize() { 
    return SystemProperties.getInt("debug.sqlite.pagesize", new StatFs("/data").getBlockSize());
}

Bitmap的解码

在Android4.4以上的系统上,对于Bitmap的解码,decodeStream()的效率要高于decodeFile()decodeResource(), 而且高的不是一点。所以解码Bitmap要使用decodeStream(),同时传给decodeStream()的文件流是BufferedInputStream

最佳实践

val bis =  BufferedInputStream(FileInputStream(filePath))
val bitmap = BitmapFactory.decodeStream(bis,null,ops)

内存

在虚拟机的Heap内存使用超过堆内存最大值就会发生OOM。当手机内存低于内存警戒线时,占用内存越多的APP越有可能被Low Memory Killer给杀掉。

内存泄漏

Activit内存泄漏

Activity对象会间接或者直接引用View、Bitmap等,所以一旦无法释放,会占用大量内存。并且Activity在Destroy的情况下,更新UI是会引发crash的。而Activity内存泄漏最常见的就以下几种case:

  1. 生命周期比Activtiy长的对象持有了Activity的引用。比如在getSystemService时使用了Activity
  2. Activity的内部类作为一个异步的回调监听。比如定义了一个Handler内部类,这时候Handler默认就会引用Activity

最佳实践

在使用getSystemService方法时尽量使用Application, 比如: applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE)

Activity中的Handler定义为静态内部类(解除对Activity的引用),并使用WeakReference<Activity>来引用Activity。

图片

Feed流中的图片如果可以应尽可能降低所占内存大小

对于要加载的图片应做压缩,并在适当的情况下可以在解码时降低质量

最佳实践

对于无透明效果、比较小的图片可以使用RGB_565格式来解码。

在设置解码图片的inSmapleSize参数时应参考要显示的View的宽与高来显示恰当的值。

图片资源不要放在错误的目录

Android的drawable分为好几个层级,比如:drawable、drawable-hdpi、drawable-ldpi、drawable-mdpi等,其实就是对应不同的屏幕密度。Android在获取某个屏幕密度的图片时会去对应的drawable目录下寻找,如果找不到就会取相近目录的资源。但这里是存在一个问题的,举个例子:

比如一张 800 * 480 的图片放置在了ldpi目录。如果480dpi(xxxhdpi)的屏幕要显示这张图片,那么他就会把这张图片放大4倍,即 3200 *1920,然后再去解码。所以图片在内存中被放大了4倍。如果这种case多了,内存很容易爆掉的。

最佳实践

尽量为设计师要高品质图片让后往高密度目录下放,这样在低密度上放大倍数是小于1的,在保证画质的前提下,内存也是可控的。

SparseArray与ArrayMap

SparseArray也是一个map,它的key必须为int类型, 在数据量不大的情况下(千级以内),它的性能会要比HashMap好,并且更升内存。

SparseArray与ArrayMap的key可以为任意类型,当数据量比较小时也可以使用它来代替HashMap

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/20171,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?