当前位置: 移动技术网 > 移动技术>移动开发>Android > Android编程学习之异步加载图片的方法

Android编程学习之异步加载图片的方法

2019年07月24日  | 移动技术网移动技术  | 我要评论

本文实例讲述了android编程学习之异步加载图片的方法。分享给大家供大家参考,具体如下:

最近在android开发中碰到比较棘手的问题,就是加载图片内存溢出。我开发的是一个新闻应用,应用中用到大量的图片,一个界面中可能会有上百张图片。开发android应用的朋友可能或多或少碰到加载图片内存溢出问题,一般情况下,加载一张大图就会导致内存溢出,同样,加载多张图片内存溢出的概率也很高。

列一下网络上查到的一般做法:

1.使用bitmapfactory.options对图片进行压缩
2.优化加载图片的adapter中的getview方法,使之尽可能少占用内存
3.使用异步加载图片的方式,使图片在页面加载后慢慢载入进来。

1、2步骤是必须做足的工作,但是对于大量图片的列表仍然无法解决内存溢出的问题,采用异步加载图片的方式才能有效解决图片加载内存溢出问题。

测试的效果图如下:

在这里我把主要的代码贴出来,给大家分享一下。

1、首先是mainactivity和activity_main.xml布局文件的代码。

(1)、mainactivity的代码如下:

package net.loonggg.test; 
import java.util.list; 
import net.loonggg.adapter.myadapter; 
import net.loonggg.bean.menu; 
import net.loonggg.util.httputil; 
import net.loonggg.util.utils; 
import android.app.activity; 
import android.app.progressdialog; 
import android.os.asynctask; 
import android.os.bundle; 
import android.view.window; 
import android.widget.listview; 
public class mainactivity extends activity { 
 private listview lv; 
 private myadapter adapter; 
 private progressdialog pd; 
 @override 
 protected void oncreate(bundle savedinstancestate) { 
  requestwindowfeature(window.feature_no_title); 
  super.oncreate(savedinstancestate); 
  setcontentview(r.layout.activity_main); 
  lv = (listview) findviewbyid(r.id.lv); 
  pd = new progressdialog(this); 
  pd.settitle("加载菜单"); 
  pd.setmessage("正在加载"); 
  adapter = new myadapter(this); 
  new mytask().execute("1"); 
 } 
 public class mytask extends asynctask<string, void, list<menu>> { 
  @override 
  protected void onpreexecute() { 
   super.onpreexecute(); 
   pd.show(); 
  } 
  @override 
  protected void onpostexecute(list<menu> result) { 
   super.onpostexecute(result); 
   adapter.setdata(result); 
   lv.setadapter(adapter); 
   pd.dismiss(); 
  } 
  @override 
  protected list<menu> doinbackground(string... params) { 
   string menuliststr = getlistdishesinfo(params[0]); 
   return utils.getinstance().parsemenusjson(menuliststr); 
  } 
 } 
 private string getlistdishesinfo(string sortid) { 
  // url 
  string url = httputil.base_url + "servlet/menuinfoservlet?sortid=" 
    + sortid + "&flag=1"; 
  // 查询返回结果 
  return httputil.querystringforpost(url); 
 } 
} 

(2)、activity_main.xml的布局文件如下:

<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:background="#ffffff" 
 android:orientation="vertical" > 
 <listview 
  android:id="@+id/lv" 
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content" > 
 </listview> 
</linearlayout> 

2、这是自定义的listview的adapter的代码:

package net.loonggg.adapter; 
import java.util.list; 
import net.loonggg.bean.menu; 
import net.loonggg.test.r; 
import net.loonggg.util.imageloader; 
import android.app.activity; 
import android.content.context; 
import android.view.layoutinflater; 
import android.view.view; 
import android.view.viewgroup; 
import android.widget.baseadapter; 
import android.widget.imageview; 
import android.widget.textview; 
public class myadapter extends baseadapter { 
 private list<menu> list; 
 private context context; 
 private activity activity; 
 private imageloader imageloader; 
 private viewholder viewholder; 
 public myadapter(context context) { 
  this.context = context; 
  this.activity = (activity) context; 
  imageloader = new imageloader(context); 
 } 
 public void setdata(list<menu> list) { 
  this.list = list; 
 } 
 @override 
 public int getcount() { 
  return list.size(); 
 } 
 @override 
 public object getitem(int position) { 
  return list.get(position); 
 } 
 @override 
 public long getitemid(int position) { 
  return position; 
 } 
 @override 
 public view getview(int position, view convertview, viewgroup parent) { 
  if (convertview == null) { 
   convertview = layoutinflater.from(context).inflate( 
     r.layout.listview_item, null); 
   viewholder = new viewholder(); 
   viewholder.tv = (textview) convertview.findviewbyid(r.id.item_tv); 
   viewholder.iv = (imageview) convertview.findviewbyid(r.id.item_iv); 
   convertview.settag(viewholder); 
  } else { 
   viewholder = (viewholder) convertview.gettag(); 
  } 
  viewholder.tv.settext(list.get(position).getdishes()); 
  imageloader.displayimage(list.get(position).getpicpath(), activity, 
    viewholder.iv); 
  return convertview; 
 } 
 private class viewholder { 
  private imageview iv; 
  private textview tv; 
 } 
} 

3、这是最重要的一部分代码,这就是异步加载图片的一个类,这里我就不解释了,代码中附有注释。代码如下:

package net.loonggg.util; 
import java.io.file; 
import java.io.fileinputstream; 
import java.io.filenotfoundexception; 
import java.io.fileoutputstream; 
import java.io.inputstream; 
import java.io.outputstream; 
import java.net.httpurlconnection; 
import java.net.url; 
import java.util.collections; 
import java.util.map; 
import java.util.stack; 
import java.util.weakhashmap; 
import net.loonggg.test.r; 
import android.app.activity; 
import android.content.context; 
import android.graphics.bitmap; 
import android.graphics.bitmapfactory; 
import android.widget.imageview; 
/** 
 * 异步加载图片类 
 * 
 * @author loonggg 
 * 
 */ 
public class imageloader { 
 // 手机中的缓存 
 private memorycache memorycache = new memorycache(); 
 // sd卡缓存 
 private filecache filecache; 
 private picturesloader pictureloaderthread = new picturesloader(); 
 private picturesqueue picturesqueue = new picturesqueue(); 
 private map<imageview, string> imageviews = collections 
   .synchronizedmap(new weakhashmap<imageview, string>()); 
 public imageloader(context context) { 
  // 设置线程的优先级 
  pictureloaderthread.setpriority(thread.norm_priority - 1); 
  filecache = new filecache(context); 
 } 
 // 在找不到图片时,默认的图片 
 final int stub_id = r.drawable.stub; 
 public void displayimage(string url, activity activity, imageview imageview) { 
  imageviews.put(imageview, url); 
  bitmap bitmap = memorycache.get(url); 
  if (bitmap != null) 
   imageview.setimagebitmap(bitmap); 
  else {// 如果手机内存缓存中没有图片,则调用任务队列,并先设置默认图片 
   queuephoto(url, activity, imageview); 
   imageview.setimageresource(stub_id); 
  } 
 } 
 private void queuephoto(string url, activity activity, imageview imageview) { 
  // 这imageview可能之前被用于其它图像。所以可能会有一些旧的任务队列。我们需要清理掉它们。 
  picturesqueue.clean(imageview); 
  picturetoload p = new picturetoload(url, imageview); 
  synchronized (picturesqueue.picturestoload) { 
   picturesqueue.picturestoload.push(p); 
   picturesqueue.picturestoload.notifyall(); 
  } 
  // 如果这个线程还没有启动,则启动线程 
  if (pictureloaderthread.getstate() == thread.state.new) 
   pictureloaderthread.start(); 
 } 
 /** 
  * 根据url获取相应的图片的bitmap 
  * 
  * @param url 
  * @return 
  */ 
 private bitmap getbitmap(string url) { 
  file f = filecache.getfile(url); 
  // 从sd卡缓存中获取 
  bitmap b = decodefile(f); 
  if (b != null) 
   return b; 
  // 否则从网络中获取 
  try { 
   bitmap bitmap = null; 
   url imageurl = new url(url); 
   httpurlconnection conn = (httpurlconnection) imageurl 
     .openconnection(); 
   conn.setconnecttimeout(30000); 
   conn.setreadtimeout(30000); 
   inputstream is = conn.getinputstream(); 
   outputstream os = new fileoutputstream(f); 
   // 将图片写到sd卡目录中去 
   imageutil.copystream(is, os); 
   os.close(); 
   bitmap = decodefile(f); 
   return bitmap; 
  } catch (exception ex) { 
   ex.printstacktrace(); 
   return null; 
  } 
 } 
 // 解码图像和缩放以减少内存的消耗 
 private bitmap decodefile(file f) { 
  try { 
   // 解码图像尺寸 
   bitmapfactory.options o = new bitmapfactory.options(); 
   o.injustdecodebounds = true; 
   bitmapfactory.decodestream(new fileinputstream(f), null, o); 
   // 找到正确的缩放值。这应该是2的幂。 
   final int required_size = 70; 
   int width_tmp = o.outwidth, height_tmp = o.outheight; 
   int scale = 1; 
   while (true) { 
    if (width_tmp / 2 < required_size 
      || height_tmp / 2 < required_size) 
     break; 
    width_tmp /= 2; 
    height_tmp /= 2; 
    scale *= 2; 
   } 
   // 设置恰当的insamplesize可以使bitmapfactory分配更少的空间 
   // 用正确恰当的insamplesize进行decode 
   bitmapfactory.options o2 = new bitmapfactory.options(); 
   o2.insamplesize = scale; 
   return bitmapfactory.decodestream(new fileinputstream(f), null, o2); 
  } catch (filenotfoundexception e) { 
  } 
  return null; 
 } 
 /** 
  * picturetoload类(包括图片的地址和imageview对象) 
  * 
  * @author loonggg 
  * 
  */ 
 private class picturetoload { 
  public string url; 
  public imageview imageview; 
  public picturetoload(string u, imageview i) { 
   url = u; 
   imageview = i; 
  } 
 } 
 public void stopthread() { 
  pictureloaderthread.interrupt(); 
 } 
 // 存储下载的照片列表 
 class picturesqueue { 
  private stack<picturetoload> picturestoload = new stack<picturetoload>(); 
  // 删除这个imageview的所有实例 
  public void clean(imageview image) { 
   for (int j = 0; j < picturestoload.size();) { 
    if (picturestoload.get(j).imageview == image) 
     picturestoload.remove(j); 
    else 
     ++j; 
   } 
  } 
 } 
 // 图片加载线程 
 class picturesloader extends thread { 
  public void run() { 
   try { 
    while (true) { 
     // 线程等待直到有图片加载在队列中 
     if (picturesqueue.picturestoload.size() == 0) 
      synchronized (picturesqueue.picturestoload) { 
       picturesqueue.picturestoload.wait(); 
      } 
     if (picturesqueue.picturestoload.size() != 0) { 
      picturetoload phototoload; 
      synchronized (picturesqueue.picturestoload) { 
       phototoload = picturesqueue.picturestoload.pop(); 
      } 
      bitmap bmp = getbitmap(phototoload.url); 
      // 写到手机内存中 
      memorycache.put(phototoload.url, bmp); 
      string tag = imageviews.get(phototoload.imageview); 
      if (tag != null && tag.equals(phototoload.url)) { 
       bitmapdisplayer bd = new bitmapdisplayer(bmp, 
         phototoload.imageview); 
       activity activity = (activity) phototoload.imageview 
         .getcontext(); 
       activity.runonuithread(bd); 
      } 
     } 
     if (thread.interrupted()) 
      break; 
    } 
   } catch (interruptedexception e) { 
    // 在这里允许线程退出 
   } 
  } 
 } 
 // 在ui线程中显示bitmap图像 
 class bitmapdisplayer implements runnable { 
  bitmap bitmap; 
  imageview imageview; 
  public bitmapdisplayer(bitmap bitmap, imageview imageview) { 
   this.bitmap = bitmap; 
   this.imageview = imageview; 
  } 
  public void run() { 
   if (bitmap != null) 
    imageview.setimagebitmap(bitmap); 
   else 
    imageview.setimageresource(stub_id); 
  } 
 } 
 public void clearcache() { 
  memorycache.clear(); 
  filecache.clear(); 
 } 
} 

4、紧接着是几个实体类,一个是缓存到sd卡中的实体类,还有一个是缓存到手机内存中的实体类。代码如下:

(1)、缓存到sd卡的实体类:

package net.loonggg.util; 
import java.io.file; 
import android.content.context; 
public class filecache { 
 private file cachedir; 
 public filecache(context context) { 
  // 找到保存缓存的图片目录 
  if (android.os.environment.getexternalstoragestate().equals( 
    android.os.environment.media_mounted)) 
   cachedir = new file( 
     android.os.environment.getexternalstoragedirectory(), 
     "newnews"); 
  else 
   cachedir = context.getcachedir(); 
  if (!cachedir.exists()) 
   cachedir.mkdirs(); 
 } 
 public file getfile(string url) { 
  string filename = string.valueof(url.hashcode()); 
  file f = new file(cachedir, filename); 
  return f; 
 } 
 public void clear() { 
  file[] files = cachedir.listfiles(); 
  for (file f : files) 
   f.delete(); 
 } 
} 

(2)、缓存到手机内存的实体类:

package net.loonggg.util; 
import java.lang.ref.softreference; 
import java.util.hashmap; 
import android.graphics.bitmap; 
public class memorycache { 
 private hashmap<string, softreference<bitmap>> cache=new hashmap<string, softreference<bitmap>>(); 
 public bitmap get(string id){ 
  if(!cache.containskey(id)) 
   return null; 
  softreference<bitmap> ref=cache.get(id); 
  return ref.get(); 
 } 
 public void put(string id, bitmap bitmap){ 
  cache.put(id, new softreference<bitmap>(bitmap)); 
 } 
 public void clear() { 
  cache.clear(); 
 } 
} 

5、这个是输入输出流转换的类,及方法:

package net.loonggg.util; 
import java.io.inputstream; 
import java.io.outputstream; 
public class imageutil { 
 public static void copystream(inputstream is, outputstream os) { 
  final int buffer_size = 1024; 
  try { 
   byte[] bytes = new byte[buffer_size]; 
   for (;;) { 
    int count = is.read(bytes, 0, buffer_size); 
    if (count == -1) 
     break; 
    os.write(bytes, 0, count); 
   } 
  } catch (exception ex) { 
  } 
 } 
} 

到这里基本就完成了。

希望本文所述对大家android程序设计有所帮助。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网