注册

简易的Android网络图片加载器

在项目开发中,我们加载图片一般使用的是第三方库,比如Glide,在闲暇之余突发奇想,自己动手实现一个简单的Android网络图片加载器。

首先定义API,API的定义应该简单易用,比如

imageLoader.displayImage(imageView,imagePath);

其次应该支持缓存。缓存一般是指三级缓存,先定义一个入口类ImageLoader

public ImageLoader(Activity activity) {
   this.activity = activity;
   memoryCache = new MemoryCache();
   diskCache = new DiskCache(activity);
   netCache = new NetCache(activity,memoryCache,diskCache);
}

在初始化的时候就初始化内存缓存,磁盘缓存,网络缓存三个变量,然后定义加载方法:

public void displayImage(final ImageView imageView, String url,int placeholder){
   imageView.setTag(url);
   imageView.setImageResource(placeholder);
   Bitmap bitmap;
   bitmap = memoryCache.getBitmap(url);
   if(bitmap != null){
       imageView.setImageBitmap(bitmap);
       Log.i(TAG, "从内存中获取图片");
       return;
  }
   bitmap = diskCache.getBitmap(url);
   if(bitmap != null){
       imageView.setImageBitmap(bitmap);
       memoryCache.setBitmap(url,bitmap);
       Log.i(TAG, "从磁盘中获取图片");
       return;
  }
   netCache.getBitmap(imageView,url);
}

首先将图片地址设置给ImageView的tag,防止因为ImageView复用导致图片错乱的问题。然后设置一个占位图防止图片加载过慢ImageVIew显示白板

三级缓存中从内存中加载缓存信息是最快的,所以第一步从内存缓存中查找,如果找到了就直接设置给ImageView,否则继续从磁盘缓存中查找,找到了就显示,最后实在找不到就从网络下载图片

内存缓存

public class MemoryCache {
   private LruCache<String,Bitmap> lruCache;
   public MemoryCache() {
       long maxMemory  = Runtime.getRuntime().maxMemory() / 8;
       lruCache = new LruCache<String,Bitmap>((int) maxMemory){
           @Override
           protected int sizeOf(String key, Bitmap value) {
               return value.getByteCount();
          }
      };
  }
   public Bitmap getBitmap(String url) {
       return lruCache.get(url);
  }
   public void setBitmap(String url,Bitmap bitmap) {
       lruCache.put(url,bitmap);
  }
}

内存缓存比较简单,只需要将加载过的图片放入内存,然后下次加载直接获取,由于内存大小有限制,所以这里使用了LruCache算法保证缓存不会无限制增长。

磁盘缓存

对于已经缓存在磁盘上的文件,就不需要在从网络下载了,直接从磁盘读取。

public Bitmap getBitmap(String url) {
   FileInputStream is;
   String cacheUrl = Md5Utils.md5(url);
   File parentFile = new File(Values.PATH_CACHE);
   File file = new File(parentFile,cacheUrl);
   if(file.exists()){
       try {
           is = new FileInputStream(file);
           Bitmap bitmap =  decodeSampledBitmapFromFile(file.getAbsolutePath());
           is.close();
           return bitmap;
      } catch (Exception e) {
           e.printStackTrace();
      }
  }
   return null;
}

考虑到多图加载的时候,如果图片太大容易OOM,所以需要对加载的图片稍作处理

public  Bitmap decodeSampledBitmapFromFile(String pathName){
   BitmapFactory.Options options = new BitmapFactory.Options();
   options.inJustDecodeBounds = true;
   BitmapFactory.decodeFile(pathName,options);
   options.inSampleSize = calculateInSampleSize(options)*2;
   options.inJustDecodeBounds = false;
   options.inPreferredConfig = Bitmap.Config.RGB_565;
   return BitmapFactory.decodeFile(pathName,options);
}

这里降低了图片的采样,对图片的质量进行了压缩

对于本地没有缓存的图片,需要从网络下载,当获取到图片流之后,保存在本地

public void saveBitmap(InputStream inputStream, String url) throws IOException {
   String cacheUrl = Md5Utils.md5(url);
   File parentFile = new File(Values.PATH_CACHE);
   if(!parentFile.exists()){
       parentFile.mkdirs();
  }
   FileOutputStream fos = new FileOutputStream(new File(parentFile,cacheUrl));
   byte[] bytes = new byte[1024];
   int index = 0;
   while ((index = inputStream.read(bytes))!=-1){
       fos.write(bytes,0,index);
       fos.flush();
  }
   inputStream.close();
   fos.close();
}

为了防止图片url带一些非法字符导致创建文件失败,所以对url进行了md5处理

网络缓存

这里比较简单,直接从服务器加载图片信息就可以了,访问网络使用了OkHttp

public void getBitmap(final ImageView imageView, final String url) {
   OkHttpClient client = new OkHttpClient();
   Request request = new Request.Builder() .get().url(url).build();
   client.newCall(request).enqueue(new Callback() {
       @Override
       public void onFailure(Call call, IOException e) {
           imageView.setImageResource(R.mipmap.ic_error);
      }
       @Override
       public void onResponse(Call call, Response response) throws IOException {
           InputStream inputStream = response.body().byteStream();
           diskCache.saveBitmap(inputStream, url);
           activity.runOnUiThread(new Runnable() {
               @Override
               public void run() {
                   if (url != null && url.equals(imageView.getTag())) {
                       Bitmap bitmap = diskCache.getBitmap(url);
                       memoryCache.setBitmap(url, bitmap);
                       if (bitmap != null) {
                           imageView.setImageBitmap(bitmap);
                      } else {
                           imageView.setImageResource(R.mipmap.ic_error);
                      }
                  } else {
                       imageView.setImageResource(R.mipmap.ic_place);
                  }
              }
          });
      }
  });
}

当获取到图片后,分别放入磁盘和内存缓存起来

使用

最后直接在需要加载图片的地方调用

new ImageLoader(activity).displayImage(imageView,path)


作者:晚来天欲雪_
来源:juejin.cn/post/7088693420109070373

0 个评论

要回复文章请先登录注册