macgg 发表于 2013-8-28 16:27

androud异步加载,处理图片溢出

88最近在Android开发中碰到比较棘手的问题,就是加载图片内存溢出。我开发的是一个新闻应用,应用中用到大量的图片,一个界面中可能会有上百张图片。开发android应用的朋友可能或多或少碰到加载图片内存溢出问题,一般情况下,加载一张大图就会导致内存溢出,同样,加载多张图片内存溢出的概率也很高。   
列一下网络上查到的一般做法:   
1.使用BitmapFactory.Options对图片进行压缩   
2.优化加载图片的adapter中的getView方法,使之尽可能少占用内存   
3.使用异步加载图片的方式,使图片在页面加载后慢慢载入进来。   
   
1、2步骤是必须做足的工作,但是对于大量图片的列表仍然无法解决内存溢出的问题,采用异步加载图片的方式才能有效解决图片加载内存溢出问题。   
测试的效果图如下:   
http://www.apkbus.com/data/attachment/forum/201307/29/231454mxfcjobb3occxtbx.jpg   
   
   
在这里我把主要的代码贴出来,给大家分享一下。   
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);                        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);      }}   
   
复制代码   
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);                        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;                        for (;;) {                              int count = is.read(bytes, 0, buffer_size);                              if (count == -1)                                        break;                              os.write(bytes, 0, count);                        }                } catch (Exception ex) {                }      }}   
   
复制代码   
到这里基本就完成了
页: [1]
查看完整版本: androud异步加载,处理图片溢出