安卓网络请求图片,对于我们来说并不陌生,因为每个应用都有可能会用到这一技术。通常情况下,我们第一次都是从网络上请求图片资源,然后将
图片资源保存到内存和本地,下一次动态显示图片的时候就不需要再从网络上请求图片资源了,直接从本地或者内存中获取就可以了。这就涉及到图片
的三级缓存技术,分别是内存缓存,本地缓存,网络缓存。
缓存的流程图:
首先我们定义一个类叫ClassLoader:
package com.jsako.showprodinfodemo; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.support.v4.util.LruCache; import android.widget.ImageView; /** * 图片加载类 * * @author Administrator * */ public class ImageLoader { private Context context; private int loadingImage; private int errorImage; private LruCache<String, Bitmap> mapCache; public ImageLoader(Context context, int loadingImage, int errorImage) { this.context = context; this.loadingImage = loadingImage; this.errorImage = errorImage; int maxMemory = (int) Runtime.getRuntime().maxMemory(); int mCacheSize = maxMemory / 8; mapCache = new LruCache<String, Bitmap>(mCacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } }; } public void getAndSetImage(String imagePath, ImageView iv_image) { //保存当前的ImageView对应的imagepath iv_image.setTag(imagePath); iv_image.setImageResource(loadingImage); /* * 从第一级缓存中找对应imagePath的图片 如果第一级缓存有对应图片,显示! 如果第一级缓存没有图片,从第二级缓存中找 */ Bitmap bitmap = getImageByFirstCache(imagePath); if (bitmap != null) { iv_image.setImageBitmap(bitmap); System.out.println("从一级缓存中找到"); return; } /* * 从第二级缓存中找对应的图片 如果有,则缓存到第一缓存中 如果没有,则从第三集缓存中找 */ bitmap = getImageBySecondCache(imagePath); if (bitmap != null) { iv_image.setImageBitmap(bitmap); cacheInFirst(imagePath, bitmap); System.out.println("从二级缓存中找到"); return; } /* * 从第三级缓存中找对应的图片 如果有,则缓存到第一、二缓存中 如果没有,则显示错误的图片 */ loadImageByThridCache(imagePath, iv_image); } /** * 将图片缓存到一级缓存 * * @param imagePath * 图片的url * @param bitmap */ private void cacheInFirst(String imagePath, Bitmap bitmap) { mapCache.put(imagePath, bitmap); } /** * 从三级缓存中寻找图片 * * @param imagePath * 图片的url * @param * @return */ private void loadImageByThridCache(final String imagePath, final ImageView iv_image) { new AsyncTask<String, Void, Bitmap>() { /** * 开启异步任务前调用 */ @Override protected void onPreExecute() { } /** * 异步任务完成后调用 */ @Override protected void onPostExecute(Bitmap result) { String nowImagePath=(String) iv_image.getTag(); if(!nowImagePath.equals(imagePath)){ //如果当前请求的图片路径和需要显示的图片路径不一致的话,就不显示图片 System.out.println("不显示图片了"); return; } if (result != null) { iv_image.setImageBitmap(result); } else { iv_image.setImageResource(errorImage); } } /** * 后台进行异步任务 */ @Override protected Bitmap doInBackground(String... params) { String nowImagePath=(String) iv_image.getTag(); if(!nowImagePath.equals(params[0])){ //如果当前请求的图片路径和需要显示的图片路径不一致的话,就不进行网络请求了 System.out.println("不进行网络请求了"); return null; } String url = params[0]; HttpURLConnection conn = null; try { URL mUrl = new URL(url); conn = (HttpURLConnection) mUrl.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(6000); conn.setConnectTimeout(6000); conn.setDoInput(true); conn.connect(); int code = conn.getResponseCode(); if (code == 200) { InputStream in = conn.getInputStream(); Bitmap bitmap = BitmapFactory.decodeStream(in); // 在分线程中缓存图片到一级和二级缓存 cacheInFirst(url, bitmap); String imageName = url .substring(url.lastIndexOf("/") + 1); String fileName = context.getExternalFilesDir(null) .getAbsolutePath() + "/" + imageName; bitmap.compress(CompressFormat.JPEG, 50, new FileOutputStream(fileName)); return bitmap; } else { return null; } } catch (Exception e) { e.printStackTrace(); return null; } finally { if (conn != null) { conn.disconnect(); } } } }.execute(imagePath); } /** * 从二级缓存中寻找图片 * * @param imagePath * 图片的url * @return */ private Bitmap getImageBySecondCache(String imagePath) { String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1); String fileName = context.getExternalFilesDir(null).getAbsolutePath() + "/" + imageName; return BitmapFactory.decodeFile(fileName); } /** * 从一级缓存中寻找图片 * * @param imagePath * 图片的url * @return */ private Bitmap getImageByFirstCache(String imagePath) { return mapCache.get(imagePath); } }
可以看到这个类的构造方法有三个,第一个是上下文,第二个是加载的图片的时候所显示的图片对应的资源id,第三个是图片加载失败后所显示的图片对应的资源id。
这里有几个关键点:
第一个是在内存缓存中,我们应该使用LruCache来缓存图片,这个类会根据当前所存储的图片空间是否大过设定值,如果比设定值大就会自动释放内存,
这样就能防止内存溢出的问题。
第二个是在使用ListView异步加载图片的时候,并且重用了convertView的时候会导致图片错位的现象。
导致图片错位的根本原因就是重用了convertView。
解决方案如下:
a. 每次getView()都将图片的url保存到ImageView上: imageView.setTag(imagePath)
b. 在分线程准备请求服务器加载图片之前, 比较准备加载图片的url与ImageView中保存的最新图片的url是同一个,
如果不是同一个, 当前加载图片的任务不应该再执行
如果相同, 继续执行加载远程图片
c. 在主线程准备显示图片之前, 比较加载到图片的url与ImageView中保存的最新图片的url是同一个
如果不是同一个, 不需要显示此图片
如果相同, 显示图片
时间: 2024-10-14 18:30:22