我的应用程序具有允许用户从当前库中选择照片或视频,或者获取新照片或视频,然后附加到电子邮件的功能。它似乎适用于大多数设备和Android版本。但是,在将uri发送到图像或视频以附加到电子邮件时,我发现某些设备设置会间歇性地崩溃。示例崩溃是:Fatal Exception: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=67425, result=-1, data=Intent { dat=content://com.google.android.apps.docs.storage/document/acc=1;doc=3 flg=0x1 }} to activity: java.lang.NullPointerException: Attempt to invoke virtual method 'char[] java.lang.String.toCharArray()' on a null object reference.
这发生在" File file = new File(imagePath);
"下面的email()方法中的行。我可以对我的代码进行哪些更改,以便为此提供更通用的解决方案吗?我的minSdkVersion是16,而我的targetSdkVersion是23.
以下是我的实施代码:
private enum Type {
PHOTO,
VIDEO
}
private enum Source {
LIBRARY,
CAMERA
}
private void select(Type type) {
if(selectedSource == Source.LIBRARY) {
// choose from library
Intent getIntent = new Intent(Intent.ACTION_GET_CONTENT);
if (type == Type.PHOTO) {
getIntent.setType("image/*");
} else {
getIntent.setType("video/*");
}
Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if (type == Type.PHOTO) {
pickIntent.setType("image/*");
} else {
pickIntent.setType("video/*");
}
Intent chooserIntent = Intent.createChooser(getIntent, "Select Photo/Video");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{pickIntent});
startActivityForResult(chooserIntent, CHOOSE_IMAGE_VIDEO_ACTIVITY_REQUEST_CODE);
} else if(selectedSource == Source.CAMERA) {
// take photo/video from camera
// check for camera permission
if ( ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ) {
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//Toast.makeText(getActivity(), "You need to allow camera access", Toast.LENGTH_LONG).show();
return;
} else {
if (getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) {
if ( ContextCompat.checkSelfPermission(getActivity(), READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ) {
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, REQUEST_EXTERNAL_STORAGE);
}
if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(getActivity(), READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
//Toast.makeText(getActivity(), "You need to allow external storage access", Toast.LENGTH_LONG).show();
return;
} else {
if (selectedType == Type.PHOTO) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
File photo = null;
try {
// place where to store camera taken picture
photo = this.createTemporaryFile("photo", ".jpg");
photo.delete();
} catch (Exception e) {
//Log.v(TAG, "Can't create file to take picture!");
Toast.makeText(getActivity(), "Please check SD card! Image shot is impossible!", Toast.LENGTH_LONG).show();
}
mImageUri = Uri.fromFile(photo);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_IMAGE_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(getActivity(), "Unable to access the camera", Toast.LENGTH_LONG).show();
}
} else if (selectedType == Type.VIDEO) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
File video = null;
try {
// place where to store camera taken video
video = this.createTemporaryFile("video", ".mp4");
video.delete();
} catch (Exception e) {
//Log.v(TAG, "Can't create file to take picture!");
Toast.makeText(getActivity(), "Please check SD card! Video is impossible!", Toast.LENGTH_LONG).show();
}
mImageUri = Uri.fromFile(video);
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
startActivityForResult(intent, TAKE_VIDEO_ACTIVITY_REQUEST_CODE);
} else {
Toast.makeText(getActivity(), "Unable to access the camera", Toast.LENGTH_LONG).show();
}
}
}
} else {
Toast.makeText(getActivity(), "No camera available", Toast.LENGTH_LONG).show();
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("REQUEST CAMERA RESULT");
select(selectedType);
} else {
//Permission denied
Toast.makeText(getActivity(), "You need to allow camera access", Toast.LENGTH_LONG).show();
}
return;
}
case REQUEST_EXTERNAL_STORAGE: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
System.out.println("REQUEST EXTERNAL STORAGE RESULT");
select(selectedType);
} else {
//Permission denied
Toast.makeText(getActivity(), "You need to allow external storage access", Toast.LENGTH_LONG).show();
}
return;
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CHOOSE_IMAGE_VIDEO_ACTIVITY_REQUEST_CODE) {
// from image picker
if (resultCode == Activity.RESULT_OK) {
if(data != null) {
//InputStream inputStream = getActivity().getContentResolver().openInputStream(data.getData());
mImageUri = data.getData();
imagePath = getPath(getActivity(), mImageUri);
email();
}
}
} else if(requestCode == TAKE_IMAGE_ACTIVITY_REQUEST_CODE || requestCode == TAKE_VIDEO_ACTIVITY_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
grabImageOrVideoTaken();
}
}
}
private void grabImageOrVideoTaken() {
getActivity().getContentResolver().notifyChange(mImageUri, null);
imagePath = getPath(getActivity(), mImageUri);
email();
}
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private void email() {
String mediaType = "photo";
if(selectedType == Type.VIDEO) {
mediaType = "video";
}
String email = "test@test.com";
Intent intent = new Intent(Intent.ACTION_SEND, Uri.fromParts("mailto", email, null));
intent.putExtra(Intent.EXTRA_SUBJECT, getResources().getString(R.string.app_name) + ": Photo/Video Submission");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
File file = new File(imagePath);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (selectedType == Type.PHOTO) {
intent.setType("image/jpeg");
} else {
intent.setType("video/3gp");
}
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + file.getAbsolutePath()));
startActivity(Intent.createChooser(intent, "Submit Photo/Video"));
}
答案 0 :(得分:0)
我可以对我的代码进行哪些更改,以便为此提供更通用的解决方案吗?
不确定。摆脱getPath()
。
它似乎适用于大多数设备和Android版本。
不是。
正确使用Uri
。 这不是文件。即使在确实指向文件的少数情况下,您也可能无法通过文件系统访问该文件。
您说您将其用于电子邮件附件。如果ACTION_SEND
加EXTRA_STREAM
,使用您获得的Uri
。在FLAG_GRANT_READ_URI_PERMISSION
ACTION_SEND
上添加Intent
,以便将您授予的读取权限发送到ACTION_SEND
处理程序。
如果您使用其他电子邮件,请使用ContentResolver
和openInputStream()
在InputStream
或file
{{1}上获取content
}}。然后,将该流传递给其他代码,或者使用该流制作您自己的本地文件副本(例如,在Uri
中),然后使用该本地副本,在不需要时删除它。 / p>