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

  仿微信相册选择图片,查看大图,写的不太好,但愿评论指出不足,谅解,先介绍一下个人基本思路。
  转载请注明出处:blog.csdn.net/self_study/…
  对技术感兴趣的同鞋加群 544645972 一块儿交流。javascript

思路

第一步,获取全部图片路径

  第一步获取手机上的全部图片路径:java

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);
}复制代码

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

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

一个是所有图片的存储顺序,第二个是按照目录的图片存储顺序。github

第二步,压缩加载图片

  获取到图片以后,放入到 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 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 有时会耗时几秒钟,微信显示效果很是好,我本身参考微信的表现想出来的处理的方式是:

  1. 第一步,从应用的缓存 temp 目录下取,若是取不到,则进行下一步;
  2. 第二步,计算图片的压缩比例 samplesize,若是 samplesize < 4(根据表现通常的相机拍摄照片为 4000*3000,须要缩放 4 倍才能加速 decode 步骤),图片的 BitmapFactory.decodeFile() 时间短,直接返回图片,可是若是 samplesize > 4,执行第三步;
  3. 第三步则将压缩后的图片存入 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();
    }
}复制代码


问题到这里就差很少解决了;

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

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

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

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

第五步,退出相册页面清空 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 的操做。

显示效果

这里写图片描述
    
这里写图片描述

源码

  源码下载地址github.com/zhaozepeng/…

相关文章
相关标签/搜索