加载图片时出现OutOfMemoryException

时间:2014-06-30 09:38:43

标签: android bitmap

我想拍照并将它们加载到imageview中,以便我可以切换图片。图片路径保存在数据库中,该数据库提供给我的项目。因此,当我将图片加载到项目中并重新打开项目时,图片仍然可见,因为它们存储在数据库中。

我的问题是,我总是遇到只有3张图片的OutOfMemoryException。我试图将inSampleSize减少到一个低值的连续性。但即使inSampleSize为15,最大图片数量也可以是3。

我使用getAllocationByteCount( )为不同的inSampleSizes测量了位图的分配大小:

  • 15980544 Byte = 15 MB(inSample 1)|| 998784字节= 0.95 MB(inSample 4)
  • 249696 Byte = 0.23 MB(inSample 15)|| 27744 Byte = 0.02 MB(示例30)

由于图片是旋转的(逆时针90°),我用matrix.postRotate(90)旋转它们,并用矩阵创建一个新的位图。我用bitmap.recyle( )删除旧位图。新位图的大小为499392字节= 0.47 MB​​

首先,我开始使用Camera Intent:

// capturePicture( ) soll die Kamera starten und ein neues Bild soll gemacht werden können
private void capturePicture() {

    // erstelle einen neuen Intent, damit sich die Kamera öffnet
    // mit MediaStore.ACTION_IMAGE_CAPTURE wird angewiesen die Kamera zu starten
    CameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    // Sicherstellen, dass auf dem Handy eine Kamera installiert ist
    if (CameraIntent.resolveActivity(getPackageManager()) != null) {


        photoFile = null;


        try {
            // rufe die Methode createImageFile( ) auf, damit die Aufnahme in eine Datei gespeichert werden kann
            photoFile = createImageFile();


        } catch (IOException ex) {

            // wenn das Erstellen des Bildes fehlschlug
            Toast.makeText(thisActivity,"Bilddatei konnte nicht erstellt werden",
                    Toast.LENGTH_LONG).show();
        }

        // wenn es eine Datei gibt, soll diese in der ImageView angezeigt werden
        if (photoFile != null) {
            CameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));

            // rufe die onActivityForResult( ) Methode mit einer ID auf, um das Bild in der ImageView anzeigen zu können
            this.savePicture(photoFile.getAbsolutePath(), photoFile.getName());
            startActivityForResult(CameraIntent, REQUEST_TAKE_PHOTO);
            //bitmap.recycle();
            //System.gc();
            this.reloadPicDB();
        }


    } else {

        // TODO: auf den Play Store mit einer Kamera App verweisen
        Toast.makeText(thisActivity,"Sie haben leider keine Kamera installiert", Toast.LENGTH_SHORT).show();
    }

    photoCount++;

}

在这个方法中,我调用方法createImageFile( )来创建一个Imagefile:

    // erstellt eine Datei nachdem ein Foto gemacht wurde und gibt diese zurück
    private File createImageFile() throws IOException {

        // TODO: Sinnvollen Dateinamen für die Bilddateien geben
        //String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmmss").format(new Date());

        imageFileName = "JPEGNEU";

        // TODO: überlegen, wohin die Bilddateien gespeichert werden sollen
        //File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File pictureStorageDir = new File("/sdcard/neu/");

        // In der Datei pictureImage werden der Name, Endung und Verzeichnis gespeichert        
        File pictureImage = File.createTempFile(imageFileName, /* prefix */
                ".jpg", /* suffix */
                pictureStorageDir /* directory */
        );

        // ermittle den absoluten Pfad der Bilddatei und speichere diesen in den String PhotoPath
        PhotoPath = pictureImage.getAbsolutePath();

        // gebe die Bilddatei zurück, damit dieses in der Methode capturePicture weiter verwendet werden kann
        return pictureImage;

    }

图片将以onActivityResult( )方式显示:

    if(requestCode == REQUEST_TAKE_PHOTO){


            ImageView beispiel = (ImageView) findViewById(R.id.beispiel);
BitmapFactory.Options options1 = new BitmapFactory.Options();
            options1.inSampleSize = 15;     //1/12 des Bildes zu laden

options1.inPreferredConfig = Config.ARGB_4444; 
            Bitmap image = BitmapFactory.decodeFile(PhotoPath, options1);
            Log.d("Speicherausgabe", "Speicherausgabe0.1 " + image.getAllocationByteCount());
Matrix matrix = new Matrix();
            matrix.postRotate(90);

            bitmap = Bitmap.createBitmap(image, 0, 0, image.getWidth() , image.getHeight(), matrix, true);
image.recycle();
            System.gc();

if(zaehlerRequestPhoto == 0){
beispiel.setImageBitmap(bitmap);
} else {
                beispiel.setImageBitmap(bitmap);
                Log.d("Speicherausgabe", "Speicherausgabe3 " + bitmap.getAllocationByteCount());
                //System.gc();




            }

在这种情况下,我该怎么做才能避免OutOfMemoryException?

3 个答案:

答案 0 :(得分:1)

当您的应用程序资源超出可用堆内存时,通常会发生内存不足异常。特别是位图使用更多内存,并且在达到最大内存并且垃圾收集器完成其工作之前,其缓存不会被清除。因此,请使用LruCache避免内存不足或仅使用库中的通用映像加载器。的 https://github.com/nostra13/Android-Universal-Image-Loader

答案 1 :(得分:1)

准备好图像后,您应该回收并将其置为空,如下所示:

image.recycle();
image=null;

OR

您还可以通过添加到AndroidManifest来增加堆空间:

<application 
     android:label="@string/app_name"
     android:largeHeap="true"
     ...

答案 2 :(得分:0)

我在Here

之前提出了这个问题

但是我仍然无法完全解决问题,但是由于@Binh Tran能够通过以下代码降低强度

public Bitmap decodeSampledBitmapFromResourceMemOpt(
            InputStream inputStream, int reqWidth, int reqHeight) {

        byte[] byteArr = new byte[0];
        byte[] buffer = new byte[1024];
        int len;
        int count = 0;

        try {
            while ((len = inputStream.read(buffer)) > -1) {
                if (len != 0) {
                    if (count + len > byteArr.length) {
                        byte[] newbuf = new byte[(count + len) * 2];
                        System.arraycopy(byteArr, 0, newbuf, 0, count);
                        byteArr = newbuf;
                    }

                    System.arraycopy(buffer, 0, byteArr, count, len);
                    count += len;
                }
            }

            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeByteArray(byteArr, 0, count, options);

            options.inSampleSize = calculateInSampleSize(options, reqWidth,
                    reqHeight);
            options.inPurgeable = true;
            options.inInputShareable = true;
            options.inJustDecodeBounds = false;
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;

            int[] pids = { android.os.Process.myPid() };
            MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
            Log.e(TAG, "dalvikPss (decoding) = " + myMemInfo.dalvikPss);

            return BitmapFactory.decodeByteArray(byteArr, 0, count, options);

        } catch (Exception e) {
            e.printStackTrace();

            return null;
        }
    }
The method that does the calculation:

public void onButtonClicked(View v) {
        int[] pids = { android.os.Process.myPid() };
        MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
        Log.e(TAG, "dalvikPss (beginning) = " + myMemInfo.dalvikPss);

        long startTime = System.currentTimeMillis();

        FileInputStream inputStream;
        String filePath = Environment.getExternalStorageDirectory()
                .getAbsolutePath() + "/test2.png";
        File file = new File(filePath);
        try {
            inputStream = new FileInputStream(file);
//          mBitmap = decodeSampledBitmapFromResource(inputStream, 800, 800);
            mBitmap = decodeSampledBitmapFromResourceMemOpt(inputStream, 800,
                    800);
            ImageView imageView = (ImageView) findViewById(R.id.image);
            imageView.setImageBitmap(mBitmap);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
        Log.e(TAG, "dalvikPss (after all) = " + myMemInfo.dalvikPss
                + " time = " + (System.currentTimeMillis() - startTime));
    }