使用Android新式LruCache缓存图片,基于线程池异步加载图片

简介: import java.io.BufferedInputStream;import java.io.ByteArrayOutputStream;import java.
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

/*
 * 使用Android新式LruCache缓存图片,基于线程池异步加载图片。 
 * 基本思路:开辟一个线程池下载网络图片,同时创建一个LruCache作为Android内存缓存混存图片。
 * 上层应用传递过来一个URL要求从该URL下载图片时,首先检查LruCache中是否存在以该URL为索引的缓存图片,
 * 若有,则直接从缓存中读出来返回给上层应用;若没有,此时再开辟线程下载,下载完成后将此bitmap埋入缓存。
 * 备注:图片的url作为缓存图片时LruCache的 Key。
 * LruCache在内存中的缓存模型为<K,V>。
 */

public class AsyncImageLoader {

	private ExecutorService pool;
	private Handler handler;
	private ImageLoadedListener listener;

	private final int WHAT = 0xe001;

	// 默认的线程池容量
	private int DEFAULT_TASK_NUMBER = 10;

	// 网络超时时间:30秒
	private static int TIMEOUT = 30 * 1000;

	private LruCache<String, Bitmap> mMemoryCache;
	// 4MB缓存大小
	private final int CACHE_SIZE = 4 * 1024 * 1024;

	public AsyncImageLoader(int asyncTaskNumber) {
		initialization(asyncTaskNumber);
	}

	public AsyncImageLoader() {
		// 默认的构造函数初始化线程池容量为:TASK_NUMBER
		initialization(DEFAULT_TASK_NUMBER);
	}

	// 初始化
	private void initialization(int asyncTaskNumber) {
		mMemoryCache = new LruCache<String, Bitmap>(CACHE_SIZE) {
			@Override
			protected int sizeOf(String key, Bitmap value) {
				return value.getRowBytes() * value.getHeight();
			}
		};

		// 创建容量为 asyncTaskNumber 的线程池。
		pool = Executors.newFixedThreadPool(asyncTaskNumber);

		handler = new Handler() {
			@Override
			public void handleMessage(Message message) {
				switch (message.what) {
				case WHAT:
					listener.imageLoaded((Bitmap) message.obj);
				}
			}
		};
	}

	
	// 异步设置一个ImagView的Bitmap(该ImageView的Bitmap从网络加载)
	// 该方法为public,作为该工具类的外部调用接口。
	public void asyncSetImageBitmap(String url, final ImageView view) {
		download(url, new ImageLoadedListener() {
			@Override
			public void imageLoaded(Bitmap bitmap) {
				view.setImageBitmap(bitmap);
			}
		});
	}

	// 异步的从一个URL下载一个Bitmap。
	// 下载成功后,回调imageLoaded(Bitmap bitmap, String url);
	// 该方法为public,作为该工具类的外部调用接口。
	public void download(String url, ImageLoadedListener listener) {
		this.listener = listener;

		// 首先从缓存中检查是否存在以url为key的bitmap。
		// 若有,则直接从缓存中读取使用,不再使用线程重复加载。
		Bitmap bmp = mMemoryCache.get(url);
		if (bmp != null) {
			Log.d("读取缓存", url + " 已经缓存,无须重复下载!");
			sendResult(bmp);
			return;
		}

		Thread t = new DownloadThread(url);
		pool.execute(t);
	}
	

	// 开辟一个下载线程
	private class DownloadThread extends Thread {

		private String url;

		public DownloadThread(String url) {
			this.url = url;
		}

		@Override
		public void run() {
			Bitmap bmp = loadBitmapFromURL(url);

			// 将新的bitmap埋入缓存
			mMemoryCache.put(url, bmp);

			sendResult(bmp);
		}
	}

	// 发送消息通知:bitmap已经下载完成。
	private void sendResult(Bitmap bitmap) {
		Message message = handler.obtainMessage();
		message.what = WHAT;
		message.obj = bitmap;
		handler.sendMessage(message);
	}

	// 回调函数
	public interface ImageLoadedListener {
		public void imageLoaded(Bitmap bitmap);
	}

	// 给定一个URL,从这个URL下载Bitmap
	public static Bitmap loadBitmapFromURL(String url) {
		if (!url.contains("http://")) {
			url = "http://" + url;
		}

		Log.d("线程:" + Thread.currentThread().getId(), "开始下载: " + url);

		Bitmap bmp = null;
		try {
			byte[] imageBytes = loadRawDataFromURL(url);
			bmp = BitmapFactory.decodeByteArray(imageBytes, 0,
					imageBytes.length);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return bmp;
	}

	// 给定一个URL,从这个URL下载原始数据块。
	public static byte[] loadRawDataFromURL(String u) throws Exception {
		URL url = new URL(u);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		// 配置基础网络链接参数
		conn.setConnectTimeout(TIMEOUT);
		conn.setReadTimeout(TIMEOUT);

		InputStream is = conn.getInputStream();
		BufferedInputStream bis = new BufferedInputStream(is);

		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		final int BUFFER_SIZE = 1024 * 5;
		final int EOF = -1;

		int c;
		byte[] buf = new byte[BUFFER_SIZE];

		while (true) {
			c = bis.read(buf);
			if (c == EOF)
				break;

			baos.write(buf, 0, c);
		}

		conn.disconnect();
		is.close();

		byte[] data = baos.toByteArray();
		baos.flush();

		return data;
	}
}

相关文章
|
2月前
|
缓存 安全 Android开发
Android经典实战之用Kotlin泛型实现键值对缓存
本文介绍了Kotlin中泛型的基础知识与实际应用。泛型能提升代码的重用性、类型安全及可读性。文中详细解释了泛型的基本语法、泛型函数、泛型约束以及协变和逆变的概念,并通过一个数据缓存系统的实例展示了泛型的强大功能。
34 2
|
27天前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
46 20
Android经典面试题之图片Bitmap怎么做优化
|
28天前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
51 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
21天前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
42 8
|
21天前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
53 5
|
1月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
47 10
|
28天前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
1月前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
2月前
|
数据处理 开发工具 数据安全/隐私保护
Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路
本文探讨了Android平台上推流模块中添加文字与PNG水印的技术演进。自2015年起,为了满足应急指挥及安防领域的需求,逐步发展出三代水印技术:第一代为静态文字与图像水印;第二代实现了动态更新水印内容的能力,例如实时位置与时间信息;至第三代,则优化了数据传输效率,直接使用Bitmap对象传递水印数据至JNI层,减少了内存拷贝次数。这些迭代不仅提升了用户体验和技术效率,也体现了开发者追求极致与不断创新的精神。
|
2月前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享安卓与iOS开发中的线程管理比较
【8月更文挑战第30天】本文将探讨网络安全与信息安全的重要性,并分享关于网络安全漏洞、加密技术和安全意识的知识。我们将了解常见的网络攻击类型和防御策略,以及如何通过加密技术和提高安全意识来保护个人和组织的信息安全。