网络图片异步加载 其实有关图片加载存在这样一个问题,图片的下载始终是一个耗时的操作,这个时候如果把图片加载放在主线程中话的是不明智的,模拟一个这样的场景, 假如在一个listview或RecyclerView中,每一个listitem中都有一张网络图片,假如不使用网络异步处理的话,滑动工作会特别卡,因为必须加载网图片后 ,才会加载下个item。除了这些,网络图片加载还存在很多优化,下面是ImageLoader对于网络图片加载的优化内容:
- 加载过程使用异步,这里使用AsyncTask
- 加载图片后,将图片放入缓存,不必每次都从网络上请求,节省流量,加快加载速度
- 使用ViewHolder,这点就不必多说了
- 监听滑动过程,其实我们知道的,在滑动的时候其实我们是没有必要加载图片的,等滑动结束的时候,加载当前页面的图片
其实网上有很多类似的开源框架,但是为了理解其中原理,我自己写了一个ImageLoader。同时在加载过程中我自己写了一个ProgressDialog,显示效果如下
1、关于异步加载:
我的Demo里面使用的异步加载方法是AsyncTask,AsyncTask是一个很创建的异步方式,这里就不做太多介绍,只要在doInBackground方法中添加图片的网络加载即可,以下是代码:
private class NewSAsyncTask extends AsyncTask<String, Void, Bitmap> { private ImageView mimageView; private String mUrl; private int mType; public NewSAsyncTask(ImageView imageview, String url,int type) { mimageView = imageview; mUrl = url; mType=type; } @Override protected Bitmap doInBackground(String... params) { String url=params[0]; Bitmap bitmap=getBitmapFromUrl(mUrl,mType); if(bitmap!=null){ addBitmapToCache(url, bitmap); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (mimageView.getTag().equals(mUrl)) { mimageView.setImageBitmap(bitmap); } } }
2、加载的图片放入缓存,或从缓存中读取图片--LruCache
LruCache是android提供给我的一个有关于手机缓存中插入和读取数据的类,使用起来也很方便,以下是代码:
public ImageLoaderUtil() { int maxMemory = (int) Runtime.getRuntime().maxMemory(); int cacheSize = maxMemory / 8; mcache = new LruCache<String, Bitmap>(cacheSize) { @SuppressLint("NewApi") @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } }; } public void addBitmapToCache(String url, Bitmap bitmap) { if (getBitmapFromCache(url) == null) { mcache.put(url, bitmap); } } public Bitmap getBitmapFromCache(String url) { return mcache.get(url); }
上面的代码中先是获取当前手机可用的最大内容,然后取其8分之一,用于我们使用的缓存。
3、判断手指滑动状态
这个很简单,直接使用ListView或RecyclerView的滑动监听即可。当手指停止滑动的时候,判断当前屏幕中都显示哪些item,然后在加载这些item。这样一来,在手指滑动的过程中,不进行任何有关加载的相关操作,节省了流量,加快了加载速度,同时提升了用户体验。
4、ImageLoader的逻辑顺序
首先判断在LruCache中通过url判断是否有欲加载的图片,没有的话从网络上加载,并使用url和图片的键值对放入LruCache中。同时监听手机滑动,当收停止滑动的时候在进行加载,滑动过程中不进行任何加载。
下面是ImageLoader的完整代码:
package xml.org.today.util; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import android.annotation.SuppressLint; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Bitmap.Config; import android.graphics.PorterDuff.Mode; import android.os.AsyncTask; import android.support.v4.util.LruCache; import android.widget.ImageView; public class ImageLoaderUtil { private LruCache<String, Bitmap> mcache; public ImageLoaderUtil() { int maxMemory = (int) Runtime.getRuntime().maxMemory(); int cacheSize = maxMemory / 8; mcache = new LruCache<String, Bitmap>(cacheSize) { @SuppressLint("NewApi") @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount(); } }; } public void addBitmapToCache(String url, Bitmap bitmap) { if (getBitmapFromCache(url) == null) { mcache.put(url, bitmap); } } public Bitmap getBitmapFromCache(String url) { return mcache.get(url); } /** * 异步加载网络图片 * * @param imageview * @param url * @param type 1、圆角图片 */ public void getImageByAsyncTask(ImageView imageview, String url, int type) { Bitmap bitmap = getBitmapFromCache(url); if (bitmap == null) { new NewSAsyncTask(imageview, url, type).execute(url); } else { imageview.setImageBitmap(bitmap); } } private class NewSAsyncTask extends AsyncTask<String, Void, Bitmap> { private ImageView mimageView; private String mUrl; private int mType; public NewSAsyncTask(ImageView imageview, String url, int type) { mimageView = imageview; mUrl = url; mType = type; } @Override protected Bitmap doInBackground(String... params) { String url = params[0]; Bitmap bitmap = getBitmapFromUrl(mUrl, mType); if (bitmap != null) { addBitmapToCache(url, bitmap); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (mimageView.getTag().equals(mUrl)) { mimageView.setImageBitmap(bitmap); } } } public Bitmap getBitmapFromUrl(String urlString, int type) { Bitmap bitmap; Bitmap outbitmap; InputStream is = null; try { URL url = new URL(urlString); try { HttpURLConnection connection = (HttpURLConnection) url .openConnection(); is = new BufferedInputStream(connection.getInputStream()); bitmap = BitmapFactory.decodeStream(is); if (type == 1) { outbitmap = toRoundBitmap(bitmap); } else { outbitmap = bitmap; } connection.disconnect(); return outbitmap; } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } public static Bitmap toRoundBitmap(Bitmap bitmap) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); Paint paint = new Paint(); int width = bitmap.getWidth(); int height = bitmap.getHeight(); Rect rect; if (width >= height) { rect = new Rect((width - height) / 2, 0, (width - height) / 2 + height, height); } else { rect = new Rect(0, (height - width) / 2, width, width + (height - width) / 2); } RectF rectF = new RectF(rect); canvas.drawOval(rectF, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rectF, paint); return output; } }
调用的方式也很简单:
ImageLoaderUtil mImageLoaderUtil=new ImageLoaderUtil(); mImageLoaderUtil.getImageByAsyncTask(mView,mUrl,1);
上面的代码中mView是欲加载图片的ImageView,mUrl是图片的远程地址,1代表欲加载圆角图片。
上面代码的github地址是:https://github.com/jiushi555/ImageLoaderDemo。