大量图片优化
最近在练习中用GridView加入相册中图片发现加入大量的相片之后,GirdView会变得很卡,想到或许可以用异步加载的方式来解决,但是能力有限,想得到却无法实现。在读了一些大牛的博客和代码之后,终于实现了。
1 在异步加载之前的代码的和普通加载代码一样,只需要在GirdView的Adapter的public View getView(int position, View convertView, ViewGroupparent)方法使用异步加载的方式返回ImageView。
2 如果能把加载过的图片给缓存起来,而不用每次都从sd卡上读取,这样效率应该会提高不少。所以可以先建一个缓存类,MemoryCache,为了能尽可能缓存,又尽可能的不抛出OOM的异常,可以使用SoftReference<Bitmap>,软引用来缓冲图片。MemoryCache类的代码如下:
/**
* 缓存类(图片的路径和图片)
*/
public class MemoryCache {
private HashMap<String, SoftReference<Bitmap>> cache=new HashMap<String, SoftReference<Bitmap>>();
//获取缓存图片
public Bitmap get(String path) {
if(cache.get(path)==null){
return null;
}else{
return cache.get(path).get();
}
}
//新增缓存
public void put(String url,Bitmap bitmap) {
cache.put(url, new SoftReference<Bitmap>(bitmap));
}
//清空缓存
public void clear() {
cache.clear();
}
}
3 弄完了缓存类,接下来就可以弄异步加载的类,这个类主要在维护一个Stack,每次向这个类请求一个ImageView时,就要传入对应ImageView和图片的路径。异步类首先会根据图片的路径先去缓存中查找是否有缓存对应的BItMap,如果有就把他放到ImageView返回,如果没有就把这个ImageView和图片路径放到Stack中,并唤醒加载图片的线程。而加载图片的线程(线程优先权低于UI线程),会无限循环查看Stack大小,如果为0,就进入等待。如果不为0,就依次出栈Stack中的元素进行处理。(感觉像生产者-消费者模式)。
3.1 接下来,就写这个异步类的变量和构造函数了:
private MemoryCache cache;
private PhotosStack photosStack;
private LoadImageThread loadImageThread;
//用来保存每个月ImageView和对应应加载的图片
private Map<ImageView, String> viewPath=Collections.synchronizedMap(new HashMap<ImageView, String>());
public LoadImage(Activity activity) {
super();
cache=new MemoryCache();
photosStack=new PhotosStack();
loadImageThread=new LoadImageThread(activity);
//设置异步加载线程优先权低于UI线程
loadImageThread.setPriority(Thread.NORM_PRIORITY);
}
其中MemoryCache是缓存类,PhotosStack类中维护一个Stack,LoadImageThread类是负责异步加载图片的。而viewPath这个变量是为了要保证GridView对应Position放对应的ImageView,如果没有这个变量,GridView中排列就会无序,在处理GridView的点击事件时候,就不好处理。而对viewPath的操作的异步,所以就需要线程安全咯。
PhotosStack代码如下:
//维护需要被填充的Image和对应图片路径的栈
class PhotosStack{
Stack<MyPhoto> stack=new Stack<LoadImage.MyPhoto>();
//移除指定ImageView
public void remove(ImageView iView) {
for(int i=0;i<stack.size();i++){
if(stack.get(i).imageView==iView){
stack.remove(i);
break;
}
}
}
}
其中MyPhoto是一个照片的实体类,有两个属性,图片的路径和对应的ImageView。
/**
* 照片的实体类
*/
class MyPhoto{
public String path;
public ImageView imageView;
public MyPhoto(String path, ImageView imageView){
this.path=path;
this.imageView=imageView;
}
}
3.2 接下来就可以实现给Adapter的调用的方法loadImage(Stringpath,ImageView iv ),接收两参数——图片路径和对应的ImageView。这个方法,首先会去缓存中查看是否有缓存对应的图片,如果有就把它设给ImageView。如果没有,就先为ImageView设个默认图片,然后以同步块(锁为PhotosStack中的stack)的方式加入PhotosStack中的stack中,并唤醒加载图片的线程。最后还要判断下加载图片的线程是否已经被启动了,如果没有,就启动。
/**
* 填充ImageView
* @param activity
* @param path 图片的路径
* @param iv 被填充的ImageView
*/
public void loadImage(String path,ImageView iv ) {
viewPath.put(iv, path);
Bitmap momeryBitmap=cache.get(path);
if(momeryBitmap!=null){//有缓存这张图片
iv.setImageBitmap(momeryBitmap);
}else{//没有缓存
iv.setImageResource(R.drawable.ic_launcher);
//有可以这个ImageVIew还没出栈,所以需要先出栈
photosStack.remove(iv);
//由于photosStack中的stack,出栈入栈操作时不会在异步的,所以需要在同步块中完成出入栈,并以photosStack.stack作为锁
synchronized (photosStack.stack) {
photosStack.stack.push(new MyPhoto(path, iv));
//唤醒持有锁的线程
photosStack.stack.notifyAll();
}
}
if(loadImageThread.getState()==Thread.State.NEW){
loadImageThread.start();
}
}
3.3 最后可以实现异步加载图片的方法了,主要是循环判断stack中元素的数量,如果为0 ,说明所有的图片已经被加载完毕了,可以进入等待状态。如果不为0,说明还有图片等待加载,就依次出栈这些元素,依次加载图片,并放到缓存中。代码如下:
//异步加载图片的线程
class LoadImageThread extends Thread{
Activity activity;
public LoadImageThread(Activity activity) {
this.activity = activity;
}
@Override
public void run() {
while(true){
try {
if(photosStack.stack.size()>0){
final MyPhoto photo;
//得到栈顶的MyPhoto
synchronized (photosStack.stack) {
photo= photosStack.stack.pop();
}
cache.put(photo.path,getSmallBitmap(photo.path) );
String ivPathString=viewPath.get(photo.imageView);
if(ivPathString!=null&& ivPathString.equalsIgnoreCase(photo.path)){
Runnable runableRunnable=new Runnable() {
@Override
public void run() {
photo.imageView.setImageBitmap(getSmallBitmap(photo.path));
}
};
activity.runOnUiThread(runableRunnable);
}
}
if(photosStack.stack.size()==0){
synchronized (photosStack.stack) {
photosStack.stack.wait();
}
}
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
其中缩小图片方法getSmallBitmap(String path),代码如下:
//缩小图片
public Bitmap getSmallBitmap(String path) {
Options options=new Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(path,options);
int REQUIRE_SIZE=80;
int scare=1;
while(true){
if(options.outWidth<=REQUIRE_SIZE|| options.outHeight<=REQUIRE_SIZE){
break;
}else{
options.outWidth=options.outWidth/2;
options.outHeight=options.outHeight/2;
scare++;
}
}
Options newoptions=new Options();
newoptions.inSampleSize=scare;
return BitmapFactory.decodeFile(path, newoptions);
}
其实这个也适合ListView加载大量图片,可以下载代码。
- 强大的API测试工具Hitchhiker v0.9 基于UI的断言测试,回顾2017
- 开源API测试工具 Hitchhiker v0.8 - 自动化测试结果统计
- 开源API测试工具 Hitchhiker v0.7更新 - Schedule的对比diff
- com.mysql.jdbc.exceptions.jdbc4.CommunicationsE...
- 简陋的swift carthage copy-frameworks 辅助脚本
- 【自问自答】关于 Swift 的几个疑问
- 高级PHP应用程序漏洞审核技术【一】
- 【读书笔记】The Swift Programming Language (Swift 4.0.3)
- Ubuntu 下mysql常用操作
- JDK1.8源码(五)——java.util.ArrayList 类
- 代码审计入门总结
- ubuntu mysql启动|停止|重启
- JDK1.8源码(四)——java.util.Arrays 类
- JDK1.8源码(三)——java.lang.String 类
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP各种常见经典算法总结【排序、查找、翻转等】
- php字符串截取函数mb_substr用法实例分析
- PHP利用DWZ.CN服务生成短网址
- php精度计算的问题解析
- PHP使用Session实现上传进度功能详解
- php常用字符串查找函数strstr()与strpos()实例分析
- php 根据URL下载远程图片、压缩包、pdf等文件到本地
- PHP使用HTML5 FormData对象提交表单操作示例
- python接入支付宝的实例操作
- php实现网页上一页下一页翻页过程详解
- Yii 使用intervention/image拓展实现图像处理功能
- PHP常用的类封装小结【4个工具类】
- 微信公众号之主动给用户发送消息功能
- laravel异步监控定时调度器实例详解
- python按顺序重命名文件并分类转移到各个文件夹中的实现代码