为什么我的应用程序会使用所有内存并出现OutOfMemoryError: Failed to allocate错误?

15

为什么这总是发生?我甚至没有位图可以回收,也不知道为什么我的应用程序会抛出内存错误。

我从相册中选择图像,以下是从相册获取图像并在某个imageView上显示它的代码。

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    ImageView b = (ImageView)  findViewById(R.id.viewImage);
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        if (requestCode == 1) {
            File f = new File(Environment.getExternalStorageDirectory().toString());
            for (File temp : f.listFiles()) {
                if (temp.getName().equals("temp.jpg")) {
                    f = temp;
                    break;
                }
            }
            try {
                Bitmap bitmap;
                BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();

                bitmap = BitmapFactory.decodeFile(f.getAbsolutePath(),
                        bitmapOptions);

                b.setImageBitmap(bitmap);

                String path = android.os.Environment
                        .getExternalStorageDirectory()
                        + File.separator
                        + "Phoenix" + File.separator + "default";
                f.delete();
                OutputStream outFile = null;
                File file = new File(path, String.valueOf(System.currentTimeMillis()) + ".jpg");
                try {
                    outFile = new FileOutputStream(file);
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 85, outFile);
                    outFile.flush();
                    outFile.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (requestCode == 2) {

            Uri selectedImage = data.getData();
            String[] filePath = { MediaStore.Images.Media.DATA };
            Cursor c = getContentResolver().query(selectedImage,filePath, null, null, null);
            c.moveToFirst();
            int columnIndex = c.getColumnIndex(filePath[0]);
            String picturePath = c.getString(columnIndex);
            c.close();
            Bitmap thumbnail = (BitmapFactory.decodeFile(picturePath));
            Log.w("path of image from gallery......******************.........", picturePath+"");
            b.setImageBitmap(thumbnail);
            b.setTag(picturePath);

        }
    }
}

接下来是用户图像下方的3个ImageButtons(从相册选择)。当用户按下其中一个按钮时,将获取该imageView可绘制对象的名称并发送到另一个活动中,然后我想在该Imagebutton上加上水印用户图像并显示在一个imageView中。

private static HashMap<Integer,String> activityMap = new HashMap<Integer,String>();
static {
    activityMap.put( R.id.agahi1,"agahi1");
    activityMap.put( R.id.agahi2,"agahi2");
}
// use this in the layout xml file for all the buttons onClick attribute
public void Clicked( View vw ) {
    String a = String.valueOf(activityMap.get(vw.getId()));

    Intent i = new Intent(this,fotCreator.class);

    ImageView b = (ImageView)  findViewById(R.id.viewImage);

    String c = (String) b.getTag();

    i.putExtra("image",c);
    i.putExtra("frame",a);
    startActivity(i);
}

这是水印应用程序的代码。

    package net.svncorp.shadikade;

import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;


public class fotCreator extends ActionBarActivity {
    private AsyncCaller myasync;
    private ProgressBar bar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fot_creator);
        //progress bar
        bar = (ProgressBar) this.findViewById(R.id.progressBar);


        MainActivity.checkversion(this);


    }
    @Override
    public void onBackPressed() {
        super.onBackPressed();
        this.finish();
    }
    @Override
    protected void onResume() {
        super.onResume();
        myasync = new AsyncCaller();
        myasync.execute();

    }

    private class AsyncCaller extends AsyncTask<Void, Void, Drawable>
    {



        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            bar.setVisibility(View.VISIBLE);
        }
        @Override
        protected Drawable doInBackground(Void... params) {

            try {            // simulate here the slow activity
                Intent intent = getIntent();

                final String frame = intent.getStringExtra("frame");
                String image = intent.getStringExtra("image");
                Resources resources = getResources();
                int id = resources.getIdentifier(frame, "drawable", getPackageName());
                Drawable d = resources.getDrawable(id);
                return d;


            } catch (Exception e1) {
                e1.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Drawable result) {
            super.onPostExecute(result);

            //this method will be running on UI thread
            ImageView finalimage = (ImageView)findViewById(R.id.finalimage);
            if (isCancelled() || result == null) {
                return;
            }
            finalimage.setImageDrawable(result);
            bar.setVisibility(View.GONE);

        }



    }


    @Override
    protected void onPause() {
        super.onPause();
        myasync.cancel(true);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_fot_creator, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }



}

现在这段代码对于点击图像按钮并进入水印活动的2到3次操作速度非常快,但它变得越来越慢,并且有时会在模拟器中冻结,我在logcat中看到了这些。

 11-24 15:01:45.960    7751-7769/net.svncorp.shadikade I/art﹕ Alloc concurrent mark sweep GC freed 2419(163KB) AllocSpace objects, 1(1047KB) LOS objects, 12% free, 13MB/15MB, paused 0 total 30ms
11-24 15:01:48.760    7751-7768/net.svncorp.shadikade I/art﹕ Clamp target GC heap from 17MB to 16MB
11-24 15:01:48.760    7751-7768/net.svncorp.shadikade I/art﹕ Clamp target GC heap from 17MB to 16MB
11-24 15:01:48.760    7751-7768/net.svncorp.shadikade I/art﹕ Forcing collection of SoftReferences for 1047KB allocation
11-24 15:01:48.790    7751-7768/net.svncorp.shadikade I/art﹕ Alloc concurrent mark sweep GC freed 1303(86KB) AllocSpace objects, 1(993KB) LOS objects, 12% free, 13MB/15MB, paused 0 total 30ms
11-24 15:01:53.700    7751-7751/net.svncorp.shadikade I/Choreographer﹕ Skipped 1272 frames!  The application may be doing too much work on its main thread.
11-24 15:01:55.350    7751-7769/net.svncorp.shadikade I/art﹕ Clamp target GC heap from 17MB to 16MB
11-24 15:01:55.350    7751-7769/net.svncorp.shadikade I/art﹕ Clamp target GC heap from 17MB to 16MB
11-24 15:01:55.350    7751-7769/net.svncorp.shadikade I/art﹕ Forcing collection of SoftReferences for 993KB allocation
11-24 15:01:55.380    7751-7769/net.svncorp.shadikade I/art﹕ Clamp target GC heap from 17MB to 16MB
11-24 15:01:55.380    7751-7769/net.svncorp.shadikade I/art﹕ Alloc concurrent mark sweep GC freed 218(9KB) AllocSpace objects, 0(0B) LOS objects, 5% free, 15MB/16MB, paused 0 total 30ms
11-24 15:01:55.380    7751-7769/net.svncorp.shadikade E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 1017612 byte allocation with 954752 free bytes and 932KB until OOM"
11-24 15:01:55.380    7751-7769/net.svncorp.shadikade D/skia﹕ --- decoder->decode returned false
11-24 15:01:55.380    7751-7769/net.svncorp.shadikade E/AndroidRuntime﹕ FATAL EXCEPTION: AsyncTask #2
    Process: net.svncorp.shadikade, PID: 7751
    java.lang.RuntimeException: An error occured while executing doInBackground()
            at android.os.AsyncTask$3.done(AsyncTask.java:300)
            at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
            at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
            at java.util.concurrent.FutureTask.run(FutureTask.java:242)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)
     Caused by: java.lang.OutOfMemoryError: Failed to allocate a 1017612 byte allocation with 954752 free bytes and 932KB until OOM
            at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
            at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
            at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
            at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
            at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:973)
            at android.content.res.Resources.loadDrawableForCookie(Resources.java:2423)
            at android.content.res.Resources.loadDrawable(Resources.java:2330)
            at android.content.res.Resources.getDrawable(Resources.java:758)
            at android.content.res.Resources.getDrawable(Resources.java:724)
            at net.svncorp.shadikade.fotCreator$AsyncCaller.doInBackground(fotCreator.java:65)
            at net.svncorp.shadikade.fotCreator$AsyncCaller.doInBackground(fotCreator.java:45)
            at android.os.AsyncTask$2.call(AsyncTask.java:288)
            at java.util.concurrent.FutureTask.run(FutureTask.java:237)
            at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
            at java.lang.Thread.run(Thread.java:818)

我认为这是我和许多其他人的问题...任何帮助将不胜感激。


我看到你正在设置一个ImageView的位图,然后调整相同的位图大小。也许将该位图简单复制一份给ImageView。 - danny117
3个回答

44

或许在应用程序标签的清单中添加这行代码会有所帮助;

<application
        ...
        ...
        android:largeHeap="true" >  

     ......
     ......

</application>

@Cüneyt 我知道这是大约两年前的事情,但对于任何通过搜索引擎来查看此响应的人来说,我要点个踩,因为使用 android:largeHeap="true" 应该是最后的选择。 - Anil Gorthy
@AnilGorthy 谢谢您的建议。如果您能提供其他选项,我们可以修正答案。 - Cüneyt
1
在设置largeHeap之前,请阅读此文章:https://medium.com/square-corner-blog/leakcanary-detect-all-memory-leaks-875ff8360745#.lccgfnif5 - Cüneyt

15

在Android 5上遇到过类似问题。将你的图片移动到/drawable-nodpi而不是/drawable。


为什么?你能解释一下吗? - Ton
7
如果您将图像放在 /drawable-nodpi 文件夹中,Android 将保留其大小。 - Samoka
我认为这与使用当前设备 DPI 自动缩放图像有关。来自 /drawable-nodpi 的图像不会被缩放。 - Borzh
1
例如,如果您在/drawable-mdpi中有一个大图像,但您的设备是xxxhdpi,则Android会使用mdpi中可用的图像并尝试将其缩放以匹配xxxhdpi,这可能占用太多内存。但是,来自/drawable-nodpi的图像不会被缩放,它们将按原样使用。 - Borzh
是的!只需要这样! - swooby

6
如果您正在使用模拟器进行测试,请尝试将虚拟设备的 RAM 设置为2GB左右。我曾在虚拟设备上遇到相同的问题,但程序在我的实际安卓设备上运行很顺畅。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接