Android从相机获取URI [英] Android getting URI from Camera
问题描述
在Nexus 5上使用相机拍摄照片时遇到问题.我可以从图库中毫无问题地拍摄照片并将其保存到后端.但是,当我尝试使用相机拍照时,在拍照并选择勾号后,应用程序崩溃了... 我可以看到传递给onActivityResult()的意图是空的,但是我不知道如何解决这个问题.有人可以帮忙吗?谢谢.
I am having an issue when taking a photo with the camera on my Nexus 5. I can take photos from the gallery no problem and save them to the back end. However when I try to take a picture using the camera, after I take the picture and select the tick, then the app crashes... I can see that the intent passed to the onActivityResult() is empty but I don't know how to fix this. Can anyone help? thanks.
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import com.khackett.runmate.R;
import com.khackett.runmate.utils.FileHelper;
import com.khackett.runmate.utils.ParseConstants;
import com.parse.ParseException;
import com.parse.ParseFile;
import com.parse.ParseUser;
import com.parse.SaveCallback;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class MyProfileFragment extends Fragment implements View.OnClickListener {
public static final String TAG = MyProfileFragment.class.getSimpleName();
protected Button mTakePicture;
protected Button mChoosePicture;
/**
* Default constructor
*/
public MyProfileFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_my_profile, container, false);
mTakePicture = (Button) rootView.findViewById(R.id.takePictureButton);
mChoosePicture = (Button) rootView.findViewById(R.id.choosePictureButton);
mTakePicture.setOnClickListener(this);
mChoosePicture.setOnClickListener(this);
return rootView;
}
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
// Switch statement to select which action to take depending on button/text pressed
switch (v.getId()) {
case R.id.takePictureButton:
takeCameraPicture();
break;
case R.id.choosePictureButton:
chooseGalleryPicture();
break;
default:
System.out.println("Problem with input");
}
}
public static final int TAKE_PHOTO_REQUEST = 1888;
public static final int PICK_PHOTO_REQUEST = 1888;
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 1888;
// member variable to store the media type as a URI, that can be stored in multiple places
// Uri = uniform resource identifier
protected Uri mMediaUri;
public void takeCameraPicture() {
// Take picture
// use an existing camera app on the phone by starting an intent
// declare intent to capture a photo using whatever camera app is available
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// after invoking the camera,
mMediaUri = getOutputMediaFileUri();
// check that a null value is not returned
if (mMediaUri == null) {
// display an error
Toast.makeText(getActivity(), R.string.error_external_storage, Toast.LENGTH_LONG).show();
} else {
// to add extra data to an intent, use the putExtra() method
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mMediaUri);
// start an activity for a result so that the activity exits and returns a result back for us
// ie, the main activity will wait for the result
startActivityForResult(takePhotoIntent, TAKE_PHOTO_REQUEST);
}
}
public void chooseGalleryPicture() {
// Choose picture
Intent choosePhotoIntent = new Intent(Intent.ACTION_GET_CONTENT);
// need to specify which type of action we want to get - an image in this case
choosePhotoIntent.setType("image/*");
startActivityForResult(choosePhotoIntent, PICK_PHOTO_REQUEST);
}
private Uri getOutputMediaFileUri() {
// To be safe, you should check that the SD card / external storage is mounted
// using Environment.getExternalStorageState() before doing this.
// see method below...
if (isExternalStorageAvailable()) {
String appName = MyProfileFragment.this.getString(R.string.app_name);
// get the Uri
// Get the external storage directory - we want to return a file object
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appName);
// Create our subdirectory
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdir()) {
Log.e(TAG, "Failed to create directory");
return null;
}
}
// Create a file to hold the image
File mediaFile;
// get the current date and time
Date now = new Date();
// convert the date and time into a String datetimestamp
// see http://developer.android.com/guide/topics/media/camera.html#saving-media for the methods used
// need to append a timestamp to make it unique - otherwise it will overwrite the previous photo
String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(now);
String path = mediaStorageDir.getPath() + File.separator;
// create a new file using the constructor that takes a name
mediaFile = new File(path + "IMG_" + timestamp + ".jpg");
Log.d(TAG, "File: " + Uri.fromFile(mediaFile));
// Return the files URI
Log.d(TAG, "Returning the files URI");
return Uri.fromFile(mediaFile);
} else {
return null;
}
}
/**
* check if external storage is available on the users device
*
* @return
*/
private boolean isExternalStorageAvailable() {
// find out what state external storage is in
String state = Environment.getExternalStorageState();
// if external storage is available, return true,
if (state.equals(Environment.MEDIA_MOUNTED)) {
return true;
} else {
return false;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (data == null) {
Log.d(TAG, "Data is null");
Toast.makeText(getActivity(), getString(R.string.general_error), Toast.LENGTH_LONG).show();
} else {
Log.d(TAG, "Data: " + data);
// the intent has data, so set the media uri
Log.d(TAG, "adding the data using the getData() method");
mMediaUri = data.getData();
Log.d(TAG, "Media Uri: " + mMediaUri);
}
// ParseUser.getCurrentUser().put("profilePic", mMediaUri);
saveImageToParse();
}
} else if (resultCode != Activity.RESULT_CANCELED) {
Log.d(TAG, "Problem getting the picture from gallery");
// Toast.makeText(getActivity(), R.string.general_error, Toast.LENGTH_LONG).show();
}
}
public void saveImageToParse() {
Log.d(TAG, "entering saveImageToParse() method");
byte[] fileBytes = FileHelper.getByteArrayFromFile(getActivity(), mMediaUri);
fileBytes = FileHelper.reduceImageForUpload(fileBytes);
String fileName = FileHelper.getFileName(getActivity(), mMediaUri, "file");
ParseFile file = new ParseFile(fileName, fileBytes);
ParseUser.getCurrentUser().put("profilePic", file);
ParseUser.getCurrentUser().saveInBackground(new SaveCallback() {
public void done(ParseException e) {
if (e == null) {
Log.d(TAG, "Image saved successfully");
// myObjectSavedSuccessfully();
} else {
Log.d(TAG, "Image not saved");
// myObjectSaveDidNotSucceed();
}
}
});
}
.
package com.khackett.runmate.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
// https://github.com/treehouse/treehouse_android_utilities
public class FileHelper {
public static final String TAG = FileHelper.class.getSimpleName();
public static final int SHORT_SIDE_TARGET = 1280;
public static byte[] getByteArrayFromFile(Context context, Uri uri) {
byte[] fileBytes = null;
InputStream inStream = null;
ByteArrayOutputStream outStream = null;
Log.d(TAG, "in the getByteArrayFromFile() method");
if (uri.getScheme() != null && uri.getScheme().equals("content")) {
try {
Log.d(TAG, "entering try to set inputstream");
inStream = context.getContentResolver().openInputStream(uri);
outStream = new ByteArrayOutputStream();
byte[] bytesFromFile = new byte[1024 * 1024]; // buffer size (1 MB)
int bytesRead = inStream.read(bytesFromFile);
while (bytesRead != -1) {
outStream.write(bytesFromFile, 0, bytesRead);
bytesRead = inStream.read(bytesFromFile);
}
fileBytes = outStream.toByteArray();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} finally {
try {
inStream.close();
outStream.close();
} catch (IOException e) { /*( Intentionally blank */ }
}
} else {
try {
File file = new File(uri.getPath());
FileInputStream fileInput = new FileInputStream(file);
fileBytes = IOUtils.toByteArray(fileInput);
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
return fileBytes;
}
public static byte[] reduceImageForUpload(byte[] imageData) {
Bitmap bitmap = ImageResizer.resizeImageMaintainAspectRatio(imageData, SHORT_SIDE_TARGET);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
byte[] reducedData = outputStream.toByteArray();
try {
outputStream.close();
} catch (IOException e) {
// Intentionally blank
}
return reducedData;
}
public static String getFileName(Context context, Uri uri, String fileType) {
String fileName = "uploaded_file.";
if (fileType.equals(ParseConstants.TYPE_IMAGE)) {
fileName += "png";
} else {
// For video, we want to get the actual file extension
if (uri.getScheme().equals("content")) {
// do it using the mime type
String mimeType = context.getContentResolver().getType(uri);
int slashIndex = mimeType.indexOf("/");
String fileExtension = mimeType.substring(slashIndex + 1);
fileName += fileExtension;
} else {
fileName = uri.getLastPathSegment();
}
}
return fileName;
}
}
.
09-01 20:15:49.322 23271-23271/com.khackett.runmate E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.khackett.runmate, PID: 23271
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=67424, result=-1, data=Intent { }} to activity {com.khackett.runmate/com.khackett.runmate.ui.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getScheme()' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:3574)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3617)
at android.app.ActivityThread.access$1300(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1352)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.getScheme()' on a null object reference
at com.khackett.runmate.utils.FileHelper.getByteArrayFromFile(FileHelper.java:29)
at com.khackett.runmate.ui.MyProfileFragment.saveImageToParse(MyProfileFragment.java:233)
at com.khackett.runmate.ui.MyProfileFragment.onActivityResult(MyProfileFragment.java:210)
at android.support.v4.app.FragmentActivity.onActivityResult(FragmentActivity.java:165)
at android.app.Activity.dispatchActivityResult(Activity.java:6192)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3570)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3617)
at android.app.ActivityThread.access$1300(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1352)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
推荐答案
从日志和您的代码中,您可以跟踪到mMediaUri
为空.
From the log, and your code, you can track that it is mMediaUri
being null.
这应该是由于屏幕旋转而导致的活动重新创建引起的,例如,如果您的应用程序通常以纵向运行,而相机强制为横向运行,或者您在使用相机时旋转了.
This should be caused by activity recreate because of screen rotation, for example, if your app usually run in Portrait, while the camera force to Landscape or you rotated when you use camera.
无论如何要解决此问题,您都必须保存mMediaUrl
并将其还原到onCreate
中,像这样修改您的片段:
Anyway to solve this, you have to save the mMediaUrl
and restore it in onCreate
, modify your Fragment like this:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current state
savedInstanceState.putString("media_url", mMediaUrl.toString());
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mMediaUrl = Uri.parse(savedInstanceState.getString("media_url"));
}
}
注意到另一个问题,请更改以下内容:
noticed another problem, change the followings:
public static final int TAKE_PHOTO_REQUEST = 1889;
public static final int PICK_PHOTO_REQUEST = 1888;
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == PICK_PHOTO_REQUEST) {
if (data == null) {
Log.d(TAG, "Data is null");
Toast.makeText(getActivity(), getString(R.string.general_error), Toast.LENGTH_LONG).show();
} else {
Log.d(TAG, "Data: " + data);
// the intent has data, so set the media uri
Log.d(TAG, "adding the data using the getData() method");
mMediaUri = data.getData();
Log.d(TAG, "Media Uri: " + mMediaUri);
}
// ParseUser.getCurrentUser().put("profilePic", mMediaUri);
saveImageToParse();
} else if(requestCode == TAKE_PHOTO_REQUEST) {
saveImageToParse();
}
} else if (resultCode != Activity.RESULT_CANCELED) {
Log.d(TAG, "Problem getting the picture from gallery");
// Toast.makeText(getActivity(), R.string.general_error, Toast.LENGTH_LONG).show();
}
}
这篇关于Android从相机获取URI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!