hiển thị bên ngoài ứng dụng thông qua ClipData.Item.getUri


99

Tôi đang cố gắng khắc phục sự cố sau khi tính năng mới được thêm vào hệ thống tệp Android nhưng tôi gặp lỗi này:

android.os.FileUriExposedException: file:///storage/emulated/0/MyApp/Camera_20180105_172234.jpg exposed beyond app through ClipData.Item.getUri()

Vì vậy, tôi hy vọng ai đó có thể giúp tôi sửa lỗi này :)

Cảm ơn

private Uri getTempUri() {
    // Create an image file name
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
    String dt = sdf.format(new Date());
    imageFile = null;
    imageFile = new File(Environment.getExternalStorageDirectory()
            + "/MyApp/", "Camera_" + dt + ".jpg");
    AppLog.Log(
            TAG,
            "New Camera Image Path:- "
                    + Environment.getExternalStorageDirectory()
                    + "/MyApp/" + "Camera_" + dt + ".jpg");
    File file = new File(Environment.getExternalStorageDirectory() + "/MyApp");
    if (!file.exists()) {
        file.mkdir();
    }
    imagePath = Environment.getExternalStorageDirectory() + "/MyApp/"
            + "Camera_" + dt + ".jpg";
    imageUri = Uri.fromFile(imageFile);
    return imageUri;
}

Câu trả lời:


166

Đối với sdk 24 trở lên, nếu bạn cần lấy Uri của tệp bên ngoài bộ nhớ ứng dụng của mình, bạn sẽ gặp lỗi này.
@ eranda.del giải pháp cho phép bạn thay đổi chính sách để cho phép điều này và nó hoạt động tốt.

Tuy nhiên, nếu bạn muốn làm theo hướng dẫn của google mà không cần phải thay đổi chính sách API của ứng dụng của mình, bạn phải sử dụng FileProvider.

Đầu tiên để lấy URI của tệp, bạn cần sử dụng phương thức FileProvider.getUriForFile ():

Uri imageUri = FileProvider.getUriForFile(
            MainActivity.this,
            "com.example.homefolder.example.provider", //(use your app signature + ".provider" )
            imageFile);

Sau đó, bạn cần phải định cấu hình nhà cung cấp của mình trong tệp kê khai android:

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

(Trong "quyền hạn" sử dụng cùng giá trị với đối số thứ hai của phương thức getUriForFile () (chữ ký ứng dụng + ".provider"))

Và cuối cùng bạn cần tạo tệp ressources: "file_paths". Tệp này cần được tạo trong thư mục res / xml (có thể bạn cũng cần tạo thư mục này):

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="." />
</paths>

78
"Làm cho cuộc sống của nhà phát triển dễ dàng hơn" - đây là khẩu hiệu cho mọi API mới, phải không Google?
Kirill Karmazin

12
Giải pháp được cung cấp bởi Brendon là giải pháp chính xác nhưng nó không sẵn sàng sao chép-dán, bạn cần kiểm tra xem nó có phù hợp với nhu cầu của bạn không, đối với người yêu cũ. <external-path ...được sử dụng khi bạn muốn Environment.getExternalStorageDirectory (). và sử dụng <files-path ...nếu bạn cần Context.getFilesDir (). Bạn tốt hơn kiểm tra tài liệu chính thức đầu tiên: developer.android.com/reference/android/support/v4/content/...
Kirill Karmazin

9
Xin nhắc lại nếu bạn đang sử dụng androidx (không gian tên hỗ trợ mới), bạn có thể tìm thấy tại đây ( developer.android.com/reference/androidx/core/content/… ) tài liệu thích hợp.
Rodrirokr

19
Lưu ý rằng trong androidxđó là:android:name="androidx.core.content.FileProvider"
smörkex

150

Thêm khối mã sau trước khi bắt đầu máy ảnh hoặc duyệt tệp

    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());

Vui lòng tham khảo chế độ nghiêm ngặt liên kết giới thiệu và giải thích tất cả các chi tiết kỹ thuật và cách sử dụng của nó.


24
Đây không phải là cách đúng đắn để giải quyết vấn đề này, điều này về cơ bản sẽ loại bỏ các chính sách chế độ nghiêm ngặt. và sẽ bỏ qua cảnh báo an ninh
Ahsan Zaheer

Có một số ưu và nhược điểm về điều này. Vui lòng tham khảo phần sau để có thêm thông tin stackoverflow.com/questions/18100161/…
eranda.del,

Nó hoạt động, nếu có bất kỳ trận hòa nào trở lại thì hãy cho tôi biết :)
Hanan

Mã đã giải quyết ngoại lệ cho tôi. Tuy nhiên, bình luận hàng đầu ở đây cho biết cách mã không phải là cách chính xác để giải quyết vấn đề này. Có ai biết phương pháp thích hợp sẽ là gì sau đó không?
Shn_Android_Dev

Google ghét lập trình viên.
Joubert Vasconcelos

0

Không nên cho phép cấu hình nhà cung cấp bắt buộc vào AndroidManifest mà chỉ cho phép Máy ảnh và Bộ nhớ. Sử dụng mã sau để khởi động máy ảnh:

final int REQUEST_ACTION_CAMERA = 9;
void openCamra() {
Intent cameraImgIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

cameraImgIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID +".provider",
                new File("your_file_name_with_dir")));
startActivityForResult(cameraImgIntent, REQUEST_ACTION_CAMERA);
}

Sau khi chụp ảnh, bạn có thể tìm thấy ảnh đã chụp của mình trong:

"your_file_name_with_dir"

Giải thích cấu hình của nhà cung cấp trong AndroidManifest sẽ tốt hơn là đưa ra mã khác.
fury.slay
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.