简体   繁体   English

Android从相机获取URI

[英]Android getting URI from Camera

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. 在Nexus 5上使用相机拍摄照片时出现问题。我可以从图库中毫无问题地拍摄照片并将其保存到后端。 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. 但是,当我尝试使用相机拍照时,在拍照并选择勾号后,应用程序崩溃了。我可以看到传递给onActivityResult()的意图为空,但我不知道如何解决这个问题。 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)

From the log, and your code, you can track that it is mMediaUri being null. 从日志和代码中,您可以跟踪到mMediaUri为空。

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. 这可能是由于屏幕旋转引起的活动重新创建引起的,例如,如果您的应用程序通常以纵向运行,而相机的力为横向,或者您使用相机时旋转了。

Anyway to solve this, you have to save the mMediaUrl and restore it in onCreate , modify your Fragment like this: 无论如何要解决此问题,您都必须保存mMediaUrl并将其还原到onCreate ,如下所示修改您的Fragment:

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

Edit: 编辑:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM