2015-09-17 20 views
22

Các ngoại lệ là:khi ảnh mất nhận được - java.lang.Throwable: file: // Uri tiếp xúc qua ClipData.Item.getUri()

file:// Uri exposed through ClipData.Item.getUri() 
java.lang.Throwable: file:// Uri exposed through ClipData.Item.getUri() 
    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1618) 
    at android.net.Uri.checkFileUriExposed(Uri.java:2341) 
    at android.content.ClipData.prepareToLeaveProcess(ClipData.java:808) 
    at android.content.Intent.prepareToLeaveProcess(Intent.java:7926) 
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1506) 
    at android.app.Activity.startActivityForResult(Activity.java:3832) 
    at android.app.Activity.startActivityForResult(Activity.java:3783) 
    at android.support.v4.app.FragmentActivity.startActivityFromFragment(Unknown Source) 
    at android.support.v4.app.Fragment.startActivityForResult(Unknown Source) 
    at me.chunyu.ChunyuDoctor.Utility.w.takePhoto(Unknown Source) 
    at me.chunyu.ChunyuDoctor.Dialog.ChoosePhotoDialogFragment.takePhoto(Unknown Source) 
    at me.chunyu.ChunyuDoctor.Dialog.ChoosePhotoDialogFragment.access$000(Unknown Source) 
    at me.chunyu.ChunyuDoctor.Dialog.b.onClick(Unknown Source) 
    at me.chunyu.ChunyuDoctor.Dialog.ChoiceDialogFragment.onClick(Unknown Source) 
    at android.view.View.performClick(View.java:4848) 
    at android.view.View$PerformClick.run(View.java:20270) 
    at android.os.Handler.handleCallback(Handler.java:815) 
    at android.os.Handler.dispatchMessage(Handler.java:104) 
    at android.os.Looper.loop(Looper.java:194) 
    at android.app.ActivityThread.main(ActivityThread.java:5643) 
    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:960) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755) 

Mã của tôi là ở đây:

public static void takePhoto(Fragment fragment, int token, Uri uri) { 
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
    if (uri != null) { 
     intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); 
    } 
    fragment.startActivityForResult(intent, token); 
} 

Tôi đã tìm kiếm các vấn đề và giải pháp tương tự. Và sửa đổi mã như sau:

public static void takePhoto(Fragment fragment, int token, Uri uri) { 
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 
      | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 
    if (uri != null) { 
     intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); 
     intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); 
    } 
    fragment.startActivityForResult(intent, token); 
} 

Nhưng nó cũng không hoạt động.

Tính năng này vui vẻ trên Android 5.1 Khi hoạt động tốt trên Android 4.3. Có ai gặp vấn đề tương tự không? Yêu cầu tạm ứng. Đang chờ trực tuyến ...

Trả lời

15

Tôi đã giải quyết vấn đề này.

Đầu tiên, sự cố này xảy ra vì StrictMode ngăn không cho URI truyền qua với lược đồ file://.

Vì vậy, có hai giải pháp:

  1. Thay đổi StrictMode. Xem similar problemits code. Nhưng đối với các ứng dụng của chúng tôi, việc sửa đổi mã nguồn Android là không thực tế.

  2. Sử dụng lược đồ URI khác thay vì file://. Ví dụ: content:// liên quan đến MediaStore.

Vì vậy, tôi đã chọn phương pháp thứ hai:

private void doTakePhoto() { 
    try { 
     ContentValues values = new ContentValues(1); 
     values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg"); 
     mCameraTempUri = getActivity().getContentResolver() 
       .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); 

     takePhoto(this, RequestCode.REQCODE_TAKE_PHOTO, mCameraTempUri); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

public static void takePhoto(Fragment fragment, int token, Uri uri) { 
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 
     | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 
    if (uri != null) { 
     intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); 
     intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); 
    } 
    fragment.startActivityForResult(intent, token); 
} 

Ngoài ra, có solution khác.

+0

"... đòi hỏi android.permission.WRITE_EXTERNAL_STORAGE, hoặc grantUriPermission()" –

5

Lý do của lỗi này là tệp: // lược đồ uri không được hỗ trợ nhiều hơn vì bảo mật được hiển thị. https://code.google.com/p/android/issues/detail?id=203555

Và chúng tôi không thể sử dụng tệp: // uri nữa sau khi có targetSDK 'N'. https://commonsware.com/blog/2016/03/14/psa-file-scheme-ban-n-developer-preview.html

Vì vậy, câu trả lời là đúng. Bất kỳ ai sử dụng tệp: // đều có thay đổi nội dung: // để cung cấp các loại tệp cục bộ.

+1

Cảm ơn, tôi hạ cấp 'targetSdk' đến ** 23 ** và nó đã bắt đầu làm việc. Ý tôi là 'File.getUriFrom (File file)' – murt

10

Vì vậy, tôi đã thực sự đọc về vấn đề này, và có vẻ như là giải pháp đúng để xử lý này là như sau:

String mCurrentPhotoPath; 

private File createImageFile() throws IOException { 
    // Create an image file name 
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 
    String imageFileName = "JPEG_" + timeStamp + "_"; 
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); 
    File image = File.createTempFile(
     imageFileName, /* prefix */ 
     ".jpg",   /* suffix */ 
     storageDir  /* directory */ 
    ); 

    // Save a file: path for use with ACTION_VIEW intents 
    mCurrentPhotoPath = "file:" + image.getAbsolutePath(); 
    return image; 
} 

static final int REQUEST_TAKE_PHOTO = 1; 

private void dispatchTakePictureIntent() { 
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
    // Ensure that there's a camera activity to handle the intent 
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) { 
     // Create the File where the photo should go 
     File photoFile = null; 
     try { 
      photoFile = createImageFile(); 
     } catch (IOException ex) { 
      // Error occurred while creating the File 
      ... 
     } 
     // Continue only if the File was successfully created 
     if (photoFile != null) { 
      Uri photoURI = FileProvider.getUriForFile(this, 
                "com.example.android.fileprovider", 
                photoFile); 
      takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); 
      startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); 
     } 
    } 
} 

Thông báo có một lưu ý rằng google nói để tạo ra một "nội dung: //" tệp thay vì tài nguyên dựa trên "tệp: //".

Đây là từ google:

Note: We are using getUriForFile(Context, String, File) which returns a content:// URI. For more recent apps targeting Android N and higher, passing a file:// URI across a package boundary causes a FileUriExposedException. Therefore, we now present a more generic way of storing images using a FileProvider.

Ngoài ra, bạn sẽ cần phải thiết lập như sau: Now, you need to configure the FileProvider. In your app's manifest, add a provider to your application:

<application> 
    ... 
    <provider 
     android:name="android.support.v4.content.FileProvider" 
     android:authorities="com.example.android.fileprovider" 
     android:exported="false" 
     android:grantUriPermissions="true"> 
     <meta-data 
      android:name="android.support.FILE_PROVIDER_PATHS" 
      android:resource="@xml/file_paths"></meta-data> 
    </provider> 
    ... 
</application> 

Lưu ý: (Trích từ trang web của google) Make sure that the authorities string matches the second argument to getUriForFile(Context, String, File). In the meta-data section of the provider definition, you can see that the provider expects eligible paths to be configured in a dedicated resource file, res/xml/file_paths.xml. Here is the content required for this particular example:

<?xml version="1.0" encoding="utf-8"?> 
<paths xmlns:android="http://schemas.android.com/apk/res/android"> 
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" /> 
</paths> 

Nếu bạn muốn biết thêm thông tin: đọc lên đây https://developer.android.com/training/camera/photobasics.html

3

Tóm lại: file: // chương trình hiện nay không được phép gắn với Ý định trên targetSdkVersion 24 (Android Nougat)

Bạn phải thay đổi mã của bạn nếu bạn có kế hoạch để hỗ trợ api 24+ hai liên kết: https://developer.android.com/training/camera/photobasics.html https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en

+0

Một điều tốt, nó thực sự hữu ích –

3

Bên cạnh những giải pháp sử dụng các FileProvider, có một cách khác để làm việc xung quanh này. Đơn giản chỉ cần đặt trong phương thức Application.onCreate(). Theo cách này, VM bỏ qua phơi nhiễm URI của tệp.

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); 
StrictMode.setVmPolicy(builder.build()); 
Các vấn đề liên quan