通过意图拍照到内部(应用程序)存储 returns 位图为 null

Taking Picture via intent to internal (app) storage returns null for bitmap

适用范围:我想通过intent拍照并将照片保存到我的应用程序的内部存储中。 然后我想将缩放版本加载到字节数组(来自输入流),将此缩放图像作为字节数组保存到 SQLight 中。 将其保存到数据库后,我想删除图片。

(这个问题只是关于保存图像到内部存储的问题,范围只在这里因为总是有人问它)

问题:我无法将图片保存到内部存储器。

我将在调试会话中添加示例作为变量后面的注释,以显示我在测试时获得的值。

我有一个 ImageView,它有一个启动 takePictureIntent 的 onClickListener:

具有以下全局属性:

Uri mCurrentPhotoUri; //URI to file
File mCurrentPicture; //the current picture don't know if I need it somewhere but for complete understanding of code

imageView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //Intent for the on-board camera
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        //device has camera
        if(takePictureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                //create a file with path the code below
                photoFile = createImageFile(); //sets photoFile to: /data/data/my.app.project/app_photo/JPEG_20151105_092219_-1434131481.jpg
            } catch (IOException e) {
                e.printStackTrace();
            }
            //file has been created, set members and add Extra to intent, then start intent.
            if(photoFile != null) {
                mCurrentPicture = photoFile; // well, same as above
                mCurrentPhotoUri = Uri.fromFile(photoFile); // this looks somehow wrong, but I don't know much about URIs: file:///data/data/my.app.project/app_photo/JPEG_20151105_092219_-1434131481.jpg
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); //same URI as above that extra should be needed to tell the cam that I don't want to save to the default path but my app path
                startActivityForResult(takePictureIntent, 10); //start the intent and use requestcode 10 for onActivityResult ...
            }
        }
    }
});

创建文件路径:

//code from google developers with some changes. 
private File createImageFile() throws IOException {
        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); //from today value: 20151105_092219
        String imageFilename = "JPEG_" + timestamp + "_"; // concat is this: JPEG_20151105_092219_
        File storageDir = this.getDir("photo", MODE_PRIVATE); //String path is: /data/data/my.app.project/app_photo
        storageDir.mkdirs();
        File image = File.createTempFile(imageFilename, ".jpg", storageDir); //String path is: /data/data/my.app.project/app_photo/JPEG_20151105_092219_-1434131481.jpg
        mCurrentPhotoPath = "file:" + image.getAbsolutePath(); //here I put the absolute path into static mCurrentPhotoPath, concate with the "file:" from googledeveloper guide: file:/data/data/my.app.project/app_photo/JPEG_20151105_092219_-1434131481.jpg
        return image;
}

所以相机打开,我可以拍照,我问我是否要保存那张照片(全部来自内置相机应用程序,设备是三星 galaxy note)。

然后调用我的 onActivityResult-Method: 我使用数据作为参数,因为我将迷你字节数组用于某些东西,但是对于自定义存储,这个 returns null 它不再被使用 .

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK) {
        switch(requestcode) {
            ...
            case 10:
                setImageView(ivPreview1, data, 0);
                ivPreview.setVisibility(View.VISIBLE);
                break;
            ...
        }
    ...
    }
}

方法 setImageView:

private void setImageView(ImageView iv, Intent data, int index) {
        try {
            Uri u = mCurrentPhotoUri; //sets u to: file:///data/data/my.app.project/app_photo/JPEG_20151105_092219_-1434131481.jpg

            File file = new File(u.getPath()); //sets file to: /data/data/my.app.project/app_photo/JPEG_20151105_092219_-1434131481.jpg
            Bitmap bm = null;
            ByteArrayOutputStream baos = null;
            int orientation = 0;
            if (file.exists()) { //this is true

                //found that somewhere in the developer training:
                ExifInterface exif = null;
                try {
                    exif = new ExifInterface(photoUri.getPath());
                } catch (IOException e) {
                    e.printStackTrace();
                }

                if(exif != null)
                    orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); //is 0 (i didn't rotate the tablet)

                //resulution I want to resize the image to:
                int reqWidth = 960, reqHeight = 1280;

                //exchange values if orientation doesn't match landscape
                if (orientation == 0 || orientation == 270) {
                    int temp = reqWidth;
                    reqWidth = reqHeight;
                    reqHeight = temp;

                }
                //this I used before I changed to internal storage to change the size of the image code below
                bm = ImageManager.decodeSampledBitmapFromFile(u.getPath(), reqWidth, reqHeight); // returns null because of this everything following is null too.

                if (orientation == 90 || orientation == 180 || orientation == 270) {
                    Matrix matrix = new Matrix();
                    // rotate the Bitmap

                    if (orientation == 90)
                        matrix.postRotate(90F);
                    else if (orientation == 270)
                        matrix.postRotate(-90F);
                    else
                        matrix.postRotate(180F);

                    // recreate the new Bitmap
                    bm = Bitmap.createBitmap(bm, 0, 0,
                            bm.getWidth(), bm.getHeight(), matrix, true);
                }
                baos = new ByteArrayOutputStream();
                bm.compress(Bitmap.CompressFormat.JPEG, 50, baos);
            }

            iv.setImageBitmap(bm);

        } catch (Exception e) {
            e.printStackTrace();
                Log.e(TAG, "Could not take Photo: ", e);
        }
    }

我用来解码文件的以下方法(自定义:http://developer.android.com/downloads/samples/DisplayingBitmaps.zip):

带有BitmapFactory.decodeFile(filename, options)的行;还创建一个日志条目:D/skia: --- SkImageDecoder::Factory 返回 null

public static Bitmap decodeSampledBitmapFromFile(String filename,
    int reqWidth, int reqHeight) {
    //this gets parameters:
    // reqHeight: 960, reqWidth: 1280 and filename: /data/data/my.app.project/app_photo/JPEG_20151105_092219_-1434131481.jpg

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;

    BitmapFactory.decodeFile(filename, options); // this adds outHeight and outWidth to -1 (variables from options) 
    //this also creates a log entry: D/skia: --- SkImageDecoder::Factory returned null

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;



    Bitmap bmp = BitmapFactory.decodeFile(filename, options);
    return bmp;
}

public static int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        // BEGIN_INCLUDE (calculate_sample_size)
        // Raw height and width of image
        final int height = options.outHeight; //is -1
        final int width = options.outWidth; //is -1
        int inSampleSize = 1;

        //because its obviously smaller in both statements code will not be executed so it returns 1
        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }

            // This offers some additional logic in case the image has a strange
            // aspect ratio. For example, a panorama may have a much larger
            // width than height. In these cases the total pixels might still
            // end up being too large to fit comfortably in memory, so we should
            // be more aggressive with sample down the image (=larger inSampleSize).

            long totalPixels = width * height / inSampleSize;

            // Anything more than 2x the requested pixels we'll sample down further
            final long totalReqPixelsCap = reqWidth * reqHeight * 2;

            while (totalPixels > totalReqPixelsCap) {
                inSampleSize *= 2;
                totalPixels /= 2;
            }
        }
        return inSampleSize;
        // END_INCLUDE (calculate_sample_size)
    }

我被困在这个问题上好几天了,我没有任何想法可以解决我的问题。这也是由于缺乏 android 知识以及我无法在我的电脑上使用模拟器,所以我什至无法查看应用程序文件夹以查看是否拍摄了照片。

尝试获取存储临时图像的路径,例如 below.which 将 return 您的应用程序文件夹 location.and 也添加权限。

 File dir = context.getExternalFilesDir(null)+"/"+"photo";

Add uses-feature for camera access too.

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />

官方documentation.