android.os.FileUriExposedException: file: ///storage/emulated/0/test.txt terbuka di luar aplikasi melalui Intent.getData ()

738

Aplikasi mogok ketika saya mencoba membuka file. Ini bekerja di bawah Android Nougat, tetapi pada Android Nougat macet. Hanya macet ketika saya mencoba membuka file dari kartu SD, bukan dari partisi sistem. Ada masalah izin?

Kode sampel:

File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line

Catatan:

android.os.FileUriExposedException: file: ///storage/emulated/0/test.txt diekspos di luar aplikasi melalui Intent.getData ()

Edit:

Saat menargetkan Android Nougat, file://URI tidak lagi diizinkan. Kita harus menggunakan content://URI saja. Namun, aplikasi saya perlu membuka file di direktori root. Ada ide?

Thomas Vos
sumber
20
Saya merasa ini adalah kesalahan yang membuat hidup tidak perlu sulit bagi pengembang aplikasi. Harus bundel "FileProvider" dan "otoritas" dengan setiap aplikasi, sepertinya Enterprisey boilerplate. Harus menambahkan bendera ke setiap maksud file tampaknya aneh dan mungkin tidak perlu. Melanggar konsep "jalan" yang elegan itu tidak menyenangkan. Dan apa untungnya? Selektif memberikan akses penyimpanan ke aplikasi (sementara sebagian besar aplikasi memiliki akses kartu sd penuh, terutama yang bekerja pada file)?
nyanpasu64
2
coba ini, kode stackoverflow.com/a/52695444/4997704
Binesh Kumar

Jawaban:

1316

Jika Anda targetSdkVersion >= 24, maka kita harus menggunakan FileProviderkelas untuk memberikan akses ke file atau folder tertentu agar dapat diakses oleh aplikasi lain. Kami membuat kelas kami sendiri yang mewarisi FileProvideruntuk memastikan FileProvider kami tidak bertentangan dengan FileProviders yang dideklarasikan dalam dependensi yang diimpor seperti dijelaskan di sini .

Langkah-langkah untuk mengganti file://URI dengan content://URI:

  • Tambahkan perluasan kelas FileProvider

    public class GenericFileProvider extends FileProvider {}
  • Tambahkan <provider>tag FileProvider di AndroidManifest.xmlbawah <application>tag. Tentukan otoritas unik untuk android:authoritiesatribut untuk menghindari konflik, dependensi yang diimpor mungkin menentukan ${applicationId}.providerdan otoritas lain yang umum digunakan.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name=".GenericFileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • Kemudian buat provider_paths.xmlfile di res/xmlfolder. Folder mungkin diperlukan untuk dibuat jika tidak ada. Konten file ditunjukkan di bawah ini. Ini menjelaskan bahwa kami ingin berbagi akses ke Penyimpanan Eksternal di folder root (path=".")dengan nama external_files .
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • Langkah terakhir adalah mengubah baris kode di bawah

    Uri photoURI = Uri.fromFile(createImageFile());

    untuk

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
  • Sunting: Jika Anda menggunakan maksud untuk membuat sistem membuka file Anda, Anda mungkin perlu menambahkan baris kode berikut:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Silakan merujuk, kode lengkap dan solusinya telah dijelaskan di sini.

Pkosta
sumber
62
Saya hanya perlu menambahkan intent.setFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
alorma
24
Apakah ini akan berfungsi untuk semua versi Android, atau hanya dari API 24?
pengembang android
9
artikel ini membantu saya medium.com/@ali.muzaffar/…
AbdulMomen عبدالمؤمن
11
@rockhammer Saya baru saja menguji ini dengan Android 5.0, 6.0, 7.1 dan 8.1, ini berfungsi dalam semua kasus. Jadi (Build.VERSION.SDK_INT > M)kondisinya tidak berguna.
Sébastien
66
FileProviderharus diperluas hanya jika Anda ingin menimpa salah satu perilaku default, jika tidak gunakan android:name="android.support.v4.content.FileProvider". Lihat developer.android.com/reference/android/support/v4/content/…
JP Ventura
313

Selain solusi menggunakan FileProvider, ada cara lain untuk mengatasi ini. Sederhananya

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

di Application.onCreate(). Dengan cara ini VM mengabaikan URIeksposur file .

metode

builder.detectFileUriExposure()

mengaktifkan pemeriksaan eksposur file, yang juga merupakan perilaku default jika kita tidak menyiapkan VmPolicy.

Saya mengalami masalah bahwa jika saya menggunakan content:// URIuntuk mengirim sesuatu, beberapa aplikasi tidak dapat memahaminya. Dan menurunkan target SDKversi tidak diizinkan. Dalam hal ini solusi saya bermanfaat.

Memperbarui:

Seperti disebutkan dalam komentar, StrictMode adalah alat diagnostik, dan tidak seharusnya digunakan untuk masalah ini. Ketika saya memposting jawaban ini setahun yang lalu, banyak aplikasi hanya dapat menerima file uris. Mereka hanya crash ketika saya mencoba mengirim uri FileProvider kepada mereka. Ini sudah diperbaiki di sebagian besar aplikasi sekarang, jadi kita harus pergi dengan solusi FileProvider.

hqzxzwb
sumber
1
@LaurynasG Dari API 18 hingga 23, android tidak memeriksa eksposur file uri secara default. Memanggil metode ini memungkinkan pemeriksaan ini. Dari API 24, android melakukan pemeriksaan ini secara default. Tetapi kita dapat menonaktifkannya dengan mengatur yang baru VmPolicy.
hqzxzwb
Apakah ada langkah lain yang diperlukan agar ini berhasil? Tidak berfungsi karena singkatan dari Moto G saya yang menjalankan Android 7.0.
CKP78
3
Bagaimana ini bisa menyelesaikan masalah ini, StrictMode adalah alat diagnostik yang harus diaktifkan dalam mode pengembang bukan mode rilis ???
Imene Noomene
1
@ImeneNoomene Sebenarnya kami menonaktifkan StrictMode di sini. Tampaknya masuk akal bahwa StrictMode tidak boleh diaktifkan dalam mode rilis, tetapi pada kenyataannya Android mengaktifkan beberapa opsi StrictMode secara default terlepas dari mode debug atau mode rilis. Tetapi dengan satu atau lain cara, jawaban ini hanya dimaksudkan sebagai solusi ketika beberapa aplikasi target tidak siap untuk menerima uris konten. Sekarang sebagian besar aplikasi telah menambahkan dukungan untuk uris konten, kita harus menggunakan pola FileProvider.
hqzxzwb
3
@ImeneNoomene Aku benar-benar bersamamu di kemarahanmu. Anda benar, ini adalah alat diagnostik, atau setidaknya sudah lama ketika saya menambahkannya ke proyek saya. Ini sangat membuat frustrasi! StrictMode.enableDefaults();, yang saya jalankan hanya di build pengembangan saya membuat crash ini tidak terjadi - jadi saya sekarang memiliki aplikasi produksi yang crash tetapi tidak crash ketika dalam pengembangan. Jadi pada dasarnya, mengaktifkan alat diagnostik di sini menyembunyikan masalah serius. Terima kasih @hqzxzwb untuk membantu saya menghilangkan bukti ini.
Jon
174

Jika targetSdkVersionlebih tinggi dari 24 , maka FileProvider digunakan untuk memberikan akses.

Buat file xml (Path: res \ xml) provider_paths.xml

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


Tambahkan Penyedia di AndroidManifest.xml

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

Jika Anda menggunakan Androidx , jalur FileProvider seharusnya:

 android:name="androidx.core.content.FileProvider"

dan ganti

Uri uri = Uri.fromFile(fileImagePath);

untuk

Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);

Sunting: Saat Anda termasuk URI dengan Intentpastikan untuk menambahkan baris di bawah ini:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

dan kamu baik untuk pergi. Semoga ini bisa membantu.

Pankaj Lilan
sumber
2
@MaksimKniazev Bisakah Anda menjelaskan kesalahan Anda secara singkat? Sehingga saya dapat membantu Anda.
Pankaj Lilan
1
@ PankajLilan, saya melakukan persis apa yang Anda katakan. Tetapi setiap kali saya membuka pdf saya di aplikasi lain itu tampak kosong (hemat dengan benar). Haruskah saya mengedit xml? Saya sudah menambahkan FLAG_GRANT_READ_URI_PERMISSION juga;
Felipe Castilhos
1
Kesalahan saya, saya menambahkan izin untuk maksud yang salah. Ini adalah jawaban benar terbaik dan paling sederhana. Terima kasih!
Felipe Castilhos
2
Itu melempar pengecualian java.lang.IllegalArgumentException: Gagal menemukan root yang dikonfigurasi / path file saya adalah /storage/emulated/0/GSMManage_DCIM/Documents/Device7298file_74.pdf. bisakah kamu membantu?
Jagdish Bhavsar
1
Tidak tahu mengapa, tetapi saya harus menambahkan izin BACA dan MENULIS: target.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION) target.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
box
160

Jika aplikasi Anda menargetkan API 24+, dan Anda masih ingin / perlu menggunakan file: // intents, Anda dapat menggunakan cara hacky untuk menonaktifkan pemeriksaan runtime:

if(Build.VERSION.SDK_INT>=24){
   try{
      Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
      m.invoke(null);
   }catch(Exception e){
      e.printStackTrace();
   }
}

Metode StrictMode.disableDeathOnFileUriExposuredisembunyikan dan didokumentasikan sebagai:

/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/

Masalahnya adalah aplikasi saya tidak lumpuh, tetapi tidak ingin dilumpuhkan dengan menggunakan konten: // maksud yang tidak dipahami oleh banyak aplikasi di luar sana. Misalnya, membuka file mp3 dengan konten: // skema menawarkan aplikasi jauh lebih sedikit daripada saat membuka file over yang sama: // skema. Saya tidak ingin membayar kesalahan desain Google dengan membatasi fungsionalitas aplikasi saya.

Google ingin pengembang menggunakan skema konten, tetapi sistem tidak siap untuk ini, selama bertahun-tahun aplikasi dibuat untuk menggunakan File bukan "konten", file dapat diedit dan disimpan kembali, sementara file yang disajikan di atas skema konten tidak dapat (bisa mereka?).

Pointer Null
sumber
3
"Sementara file yang dilayani melalui skema konten tidak dapat (bukan?)." - tentu, jika Anda memiliki akses tulis ke konten. ContentResolvermemiliki keduanya openInputStream()dan openOutputStream(). Cara yang tidak terlalu meretas untuk melakukan ini adalah dengan hanya mengkonfigurasi aturan VM sendiri , dan tidak mengaktifkan file Uriaturan.
CommonsWare
1
Persis. Ini adalah kerja keras ketika Anda membangun seluruh aplikasi Anda, kemudian mencari tahu setelah menargetkan 25 semua metode kamera Anda rusak. Ini bekerja untuk saya sampai saya mendapatkan waktu untuk melakukannya dengan cara yang benar.
Matt W
5
Berfungsi di Android 7. Terima kasih
Anton Kizema
4
Bekerja di Android 8 juga, diuji pada Huawei Nexus 6P.
Gonzalo Ledezma Torres
4
Saya mengkonfirmasi ini bekerja pada produksi (saya memiliki lebih dari 500.000 pengguna), saat ini versi 8.1 adalah versi tertinggi, dan berfungsi di sana.
Eli
90

Jika Anda targetSdkVersion berusia 24 tahun atau lebih, Anda tidak dapat menggunakan file: Urinilai di Intentsperangkat Android 7.0+ .

Pilihan Anda adalah:

  1. Drop Anda targetSdkVersionke 23 atau lebih rendah, atau

  2. Letakkan konten Anda di penyimpanan internal, lalu gunakanFileProvider untuk membuatnya tersedia secara selektif untuk aplikasi lain

Sebagai contoh:

Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));

i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);

(dari proyek sampel ini )

CommonsWare
sumber
Terima kasih atas jawabannya. Apa yang terjadi ketika saya menggunakan ini dengan file di /systempartisi? Setiap aplikasi harus dapat mengakses partisi ini tanpa root.
Thomas Vos
2
@SuperThomasLab: Saya tidak akan mengandalkan semua yang /systembisa dibaca dunia. Yang sedang berkata, saya kira Anda masih akan mendapatkan pengecualian ini. Saya menduga mereka hanya memeriksa skema dan tidak mencoba untuk menentukan apakah file tersebut benar-benar dapat dibaca dunia. Namun, FileProvidertidak akan membantu Anda, karena Anda tidak dapat mengajarinya untuk melayani /system. Anda dapat membuat strategi khusus untuk sayaStreamProvider , atau memutar sendiri ContentProvider, untuk menyelesaikan masalah.
CommonsWare
Masih berpikir bagaimana saya akan menyelesaikan ini .. Aplikasi yang saya perbarui dengan dukungan Android N adalah peramban root. Tetapi sekarang Anda tidak dapat membuka file apa pun lagi di direktori root. ( /data, /system), karena "perubahan yang baik" ini.
Thomas Vos
1
apa kelemahan paling penting untuk menjatuhkan targetSdkVersion ke 23? thnx
rommex
2
@rommex: Saya tidak tahu apa yang memenuhi syarat "paling penting". Misalnya, pengguna yang bekerja dalam mode layar terbagi atau pada perangkat multi-jendela bentuk bebas (Chromebook, Samsung DeX) akan diberi tahu bahwa aplikasi Anda mungkin tidak berfungsi dengan multi-jendela. Apakah itu penting atau tidak, itu terserah Anda.
CommonsWare
47

Pertama, Anda perlu menambahkan penyedia ke AndroidManifest Anda

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

sekarang buat file di folder sumber daya xml (jika menggunakan android studio Anda dapat menekan Alt + Enter setelah menyorot file_paths dan pilih buat opsi sumber daya xml)

Selanjutnya dalam file_paths masukkan file

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path path="Android/data/com.your.package/" name="files_root" />
  <external-path path="." name="external_storage_root" />
</paths>

Contoh ini untuk jalur eksternal yang dapat Anda refere di sini untuk lebih banyak opsi. Ini akan memungkinkan Anda untuk berbagi file yang ada di folder itu dan sub-foldernya.

Sekarang yang tersisa adalah menciptakan maksud sebagai berikut:

    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1);
    String type = mime.getMimeTypeFromExtension(ext);
    try {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.package.fileProvider", newFile);
            intent.setDataAndType(contentUri, type);
        } else {
            intent.setDataAndType(Uri.fromFile(newFile), type);
        }
        startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT);
    } catch (ActivityNotFoundException anfe) {
        Toast.makeText(getContext(), "No activity found to open this attachment.", Toast.LENGTH_LONG).show();
    }

EDIT : Saya menambahkan folder root kartu sd di file_paths. Saya telah menguji kode ini dan itu berhasil.

Karn Patel
sumber
1
Terima kasih untuk ini. Saya juga ingin memberi tahu Anda bahwa ada cara yang lebih baik untuk mendapatkan ekstensi file. String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString()); Juga, saya sarankan siapa pun yang mencari jawaban untuk membaca FileProvider terlebih dahulu dan memahami apa yang Anda hadapi di sini dengan izin file di Android N dan di atas. Ada opsi untuk penyimpanan internal vs penyimpanan eksternal dan juga untuk jalur file biasa vs jalur cache.
praneetloke
2
Saya mendapatkan pengecualian berikut: java.lang.IllegalArgumentException: Failed to find configured root ...dan satu-satunya hal yang berhasil adalah <files-path path="." name="files_root" />pada file xml bukan <external-path .... File saya disimpan di penyimpanan internal.
steliosf
26

@palash k jawaban sudah benar dan berfungsi untuk file penyimpanan internal, tetapi dalam kasus saya, saya ingin membuka file dari penyimpanan eksternal juga, aplikasi saya macet ketika membuka file dari penyimpanan eksternal seperti sdcard dan usb, tetapi saya berhasil menyelesaikan masalah dengan memodifikasi provider_paths.xml dari jawaban yang diterima

ubah provider_paths.xml seperti di bawah ini

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

<external-path path="Android/data/${applicationId}/" name="files_root" />

<root-path
    name="root"
    path="/" />

</paths>

dan di kelas java (Tidak ada perubahan karena jawaban yang diterima hanya edit kecil)

Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)

Ini membantu saya untuk memperbaiki crash untuk file dari penyimpanan eksternal, Semoga ini akan membantu seseorang yang memiliki masalah yang sama dengan saya :)

Ramz
sumber
1
Di mana Anda menemukan tentang <root-pathtolong? Bekerja. <external-path path="Android/data/${applicationId}/" name="files_root" />tidak berpengaruh untuk file terbuka dari penyimpanan eksternal.
t0m
saya menemukan ini dari berbagai hasil pencarian, izinkan saya memeriksa lagi dan segera kembali ke Anda
Ramz
juga penyimpanan eksternal yang Anda sebutkan adalah kartu sd atau penyimpanan internal?
Ramz
Maaf atas ketidakakuratan. Maksud saya Android/data/${applicationId}/di SDcard.
t0m
1
Perlu menambahkan ini ke maksud: intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
s-hunter
26

Solusi saya adalah 'Uri.parse' Path File sebagai String, alih-alih menggunakan Uri.fromFile ().

String storage = Environment.getExternalStorageDirectory().toString() + "/test.txt";
File file = new File(storage);
Uri uri;
if (Build.VERSION.SDK_INT < 24) {
    uri = Uri.fromFile(file);
} else {
    uri = Uri.parse(file.getPath()); // My work-around for new SDKs, doesn't work in Android 10.
}
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setDataAndType(uri, "text/plain");
startActivity(viewFile);

Tampaknya fromFile () menggunakan pointer file, yang saya kira bisa jadi tidak aman ketika alamat memori terpapar ke semua aplikasi. Tapi Sebuah path file String tidak pernah menyakiti siapa pun, jadi itu berfungsi tanpa melempar FileUriExposedException.

Diuji pada level API 9 hingga 27! Berhasil membuka file teks untuk diedit di aplikasi lain. Tidak memerlukan FileProvider, atau Pustaka Dukungan Android sama sekali.

CrazyJ36
sumber
Saya berharap saya melihat ini dulu. Saya tidak membuktikan itu bekerja untuk saya, tetapi jauh lebih tidak praktis daripada FileProvider.
Dale
Catatan mengapa ini benar-benar berfungsi: Ini bukan penunjuk File yang menyebabkan masalah, tetapi fakta bahwa pengecualian hanya terjadi jika Anda memiliki lintasan dengan 'file: //', yang secara otomatis ditambahkan dengan fromFile, tetapi tidak dengan parse .
Xmister
3
Ini tidak mendapatkan pengecualian, tetapi juga tidak dapat mengirim file ke aplikasi terkait. Jadi, tidak berhasil untuk saya.
Serdar Samancıoğlu
1
Ini akan gagal pada Android 10 dan lebih tinggi, karena Anda tidak dapat mengasumsikan bahwa aplikasi lain memiliki akses ke penyimpanan eksternal melalui sistem file.
CommonsWare
24

Cukup tempel kode di bawah ini dalam aktivitas onCreate ()

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

Ini akan mengabaikan paparan URI

Kaushal Sachan
sumber
1
ini adalah salah satu solusi tetapi bukan yang standar. Orang-orang yang masih downvoted jawaban salah karena ini juga bekerja dengan solusi.
saksham
23

Cukup tempel kode di bawah ini dalam aktivitas onCreate().

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

Ini akan mengabaikan paparan URI.

Selamat coding :-)

Ripdaman Singh
sumber
1
Apa kerugiannya?
James F
1
Ini akan gagal pada Android 10 dan lebih tinggi, karena Anda tidak dapat mengasumsikan bahwa aplikasi lain memiliki akses ke penyimpanan eksternal melalui sistem file.
CommonsWare
18

Menggunakan fileProvider adalah caranya. Tetapi Anda dapat menggunakan solusi sederhana ini:

PERINGATAN : Ini akan diperbaiki dalam rilis Android berikutnya - https://issuetracker.google.com/issues/37122890#comment4

menggantikan:

startActivity(intent);

oleh

startActivity(Intent.createChooser(intent, "Your title"));
Simon
sumber
7
Pemilih akan segera ditambal oleh Google untuk berisi cek yang sama. Ini bukan solusi.
Pointer Null
Yang ini berfungsi tetapi tidak akan berfungsi di versi android yang akan datang.
Diljeet
13

Saya menggunakan jawaban Palash yang diberikan di atas tetapi itu agak tidak lengkap, saya harus memberikan izin seperti ini

Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", new File(path));

        List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }else {
        uri = Uri.fromFile(new File(path));
    }

    intent.setDataAndType(uri, "application/vnd.android.package-archive");

    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    startActivity(intent);
Maks
sumber
11

Cukup tempel kode di bawah ini dalam aktivitas onCreate ()

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

Ini akan mengabaikan paparan URI

AnilS
sumber
Ini, ini akan menghapus kebijakan metode ketat. dan akan mengabaikan peringatan keamanan. Bukan solusi yang bagus.
inspire_coding
Ini juga akan gagal pada Android 10 dan lebih tinggi, karena Anda tidak dapat mengasumsikan bahwa aplikasi lain memiliki akses ke penyimpanan eksternal melalui sistem file.
CommonsWare
7

tambahkan dua baris ini di onCreate

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

Metode berbagi

File dir = new File(Environment.getExternalStorageDirectory(), "ColorStory");
File imgFile = new File(dir, "0.png");
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.setType("image/*");
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imgFile));
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(sendIntent, "Share images..."));
DEVSHK
sumber
Ini akan gagal pada Android 10 dan lebih tinggi, karena Anda tidak dapat mengasumsikan bahwa aplikasi lain memiliki akses ke penyimpanan eksternal melalui sistem file.
CommonsWare
7

Di sini solusi saya:

di Manifest.xml

<application
            android:name=".main.MainApp"
            android:allowBackup="true"
            android:icon="@drawable/ic_app"
            android:label="@string/application_name"
            android:logo="@drawable/ic_app_logo"
            android:theme="@style/MainAppBaseTheme">

        <provider
                android:name="androidx.core.content.FileProvider"
                android:authorities="${applicationId}.provider"
                android:exported="false"
                android:grantUriPermissions="true">
            <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/provider_paths"/>
        </provider>

di res / xml / provider_paths.xml

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

dalam fragmen saya, saya memiliki kode berikut:

 Uri myPhotoFileUri = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", myPhotoFile);               
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, myPhotoFileUri);

Тhat itu semua yang kamu butuhkan.

Juga tidak perlu dibuat

public class GenericFileProvider extends FileProvider {}

Saya menguji pada Android 5.0, 6.0 dan Android 9.0 dan ini berhasil.

Alex
sumber
Saya telah menguji solusi ini, dan berfungsi baik dengan perubahan kecil: intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.putExtra (Intent.EXTRA_STREAM, myPhotoFileUri) intent.type = "image / png" startActivity (Intent.createChooser (intent, " Bagikan gambar melalui ")) Ini dapat bekerja pada Android 7 dan 8.
inspire_coding
4

Untuk mengunduh pdf dari server, tambahkan kode di bawah ini dalam kelas layanan Anda. Semoga ini bermanfaat bagi Anda.

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName + ".pdf");
    intent = new Intent(Intent.ACTION_VIEW);
    //Log.e("pathOpen", file.getPath());

    Uri contentUri;
    contentUri = Uri.fromFile(file);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

    if (Build.VERSION.SDK_INT >= 24) {

        Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
        intent.setDataAndType(apkURI, "application/pdf");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    } else {

        intent.setDataAndType(contentUri, "application/pdf");
    }

Dan ya, jangan lupa untuk menambahkan izin dan penyedia di manifes Anda.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths" />
    </provider>

</application>
Bhoomika Chauhan
sumber
1
apa @xml/provider_paths?
adityasnl
1
@Heisenberg silakan merujuk Rahul Upadhyay posting dari url: stackoverflow.com/questions/38555301/…
Bhoomika Chauhan
3

Saya tidak tahu mengapa, saya melakukan semuanya persis sama dengan Pkosta ( https://stackoverflow.com/a/38858040 ) tetapi terus mendapatkan kesalahan:

java.lang.SecurityException: Permission Denial: opening provider redacted from ProcessRecord{redacted} (redacted) that is not exported from uid redacted

Saya menghabiskan waktu berjam-jam untuk masalah ini. Pelakunya? Kotlin.

val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

intentsebenarnya pengaturan getIntent().addFlagsbukannya beroperasi pada playIntent saya yang baru dinyatakan.

nyanpasu64
sumber
2

saya meletakkan metode ini sehingga jalur imageuri dengan mudah mendapatkan konten.

enter code here
public Uri getImageUri(Context context, Bitmap inImage)
{
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    inImage.compress(Bitmap.CompressFormat.PNG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(context.getContentResolver(), 
    inImage, "Title", null);
    return Uri.parse(path);
}
Jitu Batiya
sumber
2
As of Android N, in order to work around this issue, you need to use the FileProvider API

Ada 3 langkah utama di sini seperti yang disebutkan di bawah ini

Langkah 1: Manifest Masuk

<manifest ...>
    <application ...>
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

Langkah 2: Buat file XML res / xml / provider_paths.xml

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

Langkah 3: Perubahan kode

File file = ...;
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Old Approach
    install.setDataAndType(Uri.fromFile(file), mimeType);
// End Old approach
// New Approach
    Uri apkURI = FileProvider.getUriForFile(
                             context, 
                             context.getApplicationContext()
                             .getPackageName() + ".provider", file);
    install.setDataAndType(apkURI, mimeType);
    install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// End New Approach
    context.startActivity(install);
Suraj Bahadur
sumber
1

Saya tahu ini adalah pertanyaan yang cukup lama tetapi jawaban ini untuk pemirsa di masa depan. Jadi saya mengalami masalah yang sama dan setelah meneliti, saya menemukan alternatif untuk pendekatan ini.

Maksud Anda di sini misalnya: Untuk melihat gambar Anda dari jalur Anda di Kotlin

 val intent = Intent()
 intent.setAction(Intent.ACTION_VIEW)
 val file = File(currentUri)
 intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
 val contentURI = getContentUri(context!!, file.absolutePath)
 intent.setDataAndType(contentURI,"image/*")
 startActivity(intent)

Fungsi Utama di bawah ini

private fun getContentUri(context:Context, absPath:String):Uri? {
        val cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            arrayOf<String>(MediaStore.Images.Media._ID),
            MediaStore.Images.Media.DATA + "=? ",
            arrayOf<String>(absPath), null)
        if (cursor != null && cursor.moveToFirst())
        {
            val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID))
            return Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Integer.toString(id))
        }
        else if (!absPath.isEmpty())
        {
            val values = ContentValues()
            values.put(MediaStore.Images.Media.DATA, absPath)
            return context.getContentResolver().insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        }
        else
        {
            return null
        }
    }

Demikian juga, alih-alih gambar, Anda dapat menggunakan format file lain seperti pdf dan dalam kasus saya, itu berfungsi dengan baik


sumber
0

Saya menghabiskan hampir satu hari mencoba mencari tahu mengapa saya mendapatkan pengecualian ini. Setelah banyak perjuangan, konfigurasi ini bekerja dengan sempurna ( Kotlin ):

AndroidManifest.xml

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

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <files-path name="movies_csv_files" path="."/>
</paths>

Niat itu sendiri

fun goToFileIntent(context: Context, file: File): Intent {
    val intent = Intent(Intent.ACTION_VIEW)
    val contentUri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
    val mimeType = context.contentResolver.getType(contentUri)
    intent.setDataAndType(contentUri, mimeType)
    intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION

    return intent
}

Saya jelaskan seluruh proses di sini .

lomza
sumber
-1

https://stackoverflow.com/a/38858040/395097 jawaban ini selesai.

Jawaban ini untuk - Anda sudah memiliki aplikasi yang menargetkan di bawah 24, dan sekarang Anda meningkatkan ke targetSDKVersion> = 24.

Di Android N, hanya file uri yang terpapar ke aplikasi pihak ketiga yang diubah. (Bukan cara kami menggunakannya sebelumnya). Jadi ubah hanya tempat-tempat di mana Anda berbagi jalur dengan aplikasi pihak ke-3 (Kamera dalam kasus saya)

Di aplikasi kami, kami mengirim uri ke aplikasi Kamera, di lokasi itu kami mengharapkan aplikasi kamera untuk menyimpan gambar yang diambil.

  1. Untuk android N, kami menghasilkan Konten baru: // url berbasis uri yang menunjuk ke file.
  2. Kami membuat jalur berbasis File api biasa untuk hal yang sama (menggunakan metode yang lebih lama).

Sekarang kami memiliki 2 uri berbeda untuk file yang sama. # 1 dibagikan dengan aplikasi Kamera. Jika niat kamera berhasil, kita dapat mengakses gambar dari # 2.

Semoga ini membantu.

Aram
sumber
1
Anda mereferensikan jawaban yang sudah diposting di sini, jika Anda perlu melengkapinya, komentari dalam jawaban tlg.
IgniteCoders
1
@IgniteCoders Seperti yang saya jelaskan dalam pesan, jawaban saya mencakup use case yang terkait.
Aram
-1

Xamarin. Android

Catatan: Jalur xml / provider_paths.xml (.axml) tidak dapat diatasi, bahkan setelah membuat folder xml di bawah Sumber Daya (mungkin dapat diletakkan di lokasi yang ada seperti Nilai , tidak mencoba), jadi saya terpaksa ini yang berfungsi untuk saat ini. Pengujian menunjukkan bahwa itu hanya perlu dipanggil sekali per aplikasi dijalankan (yang masuk akal adalah bahwa ia mengubah keadaan operasional VM host).

Catatan: xml perlu dikapitalisasi, jadi Resources / Xml / provider_paths.xml

Java.Lang.ClassLoader cl = _this.Context.ClassLoader;
Java.Lang.Class strictMode = cl.LoadClass("android.os.StrictMode");                
System.IntPtr ptrStrictMode = JNIEnv.FindClass("android/os/StrictMode");
var method = JNIEnv.GetStaticMethodID(ptrStrictMode, "disableDeathOnFileUriExposure", "()V");                
JNIEnv.CallStaticVoidMethod(strictMode.Handle, method);
Sam adalah
sumber
-1

@Pkosta menjawab adalah salah satu cara untuk melakukan ini.

Selain menggunakan FileProvider, Anda juga dapat memasukkan file ke MediaStore(terutama untuk file gambar dan video), karena file di MediaStore dapat diakses oleh setiap aplikasi:

MediaStore terutama ditujukan untuk jenis video, audio dan gambar MIME, namun dimulai dengan Android 3.0 (API level 11) juga dapat menyimpan jenis non-media (lihat MediaStore.Files untuk info lebih lanjut). File dapat dimasukkan ke dalam MediaStore menggunakan scanFile () setelah konten: // style Uri yang cocok untuk dibagikan diteruskan ke callback yang disediakan pada callCompleted (). Perhatikan bahwa setelah ditambahkan ke sistem MediaStore, konten dapat diakses oleh aplikasi apa pun di perangkat.

Misalnya, Anda dapat memasukkan file video ke MediaStore seperti ini:

ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.DATA, videoFilePath);
Uri contentUri = context.getContentResolver().insert(
      MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);

contentUriseperti content://media/external/video/media/183473, yang dapat diteruskan langsung ke Intent.putExtra:

intent.setType("video/*");
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);

Ini bekerja untuk saya, dan menyimpan kerepotan menggunakan FileProvider.

NeoWang
sumber
-1

Biarkan saja mengabaikan URI Exposure ... Tambahkan setelah di buat

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build()); 
Anurag Bhalekar
sumber
Ini akan gagal pada Android 10 dan lebih tinggi, karena Anda tidak dapat mengasumsikan bahwa aplikasi lain memiliki akses ke penyimpanan eksternal melalui sistem file.
CommonsWare
Ini tidak harus digunakan dalam aplikasi producction.
Jorgesys
-1

Coba solusi ini

TETAPKAN IZIN INI DALAM MANIFEST

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.CAMERA" />

Niat UNTUK MENANGKAP GAMBAR

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
                    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
                }

DAPATKAN GAMBAR YANG DITAMBAH DALAM HASIL ONACTIVITY

@Override
            protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                super.onActivityResult(requestCode, resultCode, data);
                if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
                    Bundle extras = data.getExtras();
                    Bitmap imageBitmap = (Bitmap) extras.get("data");
                    // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
                    Uri tempUri = getImageUri(getApplicationContext(), imageBitmap);
                    //DO SOMETHING WITH URI
                }
            } 

METODE UNTUK MENDAPATKAN GAMBAR URI

public Uri getImageUri(Context inContext, Bitmap inImage) {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
        String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
        return Uri.parse(path);
    }
Abdul Basit Rishi
sumber
Adakah yang bisa memberitahu saya mengapa turun suara? ini adalah 100% solusi kerja.
Abdul Basit Rishi
Ini hanya membuat Anda thumbnail bukan gambar penuh.
Build3r
-2

Dalam kasus saya, saya menyingkirkan pengecualian dengan mengganti SetDataAndTypedengan just SetData.

thomiel
sumber