如何在Android开发中让你的代码更有效率,耗时两个礼拜8000字Android面试长文

这个图片app的代码:https://github.com/penkzhou/iogallery。ppt:http://greenrobot.qiniudn.com/103.pdf

现在我将视频里面的内容记录如下:

使用LruCache避免OOM


首先我们的图片app是用来展示手机里面保存的图片。当app里面需要展示大量的图片的时候,我们需要将这些图片从disk加载到内存当中。如果我们来回地滑动activity,系统会重复许多disk I/O;而且在一个activity里面同时加载多张图片将会占用大量内存,造成系统内存紧张,进而影响用户体验。

如何使用LruCache


这个时候我们可以引入LruCache,将我们最常用的图片缓存到内存里面,这样可以避免大量重复的disk I/O,还可以让app加载图片时内存占用不超过设定值。具体代码如下:

//这里假设通过picture的id(Long)来作为key获取对应的bitmap

public class PictureCache extends LruCache<Long, Bitmap>{

//设定最大的byte值,也就是说整个缓存所能占用的最大内存

public PictureCache(int maxByteSizes){

super(maxByteSizes);

}

// 计算每次添加bitmap的时候,给缓存所添加的数字,默认就是数量,

//这里因为添加的是bitmap,所以每次添加都是计算bitmap对应的字节数

protected int sizeOf(Long key,Bitmap value){

return value.getByteCount();

}

public void put(Long key, Bitmap value);

public Bitmap get(Long key);

}

如何确定Cache大小


一般我们是通过ActivityManager.getMemorySize()来确定Cache的大小。ActivityManager.getMemorySize()表明了在系统正常运行的前提下一个App所占内存的极限。所以我们可以使用它来作为Cache大小的一个衡量,比方如下的代码中,我们使用它的一半来作为Cache的大小:

final ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

final int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;

PictureCache mCache = new PictureCache(memoryClassBytes / 2);

app跑到后台去了


当我们用这个图片app浏览完图片之后呢,我们回到Android 主界面,开始玩游戏。大家都知道,游戏很耗内存。可能我们在玩的过程中,直接用完了剩下可用的所有内存都还不够,那怎么办呢?Android会在这个时候kill一些后台的app获取相应的内存。

这里需要先说一命令。我们如何获取一个app的内存占用?很简单,使用 “adb shell procrank”命令行。这个命令行显示所有系统运行的进程所占内存大小,一般包括Vss、Rss、Pss、Uss,其中Uss对我们来说最重要,Uss表示如果这个进程被系统干掉了,那么系统可以从这个进程上面获得多少的可用内存。

好了回到之前的情景。Android会kill掉一些后台程序来供给游戏所需的内存。假设Android因为内存紧张kill掉了图片app,我在玩了一会游戏之后又打开了图片app。这个时候图片app又要重新布局,重新加载图片,整个体验对用户来说非常不好。有什么办法让Android在kill应用之前通知app一声,好让app有所准备?

这个时候我们就要用到ComponentCallbacks2,详情看文档。app在系统处于不同的内存环境时会有相应的callback,我们只需在activity里面重写这个onTrimMemory方法即可,具体示例代码如下:

public void onTrimMemory(int level) {

super.onTrimMemory(level);

if (level >= TRIM_MEMORY_MODERATE) { // 60

// 这个app已经进入后
台有一段时间了,基本上表示用户接下来

//不会重新打开这个app,我们可以清掉所有缓存所占内存

Log.v(TAG, “evicting entire thumbnail cache”);

mCache.evictAll();

} else if (level >= TRIM_MEMORY_BACKGROUND) { // 40

// 表示app刚进入后台,我们可以缩减一部分缓存所占内存

// 来保证其他前台app的内存需要

Log.v(TAG, “evicting oldest half of thumbnail cache”);

mCache.trimToSize(mCache.size() / 2);

}

}

这个callback只是建议,不一定会被系统调用。系统在内存紧张的时候可能会直接kill掉app而不去调用这个callback。但是如果这些callback可以调用的话,这将大大地提升我们app的用户体验。


善用Android自带容器


现在我们需要为这个app添加一些新特性,我们要让这个app可以进行收藏操作(为图片添加一个是否被收藏的属性即可)。我们会一次性收藏多张图片,那么我们可以使用GridView的多选模式。按照常理来说,我们可以使用HashMap()<Long, Boolean>来存储哪些照片需要被收藏。不过这里使用HashMap有点大材小用,效率不高。我们可以使用SparseBooleanArray等Android特有的容器来代替HashMap,节省系统开销(主要是autoboxing带来的开销)


SQLite中读写操作优化


当我们获得了要收藏的图片信息(保存在SparseBooleanArray中)之后,我们需要讲这些数据保存在SQLite当中,示例代码如下:

SQLiteDatabase db;

long[] mPhotoIds;

ContentValues values = new ContentValues();

for (long photoId : mPhotoIds) {

values.put(COLUMN_ID, photoId);

values.put(COLUMN_FAVORITE, favorite);

db.insert(TABLE_FAVORITE, null, values);

}

上面的代码中,每次insert都有开销。这个时候我们可以考虑使用transaction。代码如下:

SQLiteDatabase db;

long[] mPhotoIds;

ng photoId : mPhotoIds) {

values.put(COLUMN_ID, photoId);

values.put(COLUMN_FAVORITE, favorite);

db.insert(TABLE_FAVORITE, null, values);

}

上面的代码中,每次insert都有开销。这个时候我们可以考虑使用transaction。代码如下:

SQLiteDatabase db;

long[] mPhotoIds;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值