android.os.FileUriExposedException: dosya: ///storage/emulated/0/test.txt Intent.getData () aracılığıyla uygulamanın ötesinde maruz


738

Bir dosya açmaya çalıştığımda uygulama kilitleniyor. Android Nougat'ın altında çalışır, ancak Android Nougat'ta çöker. Yalnızca sistem bölümünden değil, SD karttan bir dosya açmaya çalıştığımda çöküyor. İzin sorunu mu var?

Basit kod:

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

Log:

android.os.FileUriExposedException: dosya: ///storage/emulated/0/test.txt Intent.getData () aracılığıyla uygulamanın ötesinde maruz

Düzenle:

Android Nougat'ı hedeflerken, file://URI'lara artık izin verilmiyor. Biz kullanmalıdır content://URI'lerini yerine. Ancak, uygulamamın kök dizinlerdeki dosyaları açması gerekiyor. Herhangi bir fikir?


20
Bunun, uygulama geliştiricileri için hayatı gereksiz yere zorlaştıran bir hata olduğunu hissediyorum. Her bir uygulama ile bir "FileProvider" ve "yetki" toplamanız, Enterprisey ortak metnine benziyor. Her dosya amacına bir bayrak eklemek zorunda kalmak garip ve gereksiz olabilir. Zarif "yollar" kavramını kırmak tatsızdır. Ve faydası nedir? Seçmeli olarak uygulamalara depolama erişimi verme (çoğu uygulama tam sdcard erişimine sahipken, özellikle dosyalarda çalışanlar)?
nyanpasu64

2
bunu deneyin, küçük ve mükemmel kod stackoverflow.com/a/52695444/4997704
Binesh Kumar

Yanıtlar:


1316

Eğer targetSdkVersion >= 24öyleyse, o zaman FileProviderdiğer uygulamalar için erişilebilir hale getirmek için belirli dosya veya klasöre erişim vermek için sınıfı kullanmamız gerekir. FileProviderFileProvider'ımızın burada açıklandığı gibi içe aktarılan bağımlılıklarda bildirilen FileProviders ile çakışmadığından emin olmak için kendi sınıfımızı devralıyoruz .

URI'yi file://URI ile değiştirme adımları content://:

  • Sınıf genişletme ekle FileProvider

    public class GenericFileProvider extends FileProvider {}
  • <provider>Etiketin AndroidManifest.xmlaltına bir FileProvider etiketi ekleyin <application>. android:authoritiesÇakışmalardan kaçınmak için öznitelik için benzersiz bir yetki belirtin , içe aktarılan bağımlılıklar belirleyebilir ${applicationId}.providerve diğer yaygın olarak kullanılan yetkililer.

<?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>
  • Sonra klasörde bir provider_paths.xmldosya oluşturun res/xml. Klasör yoksa oluşturmak için gerekli olabilir. Dosyanın içeriği aşağıda gösterilmiştir. Kök klasöründeki Harici Depolama'ya erişimi external_files(path=".") adıyla paylaşmak istediğimizi açıklar .
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • Son adım, aşağıdaki kod satırını

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

    için

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
  • Düzenleme: Sistemi dosyanızı açmak için bir niyet kullanıyorsanız, aşağıdaki kod satırını eklemeniz gerekebilir:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Lütfen buraya tam kod ve çözüm açıklanmıştır .


62
Sadece intent.setFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION) eklemem gerekiyordu;
alorma

24
Tüm Android sürümlerinde mi yoksa yalnızca API 24'te mi çalışır?
android geliştirici


11
@rockhammer Bunu Android 5.0, 6.0, 7.1 ve 8.1 ile test ettim, her durumda çalışır. Yani (Build.VERSION.SDK_INT > M)durum işe yaramaz.
Sébastien

66
FileProvideryalnızca varsayılan davranışlardan herhangi birini geçersiz kılmak istiyorsanız genişletilmelidir, aksi takdirde kullanın android:name="android.support.v4.content.FileProvider". Bkz. Developer.android.com/reference/android/support/v4/content/…
JP Ventura

313

Çözümünün yanı sıra, bu sorunu çözmek FileProvideriçin başka bir yol var. Basit ifadeyle

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

içinde Application.onCreate(). Bu şekilde VM, dosyanın URIpozlamasını yok sayar .

Yöntem

builder.detectFileUriExposure()

VmPolicy'i ayarlamazsak varsayılan davranış olan dosya pozlama kontrolünü etkinleştirir.

Bir content:// URIşey göndermek için a kullanırsam bazı uygulamaların anlayamadığı bir sorunla karşılaştım . Ve target SDKsürümün düşürülmesine izin verilmez. Bu durumda benim çözümüm faydalıdır.

Güncelleme:

Yorumda belirtildiği gibi, StrictMode tanı aracıdır ve bu sorun için kullanılması gerekmez. Bu yanıtı bir yıl önce yayınladığımda, birçok uygulama yalnızca Dosya urislerini alabilir. Onlara bir FileProvider uri göndermeye çalıştığımda çöküyorlar. Bu, çoğu uygulamada düzeltildi, bu yüzden FileProvider çözümüne gitmeliyiz.


1
@LaurynasG API 18'den 23'e kadar, android varsayılan olarak dosya uri pozlamasını kontrol etmez. Bu yöntemin çağrılması bu kontrolü etkinleştirir. API 24'ten, android bu kontrolü varsayılan olarak yapar. Ancak yeni bir ayar yaparak devre dışı bırakabiliriz VmPolicy.
hqzxzwb

Bunun çalışması için başka bir adım gerekiyor mu? Android 7.0 çalıştıran Moto G'mde olduğu gibi çalışmıyor.
CKP78

3
Bu nasıl bu sorunu çözebilir, ancak StrictMode geliştirici modunda serbest bırakma modunda etkinleştirilmesi gereken bir tanılama araçları ???
Imene Noomene

1
@ImeneNoomene Aslında StrictMode'u burada devre dışı bırakıyoruz. StrictMode'un yayın modunda etkinleştirilmemesi makul görünüyor, ancak aslında Android, hata ayıklama modundan veya yayın modundan bağımsız olarak bazı StrictMode seçeneklerini varsayılan olarak etkinleştiriyor. Ancak şu ya da bu şekilde, bu yanıtın yalnızca bazı hedef uygulamalar içerik urisi almak için hazırlanmadığında bir geçici çözüm olması gerekiyordu. Artık çoğu uygulama içerik urisleri için destek eklediğine göre, FileProvider desenini kullanmalıyız.
hqzxzwb

3
@ImeneNoomene Öfkede tamamen seninleyim. Haklısın, bu bir teşhis aracı, ya da en azından yıllar önce projelerime eklediğimde. Bu süper sinir bozucu! StrictMode.enableDefaults();, yalnızca geliştirme yapılarımda çalıştırdığım bu çökmenin olmasını engeller - bu yüzden şimdi çöken bir üretim uygulamam var, ancak geliştirme sırasında çökmüyor. Temel olarak, burada bir tanılama aracının etkinleştirilmesi ciddi bir sorunu gizler. @ Hqzxzwb bunu anlamama yardımcı olduğun için teşekkürler.
Jon

174

Eğer targetSdkVersiondaha yüksektir 24 , daha sonra FileProvider erişim vermek için kullanılır.

Bir xml dosyası oluşturun (Yol: res \ xml) sağlayıcı_yolları.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>


Bir ekleme Sağlayıcı içinde 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>

Androidx kullanıyorsanız , FileProvider yolu şöyle olmalıdır:

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

ve değiştir

Uri uri = Uri.fromFile(fileImagePath);

için

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

Düzenleme: URI Intenteklerken aşağıdaki satırı eklediğinizden emin olun:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

ve gitmekte fayda var. Umarım yardımcı olur.


2
@MaksimKniazev Hatanızı kısaca açıklayabilir misiniz? Böylece size yardımcı olabilirim.
Pankaj Lilan

1
@PankajLilan, tam olarak ne dedin ben yaptım. Ama pdf'imi diğer uygulamada her açtığımda boş görünüyor (doğru kaydediliyor). XML'i düzenlemem gerekir mi? FLAG_GRANT_READ_URI_PERMISSION'ı zaten ekledim;
Felipe Castilhos

1
Benim hatam, izinleri yanlış niyete ekliyordum. Bu en iyi ve en basit doğru cevaptır. Teşekkür ederim!
Felipe Castilhos

2
Java.lang.IllegalArgumentException istisnasını atar: / Dosya yolum /storage/emulated/0/GSMManage_DCIM/Documents/Device7298file_74.pdf içeren yapılandırılmış kök bulunamadı. yardım edebilir misin lütfen ?
Jagdish Bhavsar

1
Neden bilmiyorum ama hem READ hem de WRITE izinlerini eklemek zorunda kaldım: target.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION) target.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
kutu 8

160

Uygulamanız API 24+ hedefliyorsa ve hala file: // intents kullanmak istiyorsanız / kullanmanız gerekiyorsa, çalışma zamanı denetimini devre dışı bırakmak için hacky yolunu kullanabilirsiniz:

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

Yöntem StrictMode.disableDeathOnFileUriExposuregizlidir ve şu şekilde belgelenmiştir:

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

Sorun, benim app topal değil, daha ziyade orada birçok uygulama tarafından anlaşılmayan content: // intents kullanılarak sakatlanmak istemiyor. Örneğin, mp3 dosyasını content: // scheme ile açmak, file: // scheme ile aynı dosyayı açmaktan çok daha az uygulama sunar. Uygulamamın işlevselliğini sınırlandırarak Google'ın tasarım hataları için ödeme yapmak istemiyorum.

Google, geliştiricilerin içerik şemasını kullanmasını istiyor, ancak sistem bunun için hazırlanmadı, yıllarca uygulamalar "içerik" olmayan Dosyalar kullanıldı, dosyalar düzenlenebilir ve geri kaydedilebilirken, içerik şeması üzerinde sunulan dosyalar olamaz ( onlar?).


3
"içerik düzeni üzerinden sunulan dosyalar olamaz (olabilir mi?)." - içeriğe yazma erişiminiz varsa emin olun. ContentResolverhem vardır openInputStream()ve openOutputStream(). Bunu yapmanın daha az zorlayıcı bir yolu, sadece VM kurallarını kendiniz yapılandırmak ve file Urikuralı etkinleştirmektir .
CommonsWare

1
Kesinlikle. Tüm uygulamanızı oluşturduğunuzda zor bir iştir, ardından tüm kamera yöntemlerinizin kırıldığını hedefledikten sonra öğrenin. Doğru şekilde yapmak için zaman bulana kadar bu benim için çalışıyor.
Matt W

5
Android 7'de çalışıyor. Teşekkürler
Anton Kizema

4
Android 8'de de çalışır, Huawei Nexus 6P'de test edilmiştir.
Gonzalo Ledezma Torres

4
Bunun üretim üzerinde çalıştığını onaylıyorum (500.000'den fazla kullanıcım var), şu anda sürüm 8.1 en yüksek sürüm ve üzerinde çalışıyor.
Eli

90

Bilgisayarınız targetSdkVersion24 veya daha yüksekse, Android 7.0 ve sonraki sürüm cihazlarda değerleri kullanamazsınızfile: UriIntents .

Seçimleriniz:

  1. Damla targetSdkVersion23'e veya düşürebilirsiniz ya

  2. İçeriğinizi dahili depolama alanına koyun, ardından diğer uygulamalarda seçici olarak kullanılabilirFileProvider olmasını sağlamak için kullanın

Örneğin:

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

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

( bu örnek projeden )


Cevap için teşekkürler. Bunu /systembölümdeki dosyalarla kullandığımda ne olur ? Her uygulama kök olmadan bu bölüme erişebilmelidir.
Thomas Vos

2
@ SuperThomasLab: /systemDünyaca okunabilir olan her şeye güvenmem . Olduğu söyleniyor, tahminim hala bu istisnayı göreceksiniz. Sadece düzeni kontrol ediyorlar ve dosyanın gerçekten dünya tarafından okunabilir olup olmadığını belirlemeye çalışmıyorlar. Ancak, FileProviderhizmet etmeyi öğretemeyeceğiniz için size yardımcı olmayacaktır /system. Sen olabilir kardeşim için bir özel strateji oluşturmakStreamProvider veya kendi rulo ContentProvidersoruna geçmiş olsun.
CommonsWare

Hala bunu nasıl çözeceğimi düşünüyorum .. Android N desteği ile güncellediğim uygulama bir kök tarayıcı. Ancak artık kök dizinlerde artık dosya açamıyorsunuz. ( /data, /system), Çünkü bu "iyi değişim".
Thomas Vos

1
targetSdkVersion değerini 23'e düşürmenin en önemli dezavantajları nelerdir? thnx
rommex

2
@rommex: Neyin "en önemli" olarak nitelendirildiğini bilmiyorum. Örneğin, bölünmüş ekran modunda veya serbest biçimli çok pencereli cihazlarda (Chromebook'lar, Samsung DeX) çalışan kullanıcılara uygulamanızın çok pencereli çalışmayabileceği söylenir. Bunun önemli olup olmadığı size bağlıdır.
CommonsWare

47

Öncelikle Android'inize bir sağlayıcı eklemeniz gerekiyor

  <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>

şimdi xml kaynak klasöründe bir dosya oluşturun (android studio kullanıyorsanız file_paths öğesini vurguladıktan sonra Alt + Enter tuşlarına basabilir ve bir xml kaynak seçeneği oluştur'u seçebilirsiniz)

File_paths dosyasında sonraki

<?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>

Bu örnek harici yol içindir, burada daha fazla seçenek için başvuru yapabilirsiniz . Bu, o klasördeki ve alt klasöründeki dosyaları paylaşmanıza olanak tanır.

Şimdi geriye kalan tek şey niyeti şu şekilde yaratmak:

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

DÜZENLEME : Dosya_yollarına sd kartın kök klasörünü ekledim. Bu kodu test ettim ve işe yarıyor.


1
Bunun için teşekkür ederim. Ayrıca, dosya uzantısını almanın daha iyi bir yolu olduğunu bildirmek istiyorum. String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString()); Ayrıca, önce FileProvider'ı okumak için cevap arayan herkese ve burada Android N ve üstü dosya izinleriyle burada neyle uğraştığınızı anlayabilmenizi öneririm . Dahili depolama ile harici depolama arasında ve ayrıca normal dosya yolu ile önbellek yolları için seçenekler vardır.
praneetloke

2
Aşağıdaki istisna alıyordum: java.lang.IllegalArgumentException: Failed to find configured root ...ve işe <files-path path="." name="files_root" />yarayan tek şey yerine xml dosyasında oldu <external-path .... Dosyam dahili depolama birimine kaydedildi.
steliosf

26

@palash k yanıtı doğru ve dahili depolama dosyaları için çalıştı, ancak benim durumumda dosyaları harici depolamadan da açmak istiyorum, sdcard ve usb gibi harici depolamadan dosya açıldığında uygulamam çöktü, ancak sorunu değiştirerek çözmeyi başardım kabul edilen yanıttan sağlayıcı_yolları.xml

provid_paths.xml dosyasını aşağıdaki gibi değiştirin

<?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>

ve java sınıfında (Kabul edilen cevap sadece küçük bir değişiklik olarak değişiklik yok)

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

Bu, harici depolardan dosyalar için kilitlenmeyi düzeltmeme yardımcı oluyor, Umarım bu benimkiyle aynı sorunu olanlara yardımcı olur :)


1
Nerede buldun <root-pathlütfen? İşe yarıyor. <external-path path="Android/data/${applicationId}/" name="files_root" />harici depolama alanından açık dosyalar için hiçbir etkisi olmadı.
t0m

bunu çeşitli arama sonuçlarından buluyorum, tekrar kontrol edeyim ve en kısa zamanda geri
döneyim

ayrıca bahsettiğiniz harici depolama sd kart veya dahili depolama?
Ramz

Yanlışlık için özür dilerim. Android/data/${applicationId}/SDcard demek istedim.
t0m

1
Bunu amaca eklemeniz gerekiyor: intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
s-hunter

26

Çözümüm, Dosya Yolu'nu Uri.fromFile () kullanmak yerine Dize olarak 'Uri.parse' idi.

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

FromFile (), bellek adreslerinin tüm uygulamalara maruz kaldığında güvensiz olabileceğini düşündüğüm bir dosya işaretçisi kullanıyor gibi görünüyor. Ama bir dosya yolu Dize asla kimseye zarar vermez, bu yüzden FileUriExposedException atmadan çalışır.

9 ila 27 API seviyelerinde test edildi! Metin dosyasını başka bir uygulamada düzenlemek üzere başarıyla açar. FileProvider veya Android Destek Kütüphanesi gerektirmez.


Keşke bunu ilk görseydim. Benim için işe yaradığını kanıtlamadım, ancak FileProvider'dan çok daha az hantal.
Dale

Bunun gerçekte neden çalıştığına dair bir not: Sorunu belirleyen Dosya işaretçisi değil, ancak istisnanın yalnızca 'file: //' ile otomatik olarak fromFile ile eklenen, ancak ayrıştırmayla birlikte olmayan bir yolunuz varsa gerçekleşmesi gerçeği .
Xmister

3
Bu istisna almaz, ancak dosyayı ilgili uygulamaya da gönderemez. Yani, benim için çalışmadı.
Serdar Samancıoğlu

1
Diğer uygulamanın dosya sistemi üzerinden harici depolama birimine erişimi olduğunu varsayamayacağınız için bu, Android 10 ve sonraki sürümlerde başarısız olacaktır.
CommonsWare

24

Aşağıdaki kodu onCreate () etkinliğine yapıştırmanız yeterlidir

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

URI'ye maruz kalmayı yoksayar


1
bu çözümlerden biri ama standart değil. Cevapları düşüren Stil insanları yanlıştır, çünkü bu da çalışma çözümü ile çalışma kodudur.
saksham

23

Sadece aşağıdaki kodu aktiviteye yapıştırın onCreate().

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

URI maruziyetini yok sayar.

Mutlu kodlama :-)


1
Bunun dezavantajları nelerdir?
James F

1
Diğer uygulamanın dosya sistemi üzerinden harici depolama birimine erişimi olduğunu varsayamayacağınız için bu, Android 10 ve sonraki sürümlerde başarısız olacaktır.
CommonsWare

18

FileProvider'ı kullanmanın yolu budur. Ancak bu basit çözümü kullanabilirsiniz:

UYARI : Bir sonraki Android sürümünde düzeltilecektir - https://issuetracker.google.com/issues/37122890#comment4

değiştirin:

startActivity(intent);

tarafından

startActivity(Intent.createChooser(intent, "Your title"));

7
Seçici, aynı kontrolü içermesi için yakında Google tarafından yamanacaktır. Bu bir çözüm değil.
Pointer Null

Bu bir çalışır, ancak gelecekteki android sürümlerinde çalışmaz.
Diljeet

13

Yukarıda verilen Palash'ın cevabını kullandım ama biraz eksikti, böyle izin vermek zorunda kaldım

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

11

Aşağıdaki kodu onCreate () etkinliğine yapıştırmanız yeterlidir

StrictMode.VmPolicy.Builder oluşturucu = yeni StrictMode.VmPolicy.Builder (); StrictMode.setVmPolicy (builder.build ());

URI'ye maruz kalmayı yoksayar


Bu, katı mod politikalarını kaldıracaktır. ve güvenlik uyarısını yok sayar. İyi bir çözüm değil.
inspire_coding

Diğer uygulamanın dosya sistemi üzerinden harici depolama alanına erişimi olduğunu varsayamayacağınız için Android 10 ve sonraki sürümlerde de başarısız olacaktır.
CommonsWare

7

bu iki satırı onCreate'a ekle

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

Paylaşma yöntemi

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..."));

Diğer uygulamanın dosya sistemi üzerinden harici depolama birimine erişimi olduğunu varsayamayacağınız için bu, Android 10 ve sonraki sürümlerde başarısız olacaktır.
CommonsWare

7

İşte benim çözümüm:

içinde 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>

içinde 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>

benim parça bir sonraki kodu var:

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

Tek ihtiyacın olan şey bu.

Ayrıca oluşturmanıza gerek yok

public class GenericFileProvider extends FileProvider {}

Android 5.0, 6.0 ve Android 9.0'da test ediyorum ve başarı işi.


Bu çözümü test ettim ve küçük bir değişiklikle iyi çalışıyor: intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.putExtra (Intent.EXTRA_STREAM, myPhotoFileUri) intent.type = "image / png" startActivity (Intent.createChooser (intent, ") ") İle paylaşın) Android 7 ve 8'de fin çalışır
inspire_coding

4

Sunucudan pdf indirmek için servis sınıfınıza aşağıdaki kodu ekleyin. Umarım bu sizin için yararlıdır.

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

Ve evet, bildiriminize izinler ve sağlayıcı eklemeyi unutmayın.

<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>

1
nedir @xml/provider_paths?
adityasnl

1
@Heisenberg lütfen url'den Rahul Upadhyay yayınına bakın: stackoverflow.com/questions/38555301/…
Bhoomika Chauhan

3

Neden bilmiyorum, her şeyi Pkosta ( https://stackoverflow.com/a/38858040 ) ile tam olarak aynı yaptım ama hata almaya devam ettim :

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

Bu konuda saatler harcadım. Suçlu? Kotlin.

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

intentaslında getIntent().addFlagsyeni ilan ettiğim playIntent üzerinde çalışmak yerine ayar yapıyordu.


2

i imageuri yolu kolayca içeri almak bu yöntemi koymak.

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

2
As of Android N, in order to work around this issue, you need to use the FileProvider API

Burada aşağıda belirtildiği gibi 3 ana adım vardır:

Adım 1: Bildirim Girişi

<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>

Adım 2: res / xml / sağlayıcı_yolları.xml XML dosyası oluşturun

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

3. Adım: Kod değişiklikleri

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

1

Bunun oldukça eski bir soru olduğunu biliyorum ama bu cevap gelecekteki izleyiciler için. Benzer bir sorunla karşılaştım ve araştırma yaptıktan sonra bu yaklaşıma bir alternatif buldum.

Niyetiniz burada örnektir: Kotlin'deki yolunuzdan görüntünüzü görüntülemek için

 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)

Aşağıdaki ana Fonksiyonu

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
        }
    }

Benzer şekilde, bir görüntü yerine, pdf gibi başka bir dosya biçimini kullanabilirsiniz ve benim durumumda, iyi çalıştı


0

Neredeyse bu günü neden bu istisnayı aldığımı anlamaya çalışarak geçirdim. Birçok mücadeleden sonra, bu yapılandırma mükemmel çalıştı ( 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>

Niyetin kendisi

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
}

Burada tüm süreci açıklıyorum .


-1

https://stackoverflow.com/a/38858040/395097 bu cevap tamamlandı.

Bu cevap şunun için - zaten 24'ün altında hedefleyen bir uygulamanız var ve şimdi targetSDKVersion> = 24 sürümüne geçiyorsunuz.

Android N'de, yalnızca 3. taraf uygulamasına maruz kalan uri dosyası değiştirilir. (Daha önce kullanma şeklimiz değil). Bu nedenle, yalnızca 3. taraf uygulama ile yolu paylaştığınız yerleri değiştirin (benim durumumda kamera)

Bizim app Kamera uygulamasına uri gönderiyorlardı, bu yerde kamera uygulamasını çekilen görüntüyü saklamak için bekliyoruz.

  1. Android N için dosyaya işaret eden yeni Content: // uri tabanlı url üretiyoruz.
  2. Bunun için her zamanki File api tabanlı yolu üretiyoruz (eski yöntemi kullanarak).

Şimdi aynı dosya için 2 farklı uri var. # 1, Kamera uygulamasıyla paylaşılıyor. Kamera amacı başarılıysa, resme # 2'den erişebiliriz.

Bu yardımcı olur umarım.


1
Burada daha önce gönderilmiş bir cevaba atıfta bulunuyorsunuz, eğer tamamlamanız gerekiyorsa, cevap plz'de yorum yapın.
IgniteCoders

1
@IgniteCoders Mesajda açıkça belirttiğim gibi, cevabım ilgili kullanım durumunu kapsıyor.
Aram

-1

Xamarin.Android

Not: xml klasörünü Kaynaklar altında yaptıktan sonra bile xml / sağlayıcı_paths.xml (.axml) yolu çözümlenemedi (belki Değerler gibi varolan bir konuma yerleştirilebilir , denemedim), bu yüzden başvurdum Şimdilik işe yarıyor. Testler, her uygulama çalışması için yalnızca bir kez çağrılması gerektiğini gösterdi (bu, ana makine VM'sinin çalışma durumunu değiştirmesi mantıklıdır).

Not: xml'in büyük harfle yazılması gerekir, bu nedenle Kaynaklar / Xml / sağlayıcı_yollarıxxml

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

-1

@Posta'ın cevabı bunu yapmanın bir yoludur.

Kullanmanın yanı sıra FileProvider, dosyayı MediaStore(özellikle görüntü ve video dosyaları için) de ekleyebilirsiniz , çünkü MediaStore'daki dosyalara her uygulama için erişilebilir:

MediaStore öncelikle video, ses ve görüntü MIME türlerini hedeflemektedir, ancak Android 3.0'dan (API seviye 11) başlayarak medya olmayan türleri de saklayabilir (daha fazla bilgi için bkz. MediaStore.Files). Dosyalar MediaStore'a scanFile () kullanılarak eklenebilir, ardından paylaşım için uygun bir içerik: // stili Uri sağlanan onScanCompleted () geri çağrısına aktarılır. Sisteme MediaStore'a eklendikten sonra içeriğin cihazdaki herhangi bir uygulama tarafından erişilebilir olduğunu unutmayın.

Örneğin, MediaStore'a şu şekilde bir video dosyası ekleyebilirsiniz:

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

contentUrigibi content://media/external/video/media/183473doğrudan geçirilebilir hangi Intent.putExtra:

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

Bu benim için çalışıyor ve kullanım güçlüklerini koruyor FileProvider.


-1

URI Pozlamasını yok saymasına izin verin ... Oluşturduktan sonra ekleyin

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

Diğer uygulamanın dosya sistemi üzerinden harici depolama birimine erişimi olduğunu varsayamayacağınız için bu, Android 10 ve sonraki sürümlerde başarısız olacaktır.
CommonsWare

Bu bir yapım uygulamasında kullanılmamalıdır.
Jorgesys

-1

Bu çözümü deneyin

BU İZİNLERİ MANİFESTE KOYUN

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

GÖRÜNTÜ YAKALAMAK AMACI

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

ONACTIVITYRESULT'DA YAKALANMIŞ GÖRÜNTÜ ALIN

@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
                }
            } 

GÖRÜNTÜ URI'si ALMA YÖNTEMİ

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

Herkes neden aşağı oy olduğunu söyleyebilir. Bu% 100 çalışma çözümüdür.
Abdul Basit Rishi

Bu size tam resmi değil, yalnızca küçük resmi getirir.
Build3r

-2

Benim durumumda SetDataAndType, sadece yerine koyarak istisnadan kurtuldum SetData.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.