一、概述

讲解优化查询相册图片之前,我们先来看下PM提出的需求,PM的需求很简单,就是要做一个类似微信的本地相册图片查询控件,主要包含两个两部分:

进入图片选择页面就要显示出手机中所有的照片,包括系统相册图片和其他目录下的所有图片,并按照时间倒叙排列

切换相册功能,切换相册页面列出手机中所有的图片目录列表,并且显示出每个目录下所有的图片个数以及封面图片

这两个需求看似简单,实则隐藏着一系列的性能优化问题。在做优化之前,我们调研了一些其他比较出名的app在加载大数量图片的性能表现(gif录制的不够清晰,但展示问题已经够了):

下面测试了几个常用软件

微信:

微信的图片查询速度还是非常快的,基本上进入图片选择页面,相册数据就已经查出来了,包括各个图片目录下图片的个数和封面图片的url,这个体验还是比较好的。

新浪微博:

相比较微信来说,新浪微博做的体验就比较差了,进入图片选择页面后,先是黑屏然后是白屏,连个进度条都没有,让用户以为app死掉了,等过一段时间才显示出来,这个体验较差

QQ:

QQ一上来是加载的最近100张照片,这个速度非常快,但是进入Camera相册(有5000多张)后,有一个进度条等待,我体验了下,等待的时间还是比较长的,这个体验比新浪微博稍微好点,比微信差

闲鱼:

闲鱼是做的最烂的一个,一上来是卡死四五秒,然后是黑屏两三秒,最后才显示出来

二、综合对比

经过综合对比后,就微信做的还比较好,基本上进入相册页面就能展示出所有照片,相册目录也非常快的展示出来!!!

经过我们的调研,发现微信是采用循环分页加载策略,我们优化的思路也是采用这种策略,先看优化后的效果图:

进入图片选择页面,图片能够非常快的显示出来,进入更换相册页面,图片目录也能非常快的显示出来,这里没有像微信一样做图片目录的缓存:一是因为查询速度非常快,基本上不到2秒就加载出来了,二是能够实时刷新出相册的最新数据

频繁的切换各个相册目录,图片都能非常快速的查询出来,体验还是不错的!!!

三、优化实现

优化查询相册目录

因为要列举出所有的相册目录列表,这里没有其他好的办法,直接请求ContentResolver的query方法来查询,这里为了加速查询,去掉了while循环中一些耗时的判断,将一些检测图片是否判断的逻辑移到外面去,具体用的时候再去判断

查询图片的URI

MediaStore.Images.Media.EXTERNAL_CONTENT_URI

因为我们只查询图片url和图片所在的目录

String[] projection = {MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME};

PM要求相册按照图片的时间倒叙排列,图片的创建、修改会影响其所在目录的排序,排序按时间倒叙排列

String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC ";

根据这些查询条件,经过query之后得到一个Cursor,这个cursor里面就包含我们所需要的所有图片的信息,然后我们while循环遍历这个cursor,在while循环中一定不能有耗时操作

//一个辅助集合,防止同一目录被扫描多次

HashSet dirPaths = new HashSet();

while (cursor.moveToNext()) {

// 获取图片的路径

String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));

String bucketName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME));

if (TextUtils.isEmpty(allFolderItem.coverImagePath)) {

allFolderItem.coverImagePath = path;

}

File parentFile = new File(path).getParentFile();

if (parentFile == null) continue;

String dirPath = parentFile.getAbsolutePath();

PicFolderItem folderItem = null;

// 利用一个HashSet防止多次扫描同一个文件夹(不加这个判断,图片多起来还是相当恐怖的~~)

if (dirPaths.contains(dirPath)) {

continue;

} else {

dirPaths.add(dirPath);

boolean isNew = true;

//判断一下是否dirPath不同,但是bucketName相同

for (PicFolderItem item : picList) {

if (item.name.equals(bucketName)) {

folderItem = item;

item.addParentPath(dirPath);

isNew = false;

break;

}

}

if (isNew) {

folderItem = new PicFolderItem();

folderItem.coverImagePath = path;

folderItem.name = bucketName;

folderItem.addParentPath(dirPath);

}

}

String[] array = parentFile.list(new FilenameFilter() {

@Override

public boolean accept(File dir, String filename) {

if (filename.endsWith(".jpg")

|| filename.endsWith(".png")

|| filename.endsWith(".jpeg"))

return true;

return false;

}

});

int arrayCount = array == null ? 0 : array.length;

folderItem.count += arrayCount;

if (!picList.contains(folderItem) && arrayCount > 0) {

picList.add(folderItem);

}

}

这样就能非常快速的查询出手机中所有的图片目录、目录的图片张数以及封面图url。这里主要优化了三点:

while循环中去除耗时判断

之前的代码中存在判断文件图片是否存在的代码:

public static boolean isFileExist(String path) {

File file = new File(path);

if (file == null || !file.exists()) {

return false;

}

return true;

}

这段代码放到while循环中是很恐怖的,我测试了下,5000多张图片都要检测的话总时间会增加三四秒。这个判断可以放到外面去,具体操作哪一个图片的时候再做具体的业务判断!

防止一个图片文件夹被扫描多次

这里添加了一个变量来存储已经扫描过的图片目录,已经扫描的就不在处理了:

//一个辅助集合,防止统一目录查询多次

HashSet dirPaths = new HashSet();

这块优化了之后效果还是很明显的,相同的目录不会扫描多次!

获取图片目录下图片个数

String[] array = parentFile.list(new FilenameFilter() {

@Override

public boolean accept(File dir, String filename) {

if (filename.endsWith(".jpg")

|| filename.endsWith(".png")

|| filename.endsWith(".jpeg"))

return true;

return false;

}

});

这个file.list()方法内部是一个native方法,查询效率非常快!!!

当然获取某个目录下的图片有多少张也可以通过cursor查询的方式来获取!!!

查询某个相册目录下的所有照片

在介绍查询目录下的照片之前,我们先介绍下我们查询图片的两种策略,一种是针对目录下图片比较多的,动不动就上千上万张的那种;另一种是那种目录下图片比较少的,就几百张图片

一次加载策略

当目录下图片数量小于1000张时采用file.list这个native方法来一次加载所有图片,这个native查询效率非常快,上千张图片都是秒级查询出来

循环分页加载策略

当图片数量大于等于1000张时采用循环分页加载策略,这种策略专门针对图片数量特别多的情况,通过分页的方式先把第一页的图片加载出来,让用户能第一眼看到最新的图片,然后后台异步循环的查询下一页图片,直到所有图片都查询完成,这也是微信的查询相册策略。

一次加载策略实现

我们这里看下一次加载完策略实现代码,首先通过File的list方法将后缀为图片格式的文件过滤出来,返回一个图片路径数组

File dirFile = new File(dir);

String[] list = dirFile.list(new FilenameFilter() {

@Override

public boolean accept(File dir, String filename) {

if (filename.endsWith(".jpg") || filename.endsWith(".png")

|| filename.endsWith(".jpeg"))

return true;

return false;

}

});

因为我们要的是按时间倒叙进行排列的数组,所以要对上面查询出来的数组进行排序,这里用到了File文件lastModified方法

Collections.sort(strings, new Comparator() {

@Override

public int compare(String lhs, String rhs) {

Long time1 = new File(lhs).lastModified();

Long time2 = new File(rhs).lastModified();

return time2.compareTo(time1);

}

});

循环分页加载策略实现

这个策略借鉴了微信,通过分页的方式来一页一页的加载图片,直到所有的图片都加载完成。

这里的核心就是查询条件,将你要查询的某个目录添加到查询参数中

String selection = MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME + " = '" + 目录名称 + "' ";

这个selection一定不能写错,不然查询不出来

因为要分页,sortOrder不是简单的按照时间倒叙来排了

String sortOrder = MediaStore.Images.Media.DATE_TAKEN + " DESC limit " + PAGE_SIZE + " offset " + pageIndex * PAGE_SIZE;

最后对Cursor进行循环遍历拿到我们要的图片路径

PAGE_SIZE是个常量,表示我们要一次查询多少条,我们这里定的是200,一次查询200条数据,pageIndex是查询第几页,从0开始

一开始的时候查询第一页的数据,当查询的数据列表大小大于等于我们要查询的PageSize大小时,我们就认为有下一页,pageIndex加1循环查询下一页,直到查询的列表大小小于PageSize。

经过上面几步优化后,加载本地相册图片基本上就没有什么问题了。我们经过真机测试,图片5549张,都能够非常快速的查询出来,堪比微信和图库。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

android 从相册读取多张图片大小,Android优化查询加载大数量的本地相册图片相关推荐

  1. Android_优化查询加载大数量的本地相册图片

    一.概述 讲解优化查询相册图片之前,我们先来看下PM提出的需求,PM的需求很简单,就是要做一个类似微信的本地相册图片查询控件,主要包含两个两部分: 进入图片选择页面就要显示出手机中所有的照片,包括系统 ...

  2. Android手机内存图片读取,有效解决Android加载大图片内存溢出的问题

    今天在交流群里,有人问我他经常遇到加载图片时内存溢出的问题,遇到的情况还是在自己的测试机或者手机里没有问题,做好了, 到了客户手机里就内存溢出了.其实有时候不同的手机和不同的系统对内存的要求不一样,尤 ...

  3. Android官方开发文档Training系列课程中文版:高效显示位图之加载大位图

    原文地址:http://android.xsoftlab.net/training/displaying-bitmaps/index.html 引言 学习如何使用一种常规的手段来处理及加载Bitmap ...

  4. Android 高清加载巨图方案 拒绝压缩图片

    Android 高清加载巨图方案 拒绝压缩图片 转载请标明出处:  http://blog.csdn.net/lmj623565791/article/details/49300989:  本文出自: ...

  5. Android加载大图片不OutOfMemoryError

    Android加载图片时,对于分辨率小,配置低的机子,很容易发生OutOfMemoryError.手机的内存比图片的大很多,怎么会这样? 在设置Android虚拟机的内存时: RAM:模拟器的内存空间 ...

  6. Android加载大图片OOM异常解决

    Android加载大图片OOM异常解决 参考文章: (1)Android加载大图片OOM异常解决 (2)https://www.cnblogs.com/jevan/archive/2012/07/05 ...

  7. 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案

    [走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 参考文章: (1)[走过巨坑]android studio对于jni调用及运行闪退无法加载库的问题解决方案 ( ...

  8. android 代码 lut,Android BitmapFactory.decodeResource()错误或用于加载大LUT的替代选项...

    我正在创建一个图像处理应用程序,它需要一些相当大的查找表.目前,我已将表格保存为应用程序资源/原始目录中的.txt文件,作为值为3x4913的数组(3代表R,G,B).在应用程序启动时加载所有这些(9 ...

  9. 【转载】Android加载大图片OOM异常解决

    官方资料: https://developer.android.com/topic/performance/graphics/load-bitmap 思路 先测试未知来源图片的尺寸和MIME文件类型; ...

最新文章

  1. Divan and a Store 贪心(800)
  2. Java 定时任务调度(8)--ElasticJob 入门实战(ElasticJob-Lite使用)
  3. leetcode 566. 重塑矩阵(Java版,坐标转换)
  4. 利用CSS、JavaScript及Ajax实现图片预加载的三大方法及优缺点分析
  5. 关于Java基础你不得不会的34个问题
  6. for循环语句例题及解析python_Python入门第8课,for语句综合练习,突破循环累加难点...
  7. 计算机图形学考试题及答案_中国大学 MOOC_计算机图形学_测试题及答案
  8. 将文件复制到ftp发生错误 请检查是否有权限_SE文件管理器2.8.6解锁完整功能版...
  9. vs2010 C#链接 ACCESS数据库
  10. 201521123004 《Java程序设计》第2周学习总结
  11. CridView创建 实现选中,编辑,取消,删除
  12. Atitit 数据库的历史与未来 目录 1.1. 两个对于数据库强需求的行业。电信 金融 1 1.2. 艾提拉分析 对数据库强需求行业金融 1 2. 数据库历史 2 2.1. ,上个世纪50,6
  13. Linux系统下下载Tomcat详细步骤。
  14. 【CSS】对话框--禁止弹框/对话框蒙层下方内容滚动
  15. 详解Runtime运行时机制
  16. 过cloudflare,使用cfscrape,以及cfscrape挂代理的方式
  17. 物联网智能家居基本方法实现之经典
  18. html5 全景 源码,krpano 全景图 html5源码
  19. 骁龙8gen1和苹果A15评测哪个好
  20. Idea配置与项目导入(Java 与 Python 学习通法)

热门文章

  1. AV1标准特色编码工具简介
  2. 表白墙网站php源码,自适应响应式表白墙网站PHP源码 带后台管理
  3. c++版本的高斯混合模型的源代码完全注释
  4. Apollo record文件格式
  5. SpringBoot与knif4j学习
  6. 七步成诗(事)- 定义问题
  7. 到底什么是链接,它起到了什么作用?
  8. 2017鸡年女宝宝名字大全,来给你的女宝宝找个好名字
  9. 整理了一周的Python资料,包含各阶段所需网站、项目,收藏了慢慢来
  10. 数据中台方向创业者上海小胖的采访记