Android 图片加载及缓存技术解析
来源:互联网 发布:单片机usb通讯 编辑:程序博客网 时间:2024/05/21 17:49
写的一个图片缓存的demo,包括内存缓存和硬盘缓存,加载大量图片的时候感觉效果还是挺好的。
直接上代码吧:
package com.hongri.recyclerview.fragment;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v4.app.Fragment;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import com.hongri.recyclerview.R;import com.hongri.recyclerview.adapter.DetailLoadPicsTestAdapter;import com.hongri.recyclerview.common.APPConstants;import com.hongri.recyclerview.utils.DataUtil;/** * @author:zhongyao on 2016/7/22 10:52 * @description:网络加载图片性能测试 */public class DetailLoadPicsTestFragment extends Fragment{ private static DetailLoadPicsTestFragment loadPicsTestFragment; private RecyclerView rv; private DetailLoadPicsTestAdapter mAdapter; private DetailLoadPicsTestFragment(){} @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_detail_loadpicstest,container,false); initView(view); return view; } private void initView(View view) { rv = (RecyclerView) view.findViewById(R.id.rv); rv.setLayoutManager(new GridLayoutManager(getActivity(), APPConstants.Column_Nums)); mAdapter = new DetailLoadPicsTestAdapter(getActivity(), DataUtil.getImageThumbUrls()); rv.setAdapter(mAdapter); } public static Fragment newInstance() { if (loadPicsTestFragment == null){ synchronized (DetailLoadPicsTestFragment.class){ if (loadPicsTestFragment == null){ loadPicsTestFragment = new DetailLoadPicsTestFragment(); } } } return loadPicsTestFragment; }}
package com.hongri.recyclerview.adapter;import android.content.Context;import android.graphics.Bitmap;import android.os.Handler;import android.support.v7.widget.RecyclerView;import android.support.v7.widget.RecyclerView.ViewHolder;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import com.hongri.recyclerview.R;import com.hongri.recyclerview.cache.BitmapCache;import com.hongri.recyclerview.cache.BitmapDiskCache;import com.hongri.recyclerview.cache.ImageWorker;import com.hongri.recyclerview.https.HttpRequest;import com.hongri.recyclerview.https.IHttpRequest;import com.hongri.recyclerview.utils.Logger;import java.util.ArrayList;/** * @author:zhongyao on 2016/7/22 11:09 * @description: */public class DetailLoadPicsTestAdapter extends RecyclerView.Adapter<DetailLoadPicsTestAdapter.LoadPicsViewHolder> { private Context context; private ArrayList<String> imageUrls; private LayoutInflater inflater; private Handler mHandler = new Handler(); public DetailLoadPicsTestAdapter(Context context, ArrayList<String> imageUrls) { this.context = context; this.imageUrls = imageUrls; this.inflater = LayoutInflater.from(context); } @Override public LoadPicsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = inflater.inflate(R.layout.fragment_detail_loadpicstest_item, parent, false); return new LoadPicsViewHolder(view); } @Override public void onBindViewHolder(final LoadPicsViewHolder holder, final int position) { /** * 使用自己写的内存缓存+SD卡缓存(貌似效果甚至比使用DiskLruCache的效果好,这就比较蛋疼了,没看出DiskLruCache的优点) */ ImageWorker.loadImage(holder.iv,imageUrls.get(position) ,mHandler); } @Override public int getItemCount() { return imageUrls.size(); } public class LoadPicsViewHolder extends ViewHolder { private ImageView iv; public LoadPicsViewHolder(View itemView) { super(itemView); iv = (ImageView) itemView.findViewById(R.id.iv); } }}
以上就是使用RecyclerView实现的GridView,真正加载缓存图片的是:ImageWorker.loadImage(holder.iv,imageUrls.get(position),mHandler);这行代码。
下面我们着重介绍ImageWorker等类,内存缓存用的是LruCache类,sd卡缓存是自己写的,测试效果貌似不比用DiskLruCache差。(最后我会给出demo,demo中也用到了DiskLruCache做对比,有兴趣的可以自己测试下性能,对比下,欢迎交流)。
package com.hongri.recyclerview.cache;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Environment;import android.os.Handler;import android.support.v4.util.LruCache;import android.widget.ImageView;import com.hongri.recyclerview.https.DownloadImageFromNetwork;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.InputStream;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * 图片缓存类ImageWorker: * 与ImageTask类相似 */public class ImageWorker {private static final int lruSizeMaxSize = 10*1024*1024;// 图片内存缓存最大空间10Mpublic final static LruCache<String,Bitmap> mLruCache = new LruCache<String,Bitmap>(lruSizeMaxSize);private static ExecutorService mExecutorService = null;// 下载图片的线程管理类private static final int threadPoolMaxSize = 6;// 同时下载图片的 最大线程数private ImageWorker() {}private static ImageWorker mImageWorker = null;private static String bitmapPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/zhong/";// 图片在硬盘中的存储地址/** * 初始化,使用单例获得ImageWorker1的对象 */public static synchronized ImageWorker getImageWorkerInstance(){if(mImageWorker == null){mImageWorker = new ImageWorker();mExecutorService = Executors.newFixedThreadPool(threadPoolMaxSize);}return mImageWorker;}/** * (简单来说)加载图片: * 1 先从内存缓存中查找,如果没有则执行下一步。 * 2 从磁盘中查找,如果没有执行下一步。 * 3从网络下载 *//** * 1、首先判断内存缓存是否为空(不为空直接显示) * 2、内存缓存为空时,再从磁盘中读取后,将图片添加到内存缓存中(然后显示) * 3、磁盘为空时,只能重新网络加载图片(下载下来的图片,会在内存缓存、磁盘中各存储一份,然后显示) * @param mImageView * @param imgUrl * @param mHandler */public static void loadImage(ImageView mImageView, String imgUrl, Handler mHandler) {getImageWorkerInstance();Bitmap bitmapCache = getBitmapFromCache(imgUrl);if(bitmapCache!=null){mImageView.setImageBitmap(bitmapCache);}else {Bitmap bitmapDisk = getBitmapFromDisk(imgUrl);if(bitmapDisk!=null){addBitmapToLruCache(imgUrl,bitmapDisk);mImageView.setImageBitmap(bitmapDisk);}else{loadImageFromNetwork(mImageView,imgUrl,mHandler,mLruCache,bitmapPath);}}}private static void loadImageFromNetwork(ImageView mImageView, String imgUrl, Handler mHandler, LruCache<String, Bitmap> mLruCache, String bitmapPath) {mExecutorService.submit(new DownloadImageFromNetwork(mImageView,imgUrl,mHandler,mLruCache,bitmapPath));}private static void addBitmapToLruCache(String imgUrl, Bitmap bitmapDisk) {mLruCache.put(imgUrl,bitmapDisk);}private static Bitmap getBitmapFromDisk(String imgUrl) {InputStream inputStream = null;String[] fileNames = imgUrl.split("/");String fileName = fileNames[fileNames.length-1];try {inputStream = new FileInputStream(bitmapPath+fileName);} catch (FileNotFoundException e) {e.printStackTrace();}Bitmap bitmap = BitmapFactory.decodeStream(inputStream);return bitmap;}/** * 第一步:尝试从缓存中获取图片 * @param imgUrl * @return */private static Bitmap getBitmapFromCache(String imgUrl) {Bitmap bitmap = mLruCache.get(imgUrl);return bitmap;}}
说的很清晰了,还是那一套:先内存找,没有则sd,sd没有,则网络加载。
这里用到了ExecutorService线程管理类,可以设置启动最大的线程数,线程对象可重复利用,避免过多的重复创建线程对象,浪费内存空间。
package com.hongri.recyclerview.https;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Handler;import android.support.v4.util.LruCache;import android.widget.ImageView;import java.io.BufferedOutputStream;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.URL;/** * @author:zhongyao on 2016/7/11 10:39 * @description:从网络下载图片,并将下载好的图片存储到内存缓存、SD卡缓存中 */public class DownloadImageFromNetwork implements Runnable { private ImageView mImageView; private String imgUrl; private Handler mHandler; private LruCache<String, Bitmap> mLruCache; private String bitmapPath; public DownloadImageFromNetwork(ImageView mImageView, String imgUrl, Handler mHandler, LruCache<String, Bitmap> mLruCache, String bitmapPath) { this.mImageView = mImageView; this.imgUrl = imgUrl; this.mHandler = mHandler; this.mLruCache = mLruCache; this.bitmapPath = bitmapPath; } @Override public void run() { try { InputStream inputStream; URL url = new URL(imgUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(10 * 1000); conn.setConnectTimeout(10 * 1000); conn.setDoInput(true); conn.connect();// Starts the query int responseCode = conn.getResponseCode(); if (responseCode == 200) { inputStream = conn.getInputStream(); if (inputStream != null) { final Bitmap bitmapNetwork = BitmapFactory.decodeStream(inputStream); mHandler.post(new Runnable() { @Override public void run() { mImageView.setImageBitmap(bitmapNetwork); } }); /** * 此时输入流inputStream不存在了(输入流使用一次后就没有了), * 所以以下两行传送的代码是Bitmap对象 */ addBitmapToCache(imgUrl, bitmapNetwork); addBitmapToDisk(imgUrl, bitmapNetwork); } } } catch (Exception e) { e.printStackTrace(); } } private void addBitmapToDisk(String imgUrl, Bitmap bitmapNetwork) { OutputStream outputStream = null; BufferedOutputStream bos = null; InputStream inputStream = null; File file = new File(bitmapPath); if (!file.exists()) { boolean result = file.mkdirs(); } try { String[] fileNames = imgUrl.split("/"); String fileName = fileNames[fileNames.length - 1]; outputStream = new FileOutputStream(bitmapPath + fileName); bos = new BufferedOutputStream(outputStream); /** * 以下三行的代码表示: * 将Bitmap对象转换为inputStream */ ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmapNetwork.compress(Bitmap.CompressFormat.JPEG, 100, baos); inputStream = new ByteArrayInputStream(baos.toByteArray()); byte[] data = new byte[1024]; while (inputStream.read(data) != -1) { bos.write(data); } } catch (Exception e) { e.printStackTrace(); } finally { try { bos.close(); outputStream.close(); inputStream.close(); } catch (Exception e) { e.printStackTrace(); } } } private void addBitmapToCache(String imgUrl, Bitmap bitmapNetwork) { mLruCache.put(imgUrl, bitmapNetwork); }}
该类需要注意的是,下载完图片并展示的时候,需要将其加载到内存和sd卡缓存中。以后再次加载的时候直接get就可以了。
效果图:
github上的demo:点击查看源码
2 0
- Android 图片加载及缓存技术解析
- android 图片加载+缓存技术
- Android有效加载图片 之 图片缓存技术
- android 图片占用内存大小及加载解析
- android 图片占用内存大小及加载解析
- Android图片占用内存大小及加载解析
- android listview分页异步加载图片及图片缓存
- android listview分页异步加载图片及图片缓存
- android listview分页异步加载图片及图片缓存
- Android DiskLruCache框架解析,硬盘加载图片到缓存
- Android图片缓存技术
- Android图片缓存技术
- Android----图片缓存技术
- Android项目实战-ListView异步图片加载及压缩缓存
- android学习-1-图片轮显及异步加载缓存
- 网络加载图片及图片缓存处理
- Android 图片缓存、加载器
- Android 图片缓存、加载器
- 一步一步的实现Android的mvp框架
- 这里是标题
- Coreseek多表索引搜索(转载)
- 初学者的福利7
- 七夕来了,你搞清楚对象了吗?!
- Android 图片加载及缓存技术解析
- mysql workbench快捷键小结
- CentOS下LAMP网站架构之Apache性能调优篇
- 一个很好的makefile例子(经典)
- 8.3系统越狱后PP助手还是显示未越狱的解决方法
- 【HDU】4514 - 湫湫系列故事——设计风景线(树的直径 & 并查集 & dfs)
- OJ--购物单
- 常用的STL查找算法
- Qt项目中include了fstream,iostream头文件,但是还是会报:error C2653: “ios”: 不是类或命名空间名称