Android将图片保存到sd卡时出错

Android将图片保存到sd卡时出错,android,camera,image,sd-card,Android,Camera,Image,Sd Card,我有一个Android应用程序,它在一些手机上崩溃了,而在其他手机上没有。它有大约5千次下载,到目前为止已经收到了大约50份崩溃报告。然而,根据我从用户那里得到的评价/评论的数量,我认为受影响用户的百分比实际上要高得多 由于这个问题不影响我的手机,我无法重现错误并按照我的意愿进行调试 该应用程序从相机上拍摄一张照片,在上面覆盖一个位图,并将生成的图像保存到SD卡上 下面是我的onPictureTakenPictureCallback方法的代码 private Camera.Picture

我有一个Android应用程序,它在一些手机上崩溃了,而在其他手机上没有。它有大约5千次下载,到目前为止已经收到了大约50份崩溃报告。然而,根据我从用户那里得到的评价/评论的数量,我认为受影响用户的百分比实际上要高得多

由于这个问题不影响我的手机,我无法重现错误并按照我的意愿进行调试

该应用程序从相机上拍摄一张照片,在上面覆盖一个位图,并将生成的图像保存到SD卡上

下面是我的
onPictureTaken
PictureCallback方法的代码

    private Camera.PictureCallback mPicture = new Camera.PictureCallback() {

    public void onPictureTaken(byte[] data, Camera camera) {

        captureBtn.setVisibility(ImageButton.INVISIBLE); 

        File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
        if (pictureFile == null){
            return;
        }

        BitmapFactory.Options options = new BitmapFactory.Options();
        Bitmap mutableBitmap = null;
        Bitmap finalBitmap = null; 
        byte[] byteArray = null; 
        try {   
            mutableBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options).copy(Bitmap.Config.RGB_565, true);
            Matrix matrix = new Matrix();
            int width = mutableBitmap.getWidth();
            int height = mutableBitmap.getHeight();
            int newWidth = overlayView.getDrawable().getBounds().width();
            int newHeight = overlayView.getDrawable().getBounds().height();
            float scaleWidth = ((float) newWidth) / width;
            float scaleHeight = ((float) newHeight) / height;
            matrix.postScale(scaleWidth, scaleHeight);
            matrix.postRotate(90);

            Bitmap resizedBitmap = Bitmap.createBitmap(mutableBitmap, 0, 0, mutableBitmap.getWidth(), mutableBitmap.getHeight(), matrix, true);
            finalBitmap = resizedBitmap.copy(Bitmap.Config.RGB_565, true);
            Canvas canvas = new Canvas(finalBitmap);

            Bitmap overlayBitmap = null;
            if (mWeaponType == wep1) {
                overlayBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.wep1);
            } else if (mWeaponType == wep2) {
                overlayBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.wep2); 
            }

            if (overlayBitmap != null) {

                matrix = new Matrix();
                matrix.postRotate(90);
                Bitmap resizedOverlay = Bitmap.createBitmap(overlayBitmap, 0, 0, overlayBitmap.getWidth(), overlayBitmap.getHeight(), matrix, true);
                canvas.drawBitmap(resizedOverlay, 0, 0, new Paint());
                canvas.scale(50, 0);
                canvas.save();
                //finalBitmap is the image with the overlay on it

                // rotate image to save in landscape mode
                matrix = new Matrix(); 
                matrix.postRotate(270); 
                finalBitmap = Bitmap.createBitmap(finalBitmap, 0, 0, finalBitmap.getWidth(), finalBitmap.getHeight(), matrix,
                        true);

                // convert final bitmap to byte array
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                finalBitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
                byteArray = stream.toByteArray();
            }
        }

        catch(OutOfMemoryError e) {
            //fail
        }

        try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(byteArray); 
                fos.close();

                // Notify system that SD card has been updated
                sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
                Log.i(TAG, "Picture saved, intent broadcast that SD has been updated");
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
        } catch (NullPointerException e) {

        }

        finally {
            mCamera.startPreview();
            captureBtn.setVisibility(ImageButton.VISIBLE);
        }
    }
};
我得到一个NullPointerException,如下所示:

java.lang.NullPointerException
at java.io.FileOutputStream.write(FileOutputStream.java:256)
at com.mypackage.MyActivity.onPictureTaken(MyActivity.java:215)
哪一个是

 fos.write(byteArray); 
线路

上面列出的
getOutputMediaFile
方法如下:

/** Create a File for saving an image or video */
private static File getOutputMediaFile(int type){
    // To be safe, you should check that the SDCard is mounted
    // using Environment.getExternalStorageState() before doing this.

    File mediaStorageDir = Environment.getExternalStoragePublicDirectory(
              Environment.DIRECTORY_PICTURES);

    // Create the storage directory if it does not exist
    if (! mediaStorageDir.exists()){
        if (! mediaStorageDir.mkdirs()){
            Log.d("HalfLife2 Booth", "failed to create directory");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    if (type == MEDIA_TYPE_IMAGE){
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "IMG_"+ timeStamp + ".jpg");
    } else if(type == MEDIA_TYPE_VIDEO) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator +
        "VID_"+ timeStamp + ".mp4");
    } else {
        return null;
    }

    return mediaFile;
}
我认为问题可能就在这里的某个地方,在尝试获取输出媒体文件时。我已尝试更新应用程序以使用:

    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
              Environment.DIRECTORY_PICTURES), "MyApp");
相反,一个用户报告问题已得到解决,但有几个用户报告问题仍然存在


有什么想法吗?我发现这是一个棘手的调试/修复,当我无法复制或接近它

确保以原始格式缩放图片。我发现一些手机型号上的原始图像很大。如果映像非常大,我建议对其进行缩放,因为您将耗尽虚拟机的内存

例如:

     imageRef = sd_card_path+"/"+ImageName;
                BitmapFactory.Options resample = new BitmapFactory.Options();
                resample.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(imageRef, resample);
                int width = resample.outWidth;
                int height = resample.outHeight;
                if(width*height > utility.IMAGE_SIZE_MIN){
                    int imageSizef = (int)(width*height/utility.IMAGE_SIZE_MIN);
                    String imageSize = imageSizef+"";
                    resample.inSampleSize = Integer.parseInt(imageSize);
                    resample.inJustDecodeBounds = false;
                    image.setImageBitmap(BitmapFactory.decodeFile(imageRef , resample));                    
                }else{
                    image.setImageBitmap(BitmapFactory.decodeFile(imageRef ));
                }

如果出现异常跟踪,则表明您遇到了OutOfMemorryError,因此byteArray从未被填充,因此您将null传递给fos.write方法

请确保您在清单中有说明如果未设置权限,我认为所有手机上都会出现此问题。这是正确的,权限设置正确。好的,谢谢您的建议!如何实现这一点?我意识到示例使用了中的一个文件,因此这里有一行直接来自摄影机流:Bitmap Bitmap=BitmapFactory.decodeByteArray(image,0,image.length,resample);感谢Android Addict,我可以问一下你的“实用程序”对象是什么,你的“图像大小”的值来自哪里吗?实用程序类只是我用来保存应用程序各个部分所需数据的一个单独的工具,比如图像大小。如果图像大于800x600,我通常会进行缩放,因此这方面的最小值只是该计算的产物,或者是480000像素的总和。配备800万像素照相/摄像机的手机将生成800万像素的原始图像,你明白我的意思吗?我不确定,但我知道每个(红、绿、蓝)像素参考的内存消耗是分辨率的3倍。希望有帮助。(^)顺便说一句,虚拟机的最大内存分配是16Mb。因此,在800万像素的情况下,您肯定会超过虚拟机预算!绕过预算的唯一方法是使用NDK构建本机应用程序。这是正确的,在公认的答案中有更全面的解释。谢谢你的帮助。