Android 从相机获取 URI

Android getting URI from Camera

我在使用 Nexus 5 上的相机拍照时遇到问题。我可以毫无问题地从图库中拍照并将它们保存到后端。但是,当我尝试使用相机拍照时,在我拍照并打勾 select 之后,应用程序崩溃了...... 我可以看到传递给 onActivityResult() 的意图是空的,但我不知道如何解决这个问题。谁能帮忙?谢谢。

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.access00(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.access00(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 为空。

这应该是由于 activity 由于屏幕旋转而重新创建,例如,如果您的应用通常 运行 纵向模式,而相机强制横向或您在使用相机时旋转。

无论如何要解决这个问题,你必须保存 mMediaUrl 并在 onCreate 中恢复它,像这样修改你的片段:

@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"));
    }
}

编辑:

注意到另一个问题,更改以下内容:

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();
    }
}