本文实例讲述了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,imageVIEw); imageVIEw.setimageResource(stub_ID); } } private voID queuePhoto(String url,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),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,buffer_size); if (count == -1) break; os.write(bytes,count); } } catch (Exception ex) { } } }
到这里基本就完成了。
希望本文所述对大家AndroID程序设计有所帮助。
总结以上是内存溢出为你收集整理的Android编程学习之异步加载图片的方法全部内容,希望文章能够帮你解决Android编程学习之异步加载图片的方法所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)