【问题标题】:AsyncTask and Out Of Memory issue in AndroidAndroid 中的 AsyncTask 和内存不足问题
【发布时间】:2012-05-07 08:59:10
【问题描述】:

根据我的应用程序,首先我将所有图像从我的资源复制到内部存储器,然后在图像向左或向右滑动时,我从内存中获取图像及其索引并将其显示在那里。我正在用 AsynTask 做这件事。在显示大约 10 张图像后,应用程序进入黑屏,并且 log cat 说“外部分配对于这个过程来说太大了”。根据我在这里阅读的内容,我认为问题出在 AsyncTask 上,我无法释放已用于这些任务的内存。 我有三个不同的活动,用于将图像显示为图库,每个活动都使用 asyncTask 来显示图像。

下面是我的一些代码,任何帮助将不胜感激,在此先感谢。 这是我用来根据滑动图片执行图片下载器的Activity。

lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
            lid1.execute();

            imageSwitcher.setOnTouchListener(new OnTouchListener() {
                public boolean onTouch(View v, MotionEvent event) {

                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        downX = (int) event.getX(); 
                        Log.i("event.getX()", " downX " + downX);
                        return true;
                    } 

                    else if (event.getAction() == MotionEvent.ACTION_UP) {
                        upX = (int) event.getX(); 
                        Log.i("event.getX()", " upX " + downX);
                        if (upX - downX > 100) {

                            //curIndex  current image index in array viewed by user
                            curIndex--;
                            if (curIndex < 0) {
                                curIndex = imageList.size()-1;
                            }

                            imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_in_left));
                            imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_out_right));

                            lid1.cancel(true);
                            lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
                            lid1.execute();
                        }

                        else if (downX - upX > -100) {

                            curIndex++;
                            if (curIndex == imageList.size() ) {
                                curIndex = 0;
                            }

                            imageSwitcher.setInAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_in_right));
                            imageSwitcher.setOutAnimation(AnimationUtils.loadAnimation(Activities.this,R.anim.slide_out_left));

                            lid1.cancel(true);
                            lid1 = new LocalImageDownloader(imageSwitcher, myContext, path, nameList.get(curIndex) );
                            lid1.execute();
                        }
                        return true;
                    }
                    return false;
                }
            });

这是我从内存中获取图像的 AsyncTask,

public class LocalImageDownloader extends AsyncTask<String, Void, Bitmap> {

String url;
Drawable d;
Context myContext;

String path;
String fileName;

ProgressDialog dialog;
int REQUIRED_SIZE=600;

private final WeakReference<ImageSwitcher> imageViewReference;

public LocalImageDownloader(ImageSwitcher imageSwitcher,Context myContext, String path, String fileName) {
    this.myContext = myContext;
    this.path = path;
    this.fileName = fileName;
    imageViewReference = new WeakReference<ImageSwitcher>(imageSwitcher);
}

@Override
protected Bitmap doInBackground(String... urls) {
    publishProgress();
    return null;
}

@Override
protected void onPreExecute() {
    dialog = ProgressDialog.show(myContext, "", "Loading Images...", true);
    super.onPreExecute();
}

@Override
protected void onPostExecute(Bitmap result) {

    try {
        if (imageViewReference != null) {
            ImageSwitcher imageSwitcher = imageViewReference.get();
            if (imageSwitcher != null) {
                imageSwitcher.setImageDrawable(getLocalImage());
            }
        }
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    dialog.dismiss();
}

public Drawable getLocalImage() throws IOException {

    File file = new File(path,fileName);

    //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(new FileInputStream(file),null,o);

    //The new size we want to scale to

    //Find the correct scale value. It should be the power of 2.
    int scale=1;
    while(o.outWidth/scale/2>=this.REQUIRED_SIZE && o.outHeight/scale/2>=this.REQUIRED_SIZE)
        scale*=2;

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize=scale;
    o.inJustDecodeBounds = false;

    return new BitmapDrawable(BitmapFactory.decodeStream(new FileInputStream(file), null, o2));
}

}

编辑: 我已经应用了一些方法来更有效地使用位图,现在我将它们推送到内存中,但我仍然遇到几乎相同的错误。一些图像存储在内存中后,对于一些图像,我会黑屏并出现相同的错误。“此过程的外部分配太大。”知道怎么做吗?

这是下面的内存缓存代码,我将我的 MemoryCache 对象作为参数发送给 AsyncTask。

public class MemoryCache {

private static final String TAG = "MemoryCache";
private Map<String, Bitmap> cache=Collections.synchronizedMap(
        new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering
private long size=0;//current allocated size
private long limit=1000000;//max memory in bytes

public MemoryCache(){
    //use 50% of available heap size
    setLimit(Runtime.getRuntime().maxMemory()/2);
}

public void setLimit(long new_limit){
    limit=new_limit;
    Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
}

public Bitmap get(String id){
    try{
        if(!cache.containsKey(id))
            return null;
        //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
        return cache.get(id);
    }catch(NullPointerException ex){
        return null;
    }
}

public void put(String id, Bitmap bitmap){
    try{
        if(cache.containsKey(id))
            size-=getSizeInBytes(cache.get(id));
        cache.put(id, bitmap);
        size+=getSizeInBytes(bitmap);
        checkSize();
    }catch(Throwable th){
        th.printStackTrace();
    }
}

private void checkSize() {
    Log.i(TAG, "cache size="+size+" length="+cache.size());
    if(size>limit){
        Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated  
        while(iter.hasNext()){
            Entry<String, Bitmap> entry=iter.next();
            size-=getSizeInBytes(entry.getValue());
            iter.remove();
            if(size<=limit)
                break;
        }
        Log.i(TAG, "Clean cache. New size "+cache.size());
    }
}

public void clear() {
    cache.clear();
}

long getSizeInBytes(Bitmap bitmap) {
    if(bitmap==null)
        return 0;
    return bitmap.getRowBytes() * bitmap.getHeight();
}

public boolean contains(String key) {

    if(cache.containsKey(key)) {
        return true;
    }

    return false;
}

}

【问题讨论】:

    标签: android asynchronous bitmap android-asynctask


    【解决方案1】:

    试试这个链接来管理你的位图缓存,有很多解决方案可以防止 outofMemory 异常。 http://developer.android.com/training/displaying-bitmaps/index.html

    【讨论】:

    • 不适合大尺寸图片吗?因为我的图像大约是 50-100KB,所以我认为这并不重要。 ???
    • 这与位图的大小无关,也与位图的数量有关。!!如果您收到错误,位图内存不足,那么您需要通过提供软引用或将它们存储在 sdcard 上来管理它们。在普通设备中,分配给任何应用程序的 cos 内存为 16 MB,因此即使您在开发基于大量图像的应用程序时也必须小心。所以最好使用该链接中描述的任何方法。:)
    • 我会尽快尝试,如果有效,请告诉您,顺便感谢您的回答。
    • 嘿伙计,谢谢它的答案,它工作了一点,至少它不再粉碎,但我仍然有同样的错误。如果内存没有足够的位图,它仍然不显示,我只是得到一些图像的黑屏。知道怎么做吗?我将使用新代码更新我的问题。
    • 我找到了一种方法,现在它工作得很好,而不是现在通过文件流获取图像,我通过使用下面的代码设置 drawable 来做到这一点。谢谢您的帮助。 ImageView imageView = (ImageView) findViewById(R.id.image); File filePath = getFileStreamPath(fileName); imageView.setImageDrawable(Drawable.createFromPath(fileName));
    【解决方案2】:

    它与 AsyncTask 无关。您将不得不更有效地使用位图。 搜索位图OOM; 对您下载的图像进行一些采样。如here

    同时搜索 Android SoftReference。

    【讨论】:

    • 我尝试了一些有效地使用位图的方法,但我认为这不是为了使用它,因为我的图像大约 50KB,而且尺寸也很小。
    【解决方案3】:

    我找到了一种方法,现在效果很好,现在我不是通过文件流和位图获取图像,而是通过使用下面的代码设置可绘制来实现。至于从互联网下载的图像,任何想要做的人类似的东西,只需下载图像并将其保存在内部存储器或外部存储器中,然后只需提供图像的路径即可。

    ImageView imageView = (ImageView) findViewById(R.id.image); 
    File filePath = getFileStreamPath(fileName);
    imageView.setImageDrawable(Drawable.createFromPath(filePath));
    

    【讨论】:

    • 我没有看它,但我认为每次滑动图像时它只是从输入流中获取图像,而不是将图像加载到内存中。因为它不是那么快就像在记忆中一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-18
    • 2013-08-07
    相关资源
    最近更新 更多