Android开发之图片处理专题(二):利用AsyncTask和回调接口实现图片的异步加载和压缩

在上一篇专题Android开发之图片处理专题(一):利用软引用构建图片高速缓存中我们讲述了如何利用软引用技术构建高速缓存。那么想要用到图片,首先得有图片的来源。一般而言,一个应用的图片资源都是从服务器处获得的。今天,我们利用Android开发之网络请求通信专题(二):基于HttpClient的文件上传下载里面封装好的httpUtils来实现图片的下载,然后加载到本地配合软引用缓存使用,以一个listView为例子来说明。

一、准备工作

我们需要准备以下几个类(图片对象和软引用缓存类请参考上一篇专题):

1、图片下载类ImageDownload

2、图片下载工具FreedomHttpClientUtil(参考:Android开发之网络请求通信专题(二):基于HttpClient的文件上传下载

3、图片下载异步处理任务LoadTask

4、回调接口FreedomCallBack

二、工具类的编写

1、回调接口的准备

public interface FreedomCallback {
	/**
	 * @Title: imageLoaded
	 * @Description: TODO
	 * @param imageDrawable 传回的bitmap对象
	 * @param tag 用于listView查找控件
	 * @throws
	 */
	public void imageLoaded(Bitmap imageDrawable, Object tag);
}

2、图片下载类和异步处理任务

图片下载异步处理任务我们将其做成是图片下载类的内部类来实现。

public class ImageDownload {
	private static final String TAG = "ImageDownload";
	private static String path;
	//构建缓存地址
	static {
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
			File externalStorageDirectory = Environment
					.getExternalStorageDirectory();
			path = externalStorageDirectory.getAbsolutePath()
					+ ConstantValue.IMAGE_PATH;
			File directory = new File(path);
			if (!directory.exists()) {
				directory.mkdirs();
			}
		} else {
			path = null;
			Log.e(TAG, "no sdcard.");
		}
	}

	/**
	 * @Title: loadDrawable
	 * @Description: 下载图片
	 * @param imageUrl
	 * @param simpleName
	 * @param imageCallback
	 * @throws
	 */
	public void loadDrawable(String imageUrl, String simpleName,
			FreedomCallback imageCallback) {
		new FreedomLoadTask(imageCallback).execute(imageUrl, simpleName);
	}

	/**
	 * @Title: loadImageFromUrl
	 * @Description: 根据地址下载图片
	 * @param url
	 * @return
	 * @throws
	 */
	public Bitmap loadImageFromUrl(String url) {
		InputStream i = null;
		try {
			FreedomHttpClientUtil util = new FreedomHttpClientUtil();
			util.open(url, FreedomHttpClientUtil.GET);
			util.post();
			i = util.openInputStream();
			//拿到流后创建bitmap
			Bitmap bitmap = BitmapFactory.decodeStream(i);
			return bitmap;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * @Title: saveImage2SDCard
	 * @Description: 将图片存放到SD卡中
	 * @param simpleName
	 * @param bitmap
	 * @throws
	 */
	public void saveImage2SDCard(String simpleName, Bitmap bitmap) {
		if (StringUtils.isNotBlank(path)) {
			File file = new File(new File(path), simpleName + ".png");

			FileOutputStream fOut = null;
			try {
				fOut = new FileOutputStream(file);
				//将bitmap写入流中
				bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
				fOut.flush();
			} catch (Exception e) {
				org.cao.optimization.util.Log.info("文件无法创建");
				e.printStackTrace();
			} finally {
				if (fOut != null)
					try {
						fOut.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
			}

		}
	}

	/**
	 * @ClassName: FreedomLoadTask
	 * @author victor_freedom ([email protected])
	 * @createddate 2015-1-31 下午11:39:18
	 * @Description: 图片下载,返回一个bitmap对象
	 */
	class FreedomLoadTask extends AsyncTask<String, Integer, Bitmap> {
		private FreedomCallback imageCallback;

		public FreedomLoadTask(FreedomCallback imageCallback) {
			this.imageCallback = imageCallback;
		}

		@Override
		protected Bitmap doInBackground(String... params) {
			// 传入两个 参数,一个是下载路径,一个是文件名称
			Bitmap bitmap = loadImageFromUrl(params[0]);
			if (bitmap != null) {
				// 保存到SD卡中
				saveImage2SDCard(params[1], bitmap);
				// 加入缓存中
				ImageCache.getInstance().cacheImage(
						new Image(params[1], bitmap));
			}
			return bitmap;
		}

		@Override
		protected void onPostExecute(Bitmap result) {
			// 下载成功后执行回调接口
			imageCallback.imageLoaded(result, null);
			super.onPostExecute(result);
		}
	}

}

三、界面处理

1、Adapter处getView处理

我们假设adater只有一个ImageView,利用ViewHolder缓存,那么在getView方法里面,我们这样处理:

	//拿到图片的名字
		final String id = infos.getName();
		//给此处ImageView空间设置id
		continer.image.setTag(id);
		//根据名字首先从软应用中拿到bitmap
		Image image = ImageCache.getInstance().getImage(id);
		Bitmap bitmap = image.getBitmap();
		//判断bitmap是否为空
		if (bitmap == null) {

			// 判断本地SDCard中是否有相关文件
			if (Image.hasFile(id)) {
				InputStream is;
				try {
					is = new FileInputStream(Image.getFileFromSDCard(id));
					bitmap = BitmapFactory.decodeStream(is);
					ImageCache.getInstance().cacheImage(new Image(id, bitmap));
					continer.image.setImageBitmap(bitmap);
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
			} else {
				//本地也没有,就从服务器下载
				ImageDownload download = new ImageDownload();
				download.loadDrawable(news.getImgUrl(), id + "",
						new FreedomCallback() {

							@Override
							public void imageLoaded(Bitmap imageDrawable,
									Object tag) {
								if (imageDrawable != null) {
									//构建adapter时传入的回调接口,如果下载图片成功,回调此方法
									listCallback.imageLoaded(imageDrawable, id);
								}
							}
						});
				continer.image.setImageResource(R.drawable.ic_launcher);
			}

		} else {
			//如果软引用还存在,则直接拿来用
			continer.image.setImageBitmap(bitmap);
		}

2、ListView初始化相关处理

	adapter = new NewsAdapter(context, new ListCallback() {

			@Override
			public void imageLoaded(Bitmap bitmap, Object tag) {
				//根据回调传回的tag找到对应的ImageView
				ImageView imageView = (ImageView) listView.findViewWithTag(tag);
				if (imageView != null) {
					imageView.setImageBitmap(bitmap);
				}
			}
		});

		listView = new ListView(context);
		listView.setAdapter(adapter);

四、图片压缩

在上述过程中,我们还可以优化一下下。上述操作过程中,我们默认了下载下来的图片的大小是已经设定好的。当然,在实际开发中,我们下载再来的图片,可能也是和服务端沟通好设定好了。但是,有时候,图片下载下来很大,这个时候,就需要对图片进行压缩了。一般而言,图片的压缩有两种,质量压缩和宽高压缩。我们来看一个工具类。这是博主以前不知道从哪里找来的了。

	//图片质量压缩
	public static Bitmap compressImage(Bitmap image) {

		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
		int options = 100;
		while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
			baos.reset();// 重置baos即清空baos
			image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
			options -= 10;// 每次都减少10
		}
		ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
		Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
		return bitmap;
	}

	//图片宽高压缩
	public static Bitmap getimage(String srcPath) {
		BitmapFactory.Options newOpts = new BitmapFactory.Options();
		// 开始读入图片,此时把options.inJustDecodeBounds 设回true了
		newOpts.inJustDecodeBounds = true;
		Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空

		newOpts.inJustDecodeBounds = false;
		int w = newOpts.outWidth;
		int h = newOpts.outHeight;
		// 这个设置图片的宽高,根据实际需要设定
		float hh = 800f;// 这里设置高度为800f
		float ww = 480f;// 这里设置宽度为480f
		// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
		int be = 1;// be=1表示不缩放
		if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放
			be = (int) (newOpts.outWidth / ww);
		} else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放
			be = (int) (newOpts.outHeight / hh);
		}
		if (be <= 0)
			be = 1;
		newOpts.inSampleSize = be;// 设置缩放比例
		// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
		bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
		return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩
	}

有时候,我们还可以让系统自动的给我们压缩,只要确保不会出现OOM错误又能让图片以最大性能去展示

			File file = Image.getFileFromSDCard(name, "pure");
			Options opts = new Options();
			opts.inJustDecodeBounds = true;
			BitmapFactory.decodeFile(file.getAbsolutePath(), opts);
			opts.inJustDecodeBounds = false;
			// 根据设定的值让系统自动压缩,这行代码的参数是第一个是BitmapFactory.Options,第二个参数是调整后图片最小的宽或者高,第三个参数是调整后图片的内存占用量上限。
			opts.inSampleSize = Util.computeSampleSize(opts, 600,(int) (1 * 1024 * 1024));
			opts.inDither = false;
<span style="white-space:pre">			</span>opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
			bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), opts);

图片压缩的步骤一般设定在图片下载完之后。

至此,图片从下载到显示的所有流程,我们已经实现。当然,这只是一种实现方式,图片的异步加载还可以有其他的实现方式。今天我们回顾了Android开发之网络请求通信专题(二):基于HttpClient的文件上传下载的内容里封装的httputils,下一篇我们利用之前所学的线程池ThreadPoolExcutor配合Android开发之网络请求通信专题(一):基于HttpURLConnection的请求通信里所讲的httpurlconection来实现listview的大量图片的异步加载。

时间: 01-29

Android开发之图片处理专题(二):利用AsyncTask和回调接口实现图片的异步加载和压缩的相关文章

Android实战简易教程-第四十九枪(两种方式实现网络图片异步加载)

加载图片属于比较耗时的工作,我们需要异步进行加载,异步加载有两种方式:1.通过AsyncTask类进行:2.通过Handler来实现,下面我们就来看一下如何通过这两种方式实现网络图片的异步加载. 一.AsyncTask方式 1.main.xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.co

Android开发之图片处理专题(三):利用ThreadPoolExcutor线程池实现多图片的异步加载

在上一篇专题Android开发之图片处理专题(二):利用AsyncTask和回调接口实现图片的异步加载和压缩中我们实现了listView的图片的大量加载.今天,我们换一种方式,采用线程池的方式来实现. 我们需要准备两个东西: 1.图片下载任务类 2.线程池. 1.图片下载任务类. 图片下载任务类,将需要显示的iamgeView,线程通讯消息管理者handler进行了封装.当图片下载无论成功还是失败,handler发送对应的消息,传入的iamgeView显示对应的图片.这里就不在应用软引用技术,采

Android之数据存储----使用LoaderManager异步加载数据库

一.各种概念: 1.Loaders: 适用于Android3.0以及更高的版本,它提供了一套在UI的主线程中异步加载数据的框架.使用Loaders可以非常简单的在Activity或者Fragment中异步加载数据,一般适用于大量的数据查询,或者需要经常修改并及时展示的数据显示到UI上,这样可以避免查询数据的时候,造成UI主线程的卡顿. 即使是查询SQLite数据库,用Loaders来操作会更加的简便. Loaders有以下特点: 可以适用于Activity和Fragment. 可以提供异步的方式

Android图片异步加载框架Universal Image Loader的源码分析

项目地址:https://github.com/nostra13/android-universal-image-loader 1. 功能介绍 1.1 Android Universal Image Loader Android Universal Image Loader 是一个强大的.可高度定制的图片缓存,本文简称为UIL. 简单的说 UIL 就做了一件事--获取图片并显示在相应的控件上. 1.2 基本使用 1.2.1 初始化 添加完依赖后在Application或Activity中初始化I

Android之ListView异步加载图片且仅显示可见子项中的图片

折腾了好多天,遇到 N 多让人崩溃无语的问题,不过今天终于有些收获了,这是实验的第一版,有些混乱,下一步进行改造细分,先把代码记录在这儿吧. 网上查了很多资料,发现都千篇一律,抄来抄去,很多细节和完整实例都没看到,只有自己一点点研究了,总体感觉 android 下面要显示个图片真不容易啊. 项目主要实现的功能: 异步加载图片图片内存缓存.异步磁盘文件缓存解决使用 viewHolder 后出现的图片错位问题优化列表滚动性能,仅显示可见子项中的图片无需固定图片显示高度,对高度进行缓存使列表滚动时不会

ListView与GridView异步加载图片

原理很简单,主要是用到了回调方法,下面是异步加载图片的类 <span style="font-size:16px;">package com.xxx.xxx; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.URL; import java.util.HashMap; import android.graphics.drawable.Drawable; impor

基于jQuery的图片异步加载和预加载实例

如今的网页中有很多图片,比如相册列表,那么如果一次性读取图片将会瞬间加重服务器的负担,所以我们用jQuery来实现图片的异步加载和预加载功能,这样在页面的可视范围内才会加载图片,当拖动页面至可视界面时,其他图片才会加载,改插件很好地实现了图片异步加载功能. 在线预览   源码下载 html代码部分: <div id="content"> <div id="button"> <ul> <li>小图</li>

滚屏异步加载图片

<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> </style> </head> <body> <div id="image"> </div> <script src=&qu

Android利用Volley异步加载数据完整详细示例(二)

MainActivity如下: package cc.y; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.os.Bundle; import android.util.LruCache; import android.widget.ImageView;