仿微信相册选择图片,查看大图,写的不太好,但愿评论指出不足,谅解,先介绍一下个人基本思路。
转载请注明出处: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
//优化显示效果
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 有时会耗时几秒钟,微信显示效果很是好,我本身参考微信的表现想出来的处理的方式是:
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 解析,思考与监控方案。
图片选择完成以后,完成善后工做,将 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/…。