Android内存不足位图

时间:2014-01-11 15:06:55

标签: android memory bitmap

我的应用程序出现问题:

java.lang.OutOfMemoryError: Failed to allocate a 1136368 byte allocation with 127432 free bytes
        at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
        at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
        at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
        at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:422)
        at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:840)
        at android.content.res.Resources.loadDrawable(Resources.java:2110)
        at android.content.res.Resources.getDrawable(Resources.java:700)
        at android.widget.ImageView.resolveUri(ImageView.java:636)
        at android.widget.ImageView.setImageResource(ImageView.java:366)
        at com.scoutapp.lite.ImageLoader.DisplayImage(ImageLoader.java:40)
        at com.scoutapp.lite.CustomArrayAdapter.getView(CustomArrayAdapter.java:49)
        at android.widget.AbsListView.obtainView(AbsListView.java:2242)
        at android.widget.ListView.makeAndAddView(ListView.java:1790)
        at android.widget.ListView.fillDown(ListView.java:691)
        at android.widget.ListView.fillGap(ListView.java:655)
        at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5148)
        at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:4259)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
        at android.view.Choreographer.doCallbacks(Choreographer.java:574)
        at android.view.Choreographer.doFrame(Choreographer.java:543)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5019)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)

基本上我有一个活动,其中有一个带有一些图像的列表。它们正确加载,但当我向下滚动时,我有一个力接近此错误。在这里,我发布了我的两个课程:

public class CustomArrayAdapter extends ArrayAdapter<String> {
private final Context context;
private final String[] values;
private final String[] img;
public ImageLoader imageLoader;

String t;

public CustomArrayAdapter(Context context, String[] values,String[]img) {
    super(context, R.layout.activity_spec, values);
    this.context = context;
    this.values = values;
    this.img=img;
    imageLoader=new ImageLoader(getContext().getApplicationContext());
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    View rowView = inflater.inflate(R.layout.list_row, parent, false);
    TextView textView = (TextView) rowView.findViewById(R.id.title);
    ImageView imageView = (ImageView) rowView.findViewById(R.id.list_image);
    textView.setText(values[position]);

    // Change icon based on name
    String s = values[position];
//        System.out.println(t);
    //Log.d(t,img[position]);
    if(img==null){

    }else{

    int resourceId = context.getResources().getIdentifier(img[position],"drawable", context.getPackageName());
   // int drawableId = Integer.parseInt(img[position]);
        Bitmap bm;
      //  bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath),100, 100, true);
        //mPicture = new ImageView(context);
        //mPicture.setImageBitmap(bm);
        imageLoader.DisplayImage(resourceId, imageView);
    //imageView.setImageResource(resourceId);

            //img.recycle();
            //img = null;

    }
   /* if (s.equals("Lupetti")) {
        imageView.setImageResource(R.drawable.lupetti1);
    } else if (s.equals("Reparto")) {
        imageView.setImageResource(R.drawable.reparto1);
    } else if (s.equals("Clan")) {
        imageView.setImageResource(R.drawable.clan1);
    }
*/
    return rowView;
}
}

public class ImageLoader {

MemoryCache memoryCache=new MemoryCache();
FileCache fileCache;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService; 

public ImageLoader(Context context){
    fileCache=new FileCache(context);
    executorService=Executors.newFixedThreadPool(5);
}

final int stub_id = R.drawable.ic_stub;
public void DisplayImage(int url, ImageView imageView)
{
    imageViews.put(imageView, "url");
    //Bitmap bitmap=memoryCache.get(url);
    if(url!=0)
        imageView.setImageResource(url);
    else
    {
        queuePhoto("url", imageView);
       imageView.setImageResource(stub_id);
    }
}

private void queuePhoto(String url, ImageView imageView)
{
    PhotoToLoad p=new PhotoToLoad(url, imageView);
    executorService.submit(new PhotosLoader(p));
}

private Bitmap getBitmap(String url) 
{
    File f=fileCache.getFile(url);

    //from SD cache
    Bitmap b = decodeFile(f);
    if(b!=null)
        return b;

    //from web
    try {
        Bitmap bitmap=null;
        URL imageUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
        conn.setConnectTimeout(30000);
        conn.setReadTimeout(30000);
        conn.setInstanceFollowRedirects(true);
        InputStream is=conn.getInputStream();
        OutputStream os = new FileOutputStream(f);
        Utils.CopyStream(is, os);
        os.close();
        bitmap = decodeFile(f);
        return bitmap;
    } catch (Exception ex){
       ex.printStackTrace();
       return null;
    }
}

//decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f){
    try {
        //decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f),null,o);

        //Find the correct scale value. It should be the power of 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;
        }

        //decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize=scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}

//Task for the queue
private class PhotoToLoad
{
    public String url;
    public ImageView imageView;
    public PhotoToLoad(String u, ImageView i){
        url=u; 
        imageView=i;
    }
}

class PhotosLoader implements Runnable {
    PhotoToLoad photoToLoad;
    PhotosLoader(PhotoToLoad photoToLoad){
        this.photoToLoad=photoToLoad;
    }

    @Override
    public void run() {
        if(imageViewReused(photoToLoad))
            return;
        Bitmap bmp=getBitmap(photoToLoad.url);
        memoryCache.put(photoToLoad.url, bmp);
        if(imageViewReused(photoToLoad))
            return;
        BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
        Activity a=(Activity)photoToLoad.imageView.getContext();
        a.runOnUiThread(bd);
    }
}

boolean imageViewReused(PhotoToLoad photoToLoad){
    String tag=imageViews.get(photoToLoad.imageView);
    if(tag==null || !tag.equals(photoToLoad.url))
        return true;
    return false;
}

//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
    Bitmap bitmap;
    PhotoToLoad photoToLoad;
    public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
    public void run()
    {
        if(imageViewReused(photoToLoad))
            return;
        if(bitmap!=null)
            photoToLoad.imageView.setImageBitmap(bitmap);
        else
            photoToLoad.imageView.setImageResource(stub_id);
    }
}

public void clearCache() {
    memoryCache.clear();
    fileCache.clear();
}

}

我知道如何解决这个问题?谢谢!

2 个答案:

答案 0 :(得分:3)

android中的位图可能存在许多问题,但首先你的适配器实现非常糟糕,你不会重复使用列表行,我确信它很慢,这可能是你问题的根源。 / p>

首先需要做的是阅读convertView模式,使用它然后告诉我们问题是否仍然存在。

<强>更新

每次调用getView()时,您都不需要获取LayoutInflater,您可以在适配器构造函数中设置它,然后重复使用它。

下一个listView有一种机制,只能创建在屏幕上可见的行数,当一行进入屏幕然后重复使用时。您需要做的是检查是否必须从头开始创建新行或重用现有行并用新数据填充。

public class CustomArrayAdapter extends ArrayAdapter<String> {

    // Variable declarations
    LayoutInflater inflater;

    public CustomArrayAdapter(Context context, String[] values,String[]img) {
        // your code
        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

         // convertView is null when new row is needed
         View rowView = convertView;

         if(rowView == null) {
             // new row is needed to inflate new row
             rowView = inflater.inflate(R.layout.list_row, parent, false);
         }


         TextView textView = (TextView) rowView.findViewById(R.id.title);
         ImageView imageView = (ImageView) rowView.findViewById(R.id.list_image);
         textView.setText(values[position]);

         // rest of your code
         return rowView;
     }
 }

当然,您可以使用Holder模式进行进一步的优化。

尝试实现所有这些,然后告诉我们您是否仍然遇到OutOfMemory错误

答案 1 :(得分:3)

我想你应该在Androids Developer页面阅读一些内容,特别是在这里: http://developer.android.com/training/displaying-bitmaps/index.html

阅读所有5节课,然后重新编写代码。如果它仍然不起作用,我们将很高兴看到您对教程材料的错误。