Android编程学习之异步加载图片的方法
程序员文章站
2023-11-16 17:43:16
本文实例讲述了android编程学习之异步加载图片的方法。分享给大家供大家参考,具体如下:
最近在android开发中碰到比较棘手的问题,就是加载图片内存溢出。我开发的是...
本文实例讲述了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程序设计有所帮助。