2015-09-01 121 views
0

我在Nexus 5上使用相機拍照時出現問題。我可以從相冊中拍照並保存到後端。然而,當我嘗試使用相機拍照時,在拍照並選擇了打勾後,應用程序崩潰了...... 我可以看到,傳遞給onActivityResult()的意圖是空的,但我不知道如何解決這個問題。誰能幫忙?謝謝。Android從相機獲取URI

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) 
+0

Uri在'getByteArrayFromFile()中爲空'再次嘗試聲明'mMediaUri'爲公共可能工作 – SAM

+0

Hi @SAM這沒有有什麼區別。仍然收到相同的錯誤 – KvnH

回答

2

從日誌和你的代碼中,你可以跟蹤到它是mMediaUri爲空。

這應該是由於屏幕旋轉造成的活動重建造成的,例如,如果您的應用程序通常在肖像中運行,而相機強制爲橫向,或者您在使用相機時旋轉。

反正來解決這個問題,你必須保存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(); 
    } 
} 
+0

嗨@Derek,謝謝你的回覆。我實施了你的建議,但仍然是同樣的錯誤。 'mMediaUri'被正確保存在'onSaveInstanceState()'中。這個問題似乎在'FileHelper'類中。當調用'uri.getScheme()'時,Uri傳遞給'getByteArrayFromFile'爲null並拋出一個錯誤。你知道這可能是什麼原因嗎? – KvnH

+0

發現了另一個問題,請檢查我的編輯 –

+1

將'mMediaUri'傳遞給'saveImageToParse()'方法時,我仍然遇到問題。試圖從中獲取數據時有一個NPE。我決定通過使用Bitmap bitmapPicture =(bitmap)data.getExtras()。get(「data」)創建一個'Bitmap'來繞過這個問題,並將它發送給Parse.com,而這一切都奏效了。感謝您的幫助。 – KvnH