Android相机意图后崩溃

27
我有一个已发布的应用程序,其中一个基本功能是允许用户拍照,并将该照片保存在他们外部存储的特定文件夹中。
似乎一切正常,但我现在收到了两个报告,声称在拍照后,点击“完成”退出相机(并返回到Activity),应用程序会被强制关闭,将用户带回主屏幕。
这种情况发生在三星Nexus S和Galaxy Tab上。下面我贴出了我的代码,以展示我设置意图以及如何处理onActivityResult()中的保存和显示照片。如果您能指导一下可能导致他们点击“Done”退出相机应用后崩溃的原因,那将不胜感激!
再次说明,这在大多数设备上似乎都很正常,但我想知道是否应采取更有效,更通用的方法。谢谢。
我如何触发相机意图
   case ACTION_BAR_CAMERA:

        // numbered image name
        fileName = "image_" + String.valueOf(numImages) + ".jpg";


        output = new File(direct + File.separator + fileName); // create
                                                                    // output
        while (output.exists()) { // while the file exists
            numImages++; // increment number of images
            fileName = "image_" + String.valueOf(numImages) + ".jpg";
            output = new File(outputFolder, fileName);


        }
        camera = new   Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        uriSavedImage = Uri.fromFile(output); // get Uri of the output
        camera.putExtra(MediaStore.EXTRA_OUTPUT, uriSavedImage); //pass in Uri to camera intent
        startActivityForResult(camera, 1);


        break;
    default:
        return super.onHandleActionBarItemClick(item, position);
    }
    return true;
}

我如何设置onActivityResult()?

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // TODO Auto-generated method stub
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK) { // If data was passed successfully

        Bundle extras = data.getExtras();

        //Bundle extras = data.getBundleExtra(MediaStore.EXTRA_OUTPUT);

        /*ad = new AlertDialog.Builder(this).create();
        ad.setIcon(android.R.drawable.ic_menu_camera);
        ad.setTitle("Save Image");
        ad.setMessage("Save This Image To Album?");
        ad.setButton("Ok", this);

        ad.show();*/



        bmp = (Bitmap) extras.get("data"); // Set the bitmap to the bundle
                                            // of data that was just
                                            // received
        image.setImageBitmap(bmp); // Set imageview to image that was
                                    // captured
        image.setScaleType(ScaleType.FIT_XY);


    }

}

你能否在模拟器上复制出你提到的这两个设备之一吗? - EGHDK
开发者控制台中是否有堆栈跟踪?这将非常有帮助。 - nhaarman
你尝试过这个了吗? - MKJParekh
你确定你的代码能够在拍照时处理屏幕方向变化吗? - Gautham
2
在我的情况下,在点击“完成”返回到活动之前,它崩溃了,并且在触发onActivityResult之前。你也遇到过这种情况吗? - yfsx
@yfsx 我也遇到了同样的问题,无论是在旧设备还是Moto E上,在拍摄视频后点击“完成”时都会崩溃。你找到解决方案了吗? - Jayman Jani
9个回答

41

首先让我们明确一点 - 从相机中获取图像数据有两种选择:

1. 通过传递要保存图像的精确位置Uri开始相机。

2. 只需启动相机,不传递任何位置Uri。


1.第一种情况:

通过传递要保存图像的Uri开始相机:

String imageFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/picture.jpg";  
File imageFile = new File(imageFilePath); 
Uri imageFileUri = Uri.fromFile(imageFile); // convert path to Uri

Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 
it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri); 
startActivityForResult(it, CAMERA_RESULT);

在 onActivityResult 中接收图像的方式:

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 

    if (RESULT_OK == resultCode) { 
        iv = (ImageView) findViewById(R.id.ReturnedImageView); 

        // Decode it for real 
        BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
        bmpFactoryOptions.inJustDecodeBounds = false; 

        //imageFilePath image path which you pass with intent 
        Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions); 

        // Display it 
        iv.setImageBitmap(bmp); 
    }    
} 

2 . 在第二种情况下:

如果你想在Intent(data)中接收图像,可以不传递图像Uri来启动相机:

Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); 
startActivityForResult(it, CAMERA_RESULT); 

在onActivityResult接收图像如下:

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 

    if (RESULT_OK == resultCode) { 
        // Get Extra from the intent 
        Bundle extras = data.getExtras(); 
        // Get the returned image from extra 
        Bitmap bmp = (Bitmap) extras.get("data"); 

        iv = (ImageView) findViewById(R.id.ReturnedImageView); 
        iv.setImageBitmap(bmp); 
    } 
} 


*****愉快地编码吧!!!*****


这是Nexus 7上已知的一个漏洞。 - Ajay
2
这是正确的答案,但有人能告诉我如何知道设备是否支持位置URI吗? - Jayman Jani
6
当我使用第二个版本时,我只得到缩略图,但我需要真实的图片。如果我使用第二个选项,我可以获得真实的图片,但它在三星设备上会崩溃... - Leandro Borges Ferreira
我该如何手动模拟我的设备,以便测试这个案例? - Sagar Nayak
@LeandroBorgesFerreira 找到任何解决方案了吗? - iMDroid
第二个选项在某些设备上会崩溃。你必须传递URI。 - Zar E Ahmer

4
在相机按钮点击事件中,您可以尝试这样做:
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT,
                Uri.fromFile(getTempFile(this)));
        startActivityForResult(intent, TAKE_PHOTO_CODE);

        declare TAKE_PHOTO_CODE globally as:
        private static final int TAKE_PHOTO_CODE = 1;

在代码中添加getTempFile函数,该函数将帮助将您所点击的名为myImage.png的图像保存在SD卡上,文件夹名称为您的应用程序包名称。
private File getTempFile(Context context) {
        final File path = new File(Environment.getExternalStorageDirectory(),
                context.getPackageName());
        if (!path.exists()) {
            path.mkdir();
        }
        return new File(path, "myImage.png");
    }

现在在OnActivityResult函数中添加以下内容:
if (requestCode == TAKE_PHOTO_CODE) {
                final File file = getTempFile(this);
                try {
                    Uri uri = Uri.fromFile(file);
                    Bitmap captureBmp = Media.getBitmap(getContentResolver(),
                            uri);
                    image.setImageBitmap(captureBmp);
                    } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

如果出现内存问题,请添加以下代码:
@Override
    protected void onPause() {
        image.setImageURI(null);
        super.onPause();
    }

我希望这能帮助到你。

3
我怀疑有以下3个问题可能导致您的问题:
  1. 某些设备在调用onActivityResult方法时返回null,当您调用 extras.get("data"); 时,这可能会导致NullPointerException。要解决此问题,您需要传递确切的URI位置以告诉相机应用程序您希望将其存储在何处,并在onActivityResult中使用它作为Bitmap检索图像。

  2. 其他一些设备在调用onActivityResult时返回完整大小的位图Bitmap。如果位图太大,则可能会导致OutOfMemmoryError,因此您可能需要以较小的尺寸解码图像,以不占用太多内存堆。这两个链接可以帮助您处理此情况:

    http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

    BitmapFactory OOM driving me nuts

  3. 可能您的活动被GC销毁了,因此您必须使用onSavedInstanceStateonRestoreInstanceState保存您的Activity数据。有关详细信息,请参见此前文章的答案

我不知道您是否已经处理过这些问题。

希望有所帮助:)


1

对于三星设备,请在AndroidManifest.xml文件中添加以下一行:

android:configChanges="orientation|screenSize"

希望这对您有帮助。


1
首先,请确保检查请求代码,您可能会在那里捕获到别人的结果。其次,不要使用意图数据位图 - 如果有什么问题,它很小,但由于您提供了存储捕获图像的路径,因此甚至不应该存在(请参见这里)。因此,在收到您的请求的RESULT_OK时,请将您提供的URL作为输出文件路径进行存储,并从那里读取位图:
    ...
    // save your file uri, not necessarily static
    mUriSavedImage = Uri.fromFile(output);
    startActivityForResult(camera, MY_REQUEST_CODE);
    /* Make a String like "com.myname.MY_REQUEST_CODE" and hash it into int to give it
    a bit of uniqueness */
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == MY_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
             Bitmap bmp = BitmapFactory.decodeFile(mUriSavedImage);
             if (bmp != null) {
                 image.setImageBitmap(bmp); // Set imageview to image that was
                                // captured
                 image.setScaleType(ScaleType.FIT_XY);
             } else {
                 Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show();
             }
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

0

跟随这个链接上的步骤。希望对你有用。

或者

获取图像而不崩溃

MainActivity中编写以下代码

// Storage for camera image URI components 
private final static String CAPTURED_PHOTO_PATH_KEY = "mCurrentPhotoPath";      
private final static String CAPTURED_PHOTO_URI_KEY = "mCapturedImageURI"; 

// Required for camera operations in order to save the image file on resume.    
private String mCurrentPhotoPath = null; 
private Uri mCapturedImageURI = null; 

@Override 
public void onSaveInstanceState(Bundle savedInstanceState) { 
   if (mCurrentPhotoPath != null) {            
     savedInstanceState.putString(CAPTURED_PHOTO_PATH_KEY, mCurrentPhotoPath); 
   } 
  if (mCapturedImageURI != null) {  
     savedInstanceState.putString(CAPTURED_PHOTO_URI_KEY, mCapturedImageURI.toString()); 
  } 
  super.onSaveInstanceState(savedInstanceState); 
} 

@Override 
protected void onRestoreInstanceState(Bundle savedInstanceState) {

   if (savedInstanceState.containsKey(CAPTURED_PHOTO_PATH_KEY)) {
       mCurrentPhotoPath = savedInstanceState.getString(CAPTURED_PHOTO_PATH_KEY); 
   } 
   if (savedInstanceState.containsKey(CAPTURED_PHOTO_URI_KEY)) {
   mCapturedImageURI = Uri.parse(savedInstanceState.getString(CAPTURED_PHOTO_URI_KEY)); 
   } 
   super.onRestoreInstanceState(savedInstanceState); 
}

0

大家好,我知道答案已经给出,但这也是我曾经找到的最简单的解决方案之一。

以下是一个例子,它本身就在困扰设备!

AndroidCameraUtils - 下载该项目并从库项目中包含它,下面是您可以使用的代码片段!

private void setupCameraIntentHelper() {
mCameraIntentHelper = new CameraIntentHelper(this, new CameraIntentHelperCallback() {
    @Override
    public void onPhotoUriFound(Date dateCameraIntentStarted, Uri photoUri, int rotateXDegrees) {
        messageView.setText(getString(R.string.activity_camera_intent_photo_uri_found) + photoUri.toString());

        Bitmap photo = BitmapHelper.readBitmap(CameraIntentActivity.this, photoUri);
        if (photo != null) {
            photo = BitmapHelper.shrinkBitmap(photo, 300, rotateXDegrees);
            ImageView imageView = (ImageView) findViewById(de.ecotastic.android.camerautil.sample.R.id.activity_camera_intent_image_view);
            imageView.setImageBitmap(photo);
        }
    }

    @Override
    public void deletePhotoWithUri(Uri photoUri) {
        BitmapHelper.deleteImageWithUriIfExists(photoUri, CameraIntentActivity.this);
    }

    @Override
    public void onSdCardNotMounted() {
        Toast.makeText(getApplicationContext(), getString(R.string.error_sd_card_not_mounted), Toast.LENGTH_LONG).show();
    }

    @Override
    public void onCanceled() {
        Toast.makeText(getApplicationContext(), getString(R.string.warning_camera_intent_canceled), Toast.LENGTH_LONG).show();
    }

    @Override
    public void onCouldNotTakePhoto() {
        Toast.makeText(getApplicationContext(), getString(R.string.error_could_not_take_photo), Toast.LENGTH_LONG).show();
    }

    @Override
    public void onPhotoUriNotFound() {
        messageView.setText(getString(R.string.activity_camera_intent_photo_uri_not_found));
    }

    @Override
    public void logException(Exception e) {
        Toast.makeText(getApplicationContext(), getString(R.string.error_sth_went_wrong), Toast.LENGTH_LONG).show();
        Log.d(getClass().getName(), e.getMessage());
    }
});
}

@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    mCameraIntentHelper.onSaveInstanceState(savedInstanceState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    mCameraIntentHelper.onRestoreInstanceState(savedInstanceState);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    mCameraIntentHelper.onActivityResult(requestCode, resultCode, intent);
}
}

注意:我尝试了许多相机工具的示例,当然还有其他处理方式,但对于初学者和不太熟悉核心概念的人来说,这个项目会更加舒适。谢谢!


0
<!-- User features are request to enroll due to vivo solution for mobiles-->
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.front" android:required="true" />
<uses-feature
    android:name="android.hardware.camera.autofocus"
    android:required="true"/>
<uses-feature android:name="android.hardware.microphone" />
<uses-feature
    android:name="android.hardware.camera.any"
    android:required="true"/>
<uses-feature
    android:name="android.hardware.camera.flash"
    android:required="true"/>
<!-- end User features are request to enroll due to vivo solution for mobiles-->

-1

我遇到了同样的问题。显然,解决方法是将uriSavedImage设置为静态变量。不确定这是否是最好的方法,但对我来说有效。


不好意思,伙计。我在onActivityResult中似乎遇到了一个“传递活动结果失败”的错误。你有什么想法吗? - Jade Byfield

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