android仿最新版本微信相册--附源码

仿微信相册选择图片,查看大图,写的不太好,希望评论指出不足,谅解,先介绍一下我的基本思路。
  转载请注明出处:http://blog.csdn.net/self_study/article/details/69397859
  对技术感兴趣的同鞋加群 544645972 一起交流。

思路

第一步,获取所有图片路径

第一步获取手机上的所有图片路径:

Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver contentResolver = getContentResolver();
//获取jpeg和png格式的文件,并且按照时间进行倒序
Cursor cursor = contentResolver.query(uri, null, MediaStore.Images.Media.MIME_TYPE + "=\"image/jpeg\" or " +
        MediaStore.Images.Media.MIME_TYPE + "=\"image/png\"", null, MediaStore.Images.Media.DATE_MODIFIED+" desc");
if (cursor != null){
    while (cursor.moveToNext()){
        //do something
    }
    handler.sendEmptyMessage(0);
}

然后定义一个存储图片的数据格式:

/** 按时间排序的所有图片list */  
private ArrayList<SingleImageModel> allImages;  
/** 按目录排序的所有图片list */  
private ArrayList<SingleImageDirectories> imageDirectories;  
 /** 
 * 一个文件夹中的图片数据实体 
 */  
private class SingleImageDirectories{  
    /** 父目录的路径 */  
    public String directoryPath;  
    /** 目录下的所有图片实体 */  
    public ImageDirectoryModel images;  
}  

一个是全部图片的存储顺序,第二个是按照目录的图片存储顺序。

第二步,压缩加载图片

获取到图片之后,放入到 Gridview 中进行显示,但是 BitmapFactory.decodeFile() 函数会非常耗时,所以为了使得非常流畅的显示图片,创建一个类 AlbumBitmapCacheHelper,用来异步加载图片,该类使用 LruCache<String,Bitmap> 来缓存 Bitmap,使得存储图片不会造成 OOM,我这里设置 LruCache 的初始大小为 1/4 的运行时内存然后使用 ThreadPoolExecutor 线程池来处理图片的显示,线程池大小应该设置适中(可以根据系统的处理器线程数来设置,系统也提供一个相关的线程池,感兴趣的也可以去了解),做完这两件事情之后就可以用来加载图片了,方法 getBitmap 用来返回图片:

Bitmap bitmap = getBitmapFromCache(path, width, height);
//如果能够从缓存中获取符合要求的图片,则直接回调
if (bitmap != null) {
} else {
    //新建线程放入线程池去处理该图片的显示
}
return bitmap;

如果 cache 中找不到该图片,则调用 BitmapFactory.decodeFile() 去加载图片,加载图片不能够直接加载原图,会造成 OOM,所以要去计算压缩比:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = computeScale(options, width, height);
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(path, options);
//获取之后,放入缓存,以便下次继续使用
if (bitmap != null && cache!=null) {
    cache.put(path, bitmap);
}

方法 computeScale() 主要是计算图片最小的压缩比,这样在 Gridview 中的 getview 方法中去调用 AlbumBitmapCacheHelper 的 getBitmap() 方法即可。

第三步,处理显示问题

经过上面的处理之后,在实际显示的时候会出现一些问题,这里也汇总和分析一下:

显示图片闪烁

第一个问题就是图片显示会闪烁,这主要是由于 getview 方法的 convertView 的复用导致一个 Imageview 被设置多次 background,解决方法就是使用 setTag 方法:

holder.iv_content.setTag(path);

将要显示的 Imageview 的 tag 设置为需要显示的图片路径,这样在回调的时候使用方法 gridView.findViewWithTag(path),找到这个 Imageview 进行显示,闪烁的问题就解决了。

大图片加载速度缓慢

第二个问题就是加载速度很慢,拉的速度很快的情况下,图片要很久才会加载出来,特别是很大的图片,比如拍照和截图的照片,这个问题可以有两个步骤去优化:第一个优化方案就是在 AlbumBitmapCacheHelper 类中维护一个 ArrayList<String> currentShowString,在 getView 方法中,如果该图片要显示,则直接将 path 加入到该 list 中,同时如果这个 view 的 tag 不为空,说明该 view 的原来的 path 是不需要显示的,所以需要将这个 path 从 ArrayList 中删除:

//优化显示效果
if(holder.iv_content.getTag() != null) {
    String remove = (String) holder.iv_content.getTag();
    AlbumBitmapCacheHelper.getInstance().removePathFromShowlist(remove);
}
AlbumBitmapCacheHelper.getInstance().addPathToShowlist(path);

这样在线程池中的处理方式就是先查看需要显示的 path 是否在 ArrayList 中,如果没有在 ArrayList 中,则该线程直接关闭,如果在 ArrayList 中,则显示该图片:

if (!currentShowString.contains(path)||cache==null) {
         return;
}

第二个优化方案是如果显示的图片很大,特别是拍照和截图的图片,decode 有时会耗时几秒钟,微信显示效果非常好,我自己参考微信的表现想出来的处理的方式是:<ol><li>第一步,从应用的缓存 temp 目录下取,如果取不到,则进行下一步;</li><li>第二步,计算图片的压缩比例 samplesize,如果 samplesize < 4(根据表现一般的相机拍摄照片为 4000*3000,需要缩放 4 倍才能加速 decode 步骤),图片的 BitmapFactory.decodeFile() 时间短,直接返回图片,但是如果 samplesize > 4,执行第三步;</li><li>第三步则将压缩后的图片存入 temp 目录下,以便下次快速取出,这样微信图片展示的效果就出来了,显示的速度和微信一样,第一次大图加载慢之外,之后的显示就能很快:

if (!new File(CommonUtil.getDataPath()).exists())
    new File(CommonUtil.getDataPath()).mkdirs();
//临时文件的文件名
String tempPath = CommonUtil.getDataPath() + hash + ".temp";
//如果该文件存在
if (new File(tempPath).exists())
    bitmap = BitmapFactory.decodeFile(tempPath);
......
//第三步,如果缩放比例大于4,该图的加载会非常慢,所以将该图保存到临时目录下以便下次的快速加载
if (options.inSampleSize >= 4) {
    try {
        File file = new File(tempPath);
        if (!file.exists())
            file.createNewFile();
        FileOutputStream fos = new FileOutputStream(file);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
        fos.write(baos.toByteArray());
        fos.flush();
        fos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

</li></ol>
问题到这里就差不多解决了;

第四步,详情页面大图的展示

第四步大图的查看,大图主要是使用网上开源的 ZoomImageView+Viewpagger 的组合,但是使用这个出现的问题就是很容易 OOM,没办法,我的处理方式就是在点进去大图的时候:

public void releaseHalfSizeCache() {  
    cache.resize((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));  
}  

直接将 LruCache 的大小变成原来的一半,因为查看大图页加载一张大图占用的内存本身就很大,所以这样去特殊处理,显示效果页还凑合,有别的方法,一定要留言告诉我。
  <font color='red'>还有一点需要注意的是大图的查看由于需要通过 intent 传递数据,但是 intent 传递的数据大小不能太大,如果手机上有几千张图片,则数据量大小可能会超过 intent 所能传递的最大量,所以可以写入一个公共的地方,共享内存、数据库或者文件都可以,具体的原因可以参考我的另一篇博客:Android TransactionTooLargeException 解析,思考与监控方案。</font>

第五步,退出相册页面清空 LruCache

图片选择完成之后,完成善后工作,将 AlbumBitmapCacheHelper 类中 LruCache 清空,差不多就这样了,还有很多的功能小点,比如图片时间的显示,这里就不详细一一去介绍了,具体大家看源码。

问题讨论

最新发现的问题:3.0 以前 GC 操作需要很长时间,经常大于 100ms,在执行 GC 时(关于 JVM 和 ART 的 GC 可以看看我的这篇博客 Android 性能优化之内存泄漏检测以及内存优化(上)),程序就会出现卡的现象,3.0 以后 GC 修改为同步,执行的时间通常在 5ms 以内,在 3.0 以前的版本中,加载图片时,系统把 Bitmap 加载到 Native 缓存中,并不受 GC 管理,需要手机自己释放,不然会遇到莫名奇妙的内存问题。3.0 以后 Bitmap 直接放到内存中在执行 GC 时,会及时清理无用的 Bitmap 所占的内存,在初始化图片时把图片放到内存中,当加载完后,系统会把图片从内存转移到显存中,当你用内存测试工具时,会发现在加载图片时,内存占用率很高,当加载完成后,内存使用量突然下来,加载大量图片时会发现这种情况。
  总而言之就是 2.x 版本的时候,就算你使用的是 LruCache,Bitmap 还是不会被 GC 主动回收,必须要手动释放,所以如果需要适配 2.x 版本,该 demo 需要加入手动释放 Bitmap 的操作。

显示效果

这里写图片描述

    
这里写图片描述

源码

源码下载地址https://github.com/zhaozepeng/AlbumImageSelect

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容