欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android二级缓存加载图片实现照片墙功能

程序员文章站 2022-08-08 08:55:13
实现二级缓存加载图片的功能,在使用disklrucache时,需先在工程中添加名为libcore.io的包,并将disklrucache.java文件放进去。disklru...

实现二级缓存加载图片的功能,在使用disklrucache时,需先在工程中添加名为libcore.io的包,并将disklrucache.java文件放进去。disklrucache直接百度下载即可。

在gridview的适配器中,为imageview添加图片时,先从内存缓存中加载,内存中无缓存的话则在磁盘缓存中加载,磁盘缓存也没有的话开启线程下载,然后将下载的图片缓存到磁盘,内存中。下载的图片最好先进行压缩,文章最后给出了压缩代码,但本例中并未实现压缩。

/*二级缓存实现图片墙功能,先在内存中加载缓存,内存中无缓存的话到磁盘缓存中加载,仍然没有的话开启线程下载图片,下载后缓存到磁盘中,然后缓存到内存中*/

public class erjihuancun extends arrayadapter<string> {
  /**
   * 记录所有正在下载或等待下载的任务。
   */
  private set<bitmapworkertask> taskcollection;

  /**
   * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
   */
  private lrucache<string, bitmap> mmemorycache;

  /**
   * 图片硬盘缓存核心类。
   */
  private disklrucache mdisklrucache;

  /**
   * gridview的实例
   */
  private gridview mphotowall;

  /**
   * 记录每个子项的高度。
   */
  private int mitemheight = 0;

  public erjihuancun(context context, int textviewresourceid, string[] objects,
      gridview photowall) {
    super(context, textviewresourceid, objects);
    mphotowall = photowall;
    taskcollection = new hashset<bitmapworkertask>();
    // 获取应用程序最大可用内存
    int maxmemory = (int) runtime.getruntime().maxmemory();
    int cachesize = maxmemory / 8;
    // 设置图片缓存大小为程序最大可用内存的1/8
    mmemorycache = new lrucache<string, bitmap>(cachesize) {
      @override
      protected int sizeof(string key, bitmap bitmap) {
        return bitmap.getbytecount();
      }
    };
    try {
      // 获取图片缓存路径
      file cachedir = getdiskcachedir(context, "thumb");
      if (!cachedir.exists()) {
        cachedir.mkdirs();
      }
      // 创建disklrucache实例,初始化缓存数据
      mdisklrucache = disklrucache
          .open(cachedir, getappversion(context), 1, 10 * 1024 * 1024);
    } catch (ioexception e) {
      e.printstacktrace();
    }
  }

  @override
  public view getview(int position, view convertview, viewgroup parent) {
    final string url = getitem(position);
    view view;
    if (convertview == null) {
      view = layoutinflater.from(getcontext()).inflate(r.layout.photo_layout, null);
    } else {
      view = convertview;
    }
    final imageview imageview = (imageview) view.findviewbyid(r.id.photo);
    if (imageview.getlayoutparams().height != mitemheight) {
      imageview.getlayoutparams().height = mitemheight;
    }
    // 给imageview设置一个tag,保证异步加载图片时不会乱序
    imageview.settag(url); 
    imageview.setimageresource(r.drawable.ic_launcher);
    loadbitmaps(imageview, url);
    return view;
  }

  /**
   * 将一张图片存储到lrucache中。
   * 
   * @param key
   *      lrucache的键,这里传入图片的url地址。
   * @param bitmap
   *      lrucache的键,这里传入从网络上下载的bitmap对象。
   */
  public void addbitmaptomemorycache(string key, bitmap bitmap) {
    if (getbitmapfrommemorycache(key) == null) {
      mmemorycache.put(key, bitmap);
    }
  }

  /**
   * 从lrucache中获取一张图片,如果不存在就返回null。
   * 
   * @param key
   *      lrucache的键,这里传入图片的url地址。
   * @return 对应传入键的bitmap对象,或者null。
   */
  public bitmap getbitmapfrommemorycache(string key) {
    return mmemorycache.get(key);
  }

  /**
   * 加载bitmap对象。此方法会在lrucache中检查所有屏幕中可见的imageview的bitmap对象,
   * 如果发现任何一个imageview的bitmap对象不在缓存中,就会开启异步线程去下载图片。
   */
  public void loadbitmaps(imageview imageview, string imageurl) {
    try {
      bitmap bitmap = getbitmapfrommemorycache(imageurl);
      if (bitmap == null) {
        bitmapworkertask task = new bitmapworkertask();
        taskcollection.add(task);
        task.execute(imageurl);
      } else {
        if (imageview != null && bitmap != null) {
          imageview.setimagebitmap(bitmap);
        }
      }
    } catch (exception e) {
      e.printstacktrace();
    }
  }

  /**
   * 取消所有正在下载或等待下载的任务。
   */
  public void cancelalltasks() {
    if (taskcollection != null) {
      for (bitmapworkertask task : taskcollection) {
        task.cancel(false);
      }
    }
  }

  /**
   * 根据传入的uniquename获取硬盘缓存的路径地址。
   */
  public file getdiskcachedir(context context, string uniquename) {
    string cachepath;
    if (environment.media_mounted.equals(environment.getexternalstoragestate())
        || !environment.isexternalstorageremovable()) {
      cachepath = context.getexternalcachedir().getpath();
    } else {
      cachepath = context.getcachedir().getpath();
    }
    return new file(cachepath + file.separator + uniquename);
  }

  /**
   * 获取当前应用程序的版本号。
   */
  public int getappversion(context context) {
    try {
      packageinfo info = context.getpackagemanager().getpackageinfo(context.getpackagename(),
          0);
      return info.versioncode;
    } catch (namenotfoundexception e) {
      e.printstacktrace();
    }
    return 1;
  }

  /**
   * 设置item子项的高度。
   */
  public void setitemheight(int height) {
    if (height == mitemheight) {
      return;
    }
    mitemheight = height;
    notifydatasetchanged();
  }

  /**
   * 使用md5算法对传入的key进行加密并返回。
   */
  public string hashkeyfordisk(string key) {
    string cachekey;
    try {
      final messagedigest mdigest = messagedigest.getinstance("md5");
      mdigest.update(key.getbytes());
      cachekey = bytestohexstring(mdigest.digest());
    } catch (nosuchalgorithmexception e) {
      cachekey = string.valueof(key.hashcode());
    }
    return cachekey;
  }

  /**
   * 将缓存记录同步到journal文件中。
   */
  public void flushcache() {
    if (mdisklrucache != null) {
      try {
        mdisklrucache.flush();
      } catch (ioexception e) {
        e.printstacktrace();
      }
    }
  }

  private string bytestohexstring(byte[] bytes) {
    stringbuilder sb = new stringbuilder();
    for (int i = 0; i < bytes.length; i++) {
      string hex = integer.tohexstring(0xff & bytes[i]);
      if (hex.length() == 1) {
        sb.append('0');
      }
      sb.append(hex);
    }
    return sb.tostring();
  }

  /**
   * 异步下载图片的任务。
   * 
   *
   */
  class bitmapworkertask extends asynctask<string, void, bitmap> {

    /**
     * 图片的url地址
     */
    private string imageurl;

    @override
    protected bitmap doinbackground(string... params) {
      imageurl = params[0];
      filedescriptor filedescriptor = null;
      fileinputstream fileinputstream = null;
      snapshot snapshot = null;
      try {
        // 生成图片url对应的key
        final string key = hashkeyfordisk(imageurl);
        // 查找key对应的缓存
        snapshot = mdisklrucache.get(key);
        if (snapshot == null) {
          // 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存
          disklrucache.editor editor = mdisklrucache.edit(key);
          if (editor != null) {
            outputstream outputstream = editor.newoutputstream(0);
            if (downloadurltostream(imageurl, outputstream)) {
              editor.commit();
            } else {
              editor.abort();
            }
          }
          // 缓存被写入后,再次查找key对应的缓存
          snapshot = mdisklrucache.get(key);
        }
        if (snapshot != null) {
          fileinputstream = (fileinputstream) snapshot.getinputstream(0);
          filedescriptor = fileinputstream.getfd();
        }
        // 将缓存数据解析成bitmap对象
        bitmap bitmap = null;
        if (filedescriptor != null) {
        // bitmap = bitmapfactory.decodefiledescriptor(filedescriptor);
         windowmanager wm= (windowmanager)getcontext().getsystemservice(context.window_service);
         int width=wm.getdefaultdisplay().getwidth();
         bitmap= imageresizer.decodesamplebithmapfromfiledescriptor(filedescriptor,width/3,width/3);
        }
        if (bitmap != null) {
          // 将bitmap对象添加到内存缓存当中
            addbitmaptomemorycache(params[0], bitmap);
        }
        return bitmap;
      } catch (ioexception e) {
        e.printstacktrace();
      } finally {
        if (filedescriptor == null && fileinputstream != null) {
          try {
            fileinputstream.close();
          } catch (ioexception e) {
          }
        }
      }
      return null;
    }

    @override
    protected void onpostexecute(bitmap bitmap) {
      super.onpostexecute(bitmap);
      // 根据tag找到相应的imageview控件,将下载好的图片显示出来。
      imageview imageview = (imageview) mphotowall.findviewwithtag(imageurl);
      if (imageview != null && bitmap != null) {
        imageview.setimagebitmap(bitmap);
      }
      taskcollection.remove(this);
    }

    /**
     * 建立http请求,并获取bitmap对象。
     * 
     * @param imageurl
     *      图片的url地址
     * @return 解析后的bitmap对象
     */
    private boolean downloadurltostream(string urlstring, outputstream outputstream) {
      httpurlconnection urlconnection = null;
      bufferedoutputstream out = null;
      bufferedinputstream in = null;
      try {
        final url url = new url(urlstring);
        urlconnection = (httpurlconnection) url.openconnection();
        in = new bufferedinputstream(urlconnection.getinputstream(), 8 * 1024);
        out = new bufferedoutputstream(outputstream, 8 * 1024);
        int b;
        while ((b = in.read()) != -1) {
          out.write(b);
        }
        return true;
      } catch (final ioexception e) {
        e.printstacktrace();
      } finally {
        if (urlconnection != null) {
          urlconnection.disconnect();
        }
        try {
          if (out != null) {
            out.close();
          }
          if (in != null) {
            in.close();
          }
        } catch (final ioexception e) {
          e.printstacktrace();
        }
      }
      return false;
    }

  }

}

mainactivity

/**
 * 照片墙主活动,使用gridview展示照片墙。
 * 
 * 
 */
public class mainactivity extends activity {

  /**
   * 用于展示照片墙的gridview
   */
  private gridview mphotowall;

  /**
   * gridview的适配器
   */
  private erjihuancun madapter;

  private int mimagethumbsize;
  private int mimagethumbspacing;

  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    setcontentview(r.layout.activity_main);
    //mimagethumbsize = getresources().getdimensionpixelsize(
  //     r.dimen.image_thumbnail_size);
    //mimagethumbspacing = getresources().getdimensionpixelsize(
  //     r.dimen.image_thumbnail_spacing);
    mphotowall = (gridview) findviewbyid(r.id.photo_wall);
    madapter = new erjihuancun(this, 0, images.imagethumburls,
        mphotowall);
    mphotowall.setadapter(madapter);
/*   mphotowall.getviewtreeobserver().addongloballayoutlistener(
        new viewtreeobserver.ongloballayoutlistener() {

          @override
          public void ongloballayout() {
            final int numcolumns = (int) math.floor(mphotowall
                .getwidth()
                / (mimagethumbsize + mimagethumbspacing));
            if (numcolumns > 0) {
              int columnwidth = (mphotowall.getwidth() / numcolumns)
                  - mimagethumbspacing;
              madapter.setitemheight(columnwidth);
              mphotowall.getviewtreeobserver()
                  .removeglobalonlayoutlistener(this);
            }
          }
        });*/
  }

  @override
  protected void onpause() {
    super.onpause();
    //将缓存记录同步到journal文件中
    madapter.flushcache();
  }

  @override
  protected void ondestroy() {
    super.ondestroy();
    // // 退出程序时结束所有的下载任务
    madapter.cancelalltasks();
  }

}
/**
 * 自定义正方形的imageview
 * 
 */
public class myimageview extends imageview {


  public myimageview(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    // todo auto-generated constructor stub
  }

  public myimageview(context context, attributeset attrs) {
    super(context, attrs);
    // todo auto-generated constructor stub
  }

  public myimageview(context context) {
    super(context);
    // todo auto-generated constructor stub
  }
  @override
protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  // todo auto-generated method stub
    //将高度信息改成宽度即可
  super.onmeasure(widthmeasurespec, widthmeasurespec);
}
}

主activity的layout

<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  android:padding="5dp"
 >

<gridview
  android:id="@+id/photo_wall"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:gravity="center"
  android:horizontalspacing="5dp"
  android:verticalspacing="5dp"
  android:numcolumns="3"
  android:stretchmode="columnwidth"
  />
</linearlayout>

gridview中的item imageview

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >

<com.example.imageloader.myimageview
  android:layout_width="match_parent"
  android:layout_height="0dp"
   android:id="@+id/photo"
  />
</linearlayout>

图片压缩实现

public class imageresizer {
 private static final string tag="imageresizer";

 public static bitmap decodesampledbitmapfromresource(resources res,
     int resid,int reqwidth,int reqheight){
   final bitmapfactory.options options=new bitmapfactory.options();
   options.injustdecodebounds=true;
   bitmapfactory.decoderesource(res,resid,options);
   options.insamplesize=calculateinsamplesize(options,reqwidth,reqheight);
   options.injustdecodebounds=false;
   return bitmapfactory.decoderesource(res, resid, options);

 }
 public static bitmap decodesamplebithmapfromfiledescriptor(filedescriptor fd,
     int reqwidth,int reqheight){
   final bitmapfactory.options options=new bitmapfactory.options();
   options.injustdecodebounds=true;
   bitmapfactory.decodefiledescriptor(fd, null,options);
   options.insamplesize=calculateinsamplesize(options,reqwidth,reqheight);
   options.injustdecodebounds=false;
   return bitmapfactory.decodefiledescriptor(fd, null,options);
 }
 public static int calculateinsamplesize(bitmapfactory.options options,
     int reqwidth,int reqheight){
   if(reqwidth==0||reqheight==0)
     return 1;
   final int width=options.outwidth;
   final int height=options.outheight;
   int insamplesize=1;
   if(height>reqheight||width>reqwidth  ){
     final int halfheight=height/2;
     final int halfwidth=width/2;
     //尽最大限度的压缩图片,不能让图片的宽高比imageview的宽高小,否则在将
     //图片显示到imageview时,图片会放大导致图片失真
   while(halfheight/insamplesize>reqheight&&halfwidth/insamplesize>reqwidth){
      insamplesize*=2;
    }
   }
   return insamplesize;
 }
}


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。