android.os.FileUriExposedException: file: ///storage/emulated/0/test.txt สัมผัสนอกแอพผ่าน Intent.getData ()


738

แอพหยุดทำงานเมื่อฉันพยายามเปิดไฟล์ มันทำงานด้านล่าง Android Nougat แต่ใน Android Nougat มันล้มเหลว มันพังเมื่อฉันพยายามเปิดไฟล์จากการ์ด SD ไม่ใช่จากพาร์ติชันระบบ ปัญหาการอนุญาตบางอย่าง?

รหัสตัวอย่าง:

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

เข้าสู่ระบบ:

android.os.FileUriExposedException: file: ///storage/emulated/0/test.txt สัมผัสนอกแอพผ่าน Intent.getData ()

แก้ไข:

เมื่อกำหนดเป้าหมายเป็น Android Nougat file://จะไม่อนุญาตให้ใช้ URI อีกต่อไป เราควรใช้content://URIs แทน อย่างไรก็ตามแอพของฉันต้องเปิดไฟล์ในไดเรกทอรีราก ความคิดใด ๆ


20
ฉันรู้สึกว่านี่เป็นความผิดพลาดที่ทำให้ชีวิตผู้พัฒนาแอปยากขึ้นโดยไม่จำเป็น ต้องรวม "FileProvider" และ "สิทธิ" กับแต่ละแอปดูเหมือนว่า Enterprisey สำเร็จรูป การเพิ่มการตั้งค่าสถานะลงในไฟล์ทุกไฟล์ดูเหมือนจะไม่สะดวกและอาจไม่จำเป็น การทำลายแนวคิดที่หรูหราของ "เส้นทาง" นั้นไม่เป็นที่พอใจ แล้วประโยชน์คืออะไร เลือกอนุญาตการเข้าถึงที่เก็บข้อมูลไปยังแอป (ในขณะที่แอปส่วนใหญ่มีสิทธิ์เข้าถึง sdcard แบบเต็มโดยเฉพาะอย่างยิ่งแอปที่ทำงานกับไฟล์)
nyanpasu64

2
ลองทำสิ่งนี้stackoverflow.com/a/52695444/4997704
Binesh Kumar

คำตอบ:


1316

หากของคุณtargetSdkVersion >= 24เราต้องใช้FileProviderคลาสเพื่อให้เข้าถึงไฟล์หรือโฟลเดอร์เฉพาะเพื่อให้สามารถเข้าถึงแอพอื่น ๆ ได้ เราสร้างระดับของเราเองสืบทอดFileProviderในการสั่งซื้อเพื่อให้แน่ใจว่า FileProvider ของเราไม่ได้มีความขัดแย้งกับ FileProviders ประกาศในการพึ่งพานำเข้าตามที่อธิบายไว้ที่นี่

ขั้นตอนในการแทนที่file://URI ด้วยcontent://URI:

  • เพิ่มคลาสที่ขยาย FileProvider

    public class GenericFileProvider extends FileProvider {}
  • เพิ่ม FileProvider <provider>แท็กในAndroidManifest.xmlภายใต้<application>แท็ก ระบุสิทธิ์เฉพาะสำหรับandroid:authoritiesแอ็ตทริบิวต์เพื่อหลีกเลี่ยงความขัดแย้งการอ้างอิงที่อิมพอร์ตอาจระบุ${applicationId}.providerและสิทธิใช้งานอื่นที่ใช้กันทั่วไป

<?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>
  • จากนั้นสร้างprovider_paths.xmlไฟล์ในres/xmlโฟลเดอร์ อาจจำเป็นต้องสร้างโฟลเดอร์หากไม่มี เนื้อหาของไฟล์แสดงอยู่ด้านล่าง มันอธิบายว่าเราต้องการที่จะแบ่งปันการเข้าถึงที่จัดเก็บข้อมูลภายนอกโฟลเดอร์ราก(path=".")ที่มีชื่อ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>
  • ขั้นตอนสุดท้ายคือการเปลี่ยนบรรทัดของรหัสด้านล่าง

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

    ถึง

    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
  • แก้ไข:หากคุณใช้ความตั้งใจที่จะทำให้ระบบเปิดไฟล์ของคุณคุณอาจต้องเพิ่มบรรทัดของรหัสต่อไปนี้:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

โปรดอ้างอิงรหัสเต็มและโซลูชันได้รับการอธิบายที่นี่


62
ฉันแค่ต้องการเพิ่มความตั้งใจ setFlags (เจตนา FLAG_GRANT_READ_URI_PERMISSION);
แอลอร์มา

24
มันจะใช้ได้กับ Android ทุกรุ่นหรือเฉพาะจาก API 24
นักพัฒนา android

9
บทความนี้ช่วยฉันmedium.com/@ali.muzaffar/ …
AbdulMomen عبدالمؤمن

11
@ rockhammer ฉันเพิ่งทดสอบกับ Android 5.0, 6.0, 7.1 และ 8.1 มันทำงานได้ในทุกกรณี ดังนั้น(Build.VERSION.SDK_INT > M)สภาพจึงไร้ประโยชน์
Sébastien

66
FileProviderควรจะขยายเฉพาะถ้าคุณต้องการที่จะแทนที่ใด ๆ android:name="android.support.v4.content.FileProvider"ของการทำงานเริ่มต้นมิฉะนั้นใช้ ดูdeveloper.android.com/reference/android/support/v4/content/…
JP Ventura

313

นอกจากวิธีแก้ปัญหาโดยใช้FileProviderแล้วยังมีวิธีอื่นในการแก้ไข เพียงแค่ใส่

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

ในApplication.onCreate(). ด้วยวิธีนี้ VM จะละเว้นการURIเปิดเผยไฟล์

วิธี

builder.detectFileUriExposure()

เปิดใช้งานการตรวจสอบการเปิดรับไฟล์ซึ่งเป็นพฤติกรรมเริ่มต้นถ้าเราไม่ได้ตั้งค่า VmPolicy

ฉันพบปัญหาว่าถ้าฉันใช้ a content:// URIเพื่อส่งบางอย่างแอพบางตัวก็ไม่เข้าใจ และtarget SDKไม่อนุญาตให้ปรับรุ่นรุ่น ในกรณีนี้โซลูชันของฉันมีประโยชน์

ปรับปรุง:

ตามที่ระบุไว้ในความคิดเห็น StrictMode เป็นเครื่องมือวิเคราะห์และไม่ควรใช้สำหรับปัญหานี้ เมื่อฉันโพสต์คำตอบนี้เมื่อหนึ่งปีก่อนหลาย ๆ แอปสามารถรับไฟล์ uris เท่านั้น พวกเขาพังเมื่อฉันพยายามส่ง FileProvider uri ให้พวกเขา ตอนนี้ได้รับการแก้ไขแล้วในแอพส่วนใหญ่ดังนั้นเราควรใช้โซลูชัน FileProvider


1
@LaurynasG จาก API 18 ถึง 23 Android จะไม่ตรวจสอบการเปิดรับไฟล์ uri ตามค่าเริ่มต้น การเรียกวิธีนี้ทำให้สามารถตรวจสอบได้ จาก API 24 Android จะตรวจสอบตามค่าเริ่มต้น VmPolicyแต่เราสามารถปิดการใช้งานโดยการตั้งค่าใหม่
hqzxzwb

มีขั้นตอนอื่นที่จำเป็นเพื่อให้สามารถใช้งานได้หรือไม่ ใช้งานไม่ได้กับ Moto G ที่ใช้ Android 7.0
CKP78

3
วิธีนี้สามารถแก้ปัญหานี้ได้อย่างไร StrictMode เป็นเครื่องมือวินิจฉัยที่ควรเปิดใช้งานในโหมดนักพัฒนาซอฟต์แวร์ที่ไม่ได้เปิดตัวโหมด ???
Imene Noomene

1
@ImeneNoomene จริงๆแล้วเรากำลังปิดการใช้งาน StrictMode ที่นี่ ดูเหมือนว่าไม่ควรเปิดใช้งาน StrictMode ในโหมดการเปิดตัว แต่ในความเป็นจริง Android จะเปิดใช้งานตัวเลือก StrictMode โดยค่าเริ่มต้นโดยไม่คำนึงถึงโหมดดีบักหรือโหมดการเปิดตัว แต่ไม่ทางใดก็ทางหนึ่งคำตอบนี้มีขึ้นเพื่อเป็นการแก้ปัญหาเมื่อแอปเป้าหมายบางตัวไม่ได้เตรียมไว้สำหรับรับเนื้อหา ตอนนี้แอพส่วนใหญ่ได้เพิ่มการรองรับ uris เนื้อหาแล้วเราควรใช้รูปแบบ FileProvider
hqzxzwb

3
@ImeneNoomene ฉันอยู่กับคุณอย่างเต็มที่ในความชั่วร้ายของคุณ คุณถูกต้องนี่คือเครื่องมือวินิจฉัยหรืออย่างน้อยก็เป็นเมื่อนานมาแล้วเมื่อฉันเพิ่มลงในโครงการของฉัน นี่มันช่างน่าหงุดหงิดมาก! StrictMode.enableDefaults();ซึ่งฉันทำงานเฉพาะในการพัฒนาของฉันเท่านั้นไม่ให้เกิดความผิดพลาดนี้ดังนั้นตอนนี้ฉันมีแอปพลิเคชันที่ใช้งานได้ซึ่งล้มเหลว แต่ไม่ล้มเหลวเมื่ออยู่ในการพัฒนา โดยพื้นฐานแล้วการเปิดใช้งานเครื่องมือวิเคราะห์ที่นี่จะซ่อนปัญหาร้ายแรงไว้ ขอบคุณ @hqzxzwb ที่ช่วยฉันเข้าใจอย่างนี้
Jon

174

ถ้าtargetSdkVersionสูงกว่า24แล้วFileProviderจะใช้ในการให้สิทธิ์การเข้าถึง

สร้างไฟล์ 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>


เพิ่มผู้ให้บริการใน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เส้นทาง FileProvider ที่ควรจะเป็น:

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

และแทนที่

Uri uri = Uri.fromFile(fileImagePath);

ถึง

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

แก้ไข:ในขณะที่คุณรวม URI ด้วยIntentให้เพิ่มบรรทัดด้านล่าง:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

และคุณก็พร้อมที่จะไป หวังว่ามันจะช่วย


2
@MaksimKniazev คุณสามารถอธิบายข้อผิดพลาดของคุณโดยย่อ? เพื่อให้ฉันสามารถช่วยคุณ
Pankaj Lilan

1
@PankajLilan ฉันทำสิ่งที่คุณพูด แต่ทุกครั้งที่ฉันเปิดไฟล์ PDF ของฉันในแอปพลิเคชั่นอื่น ๆ จะปรากฏว่าว่างเปล่า (บันทึกอย่างถูกต้อง) ฉันจำเป็นต้องแก้ไข xml หรือไม่ ฉันเพิ่ม FLAG_GRANT_READ_URI_PERMISSION แล้วเช่นกัน
Felipe Castilhos

1
ความผิดพลาดของฉันฉันเพิ่มการอนุญาตไปยังเจตนาที่ไม่ถูกต้อง นี่คือคำตอบที่ดีที่สุดและง่ายที่สุด ขอบคุณ!
Felipe Castilhos

2
มันมีข้อยกเว้น java.lang.IllegalArgumentException: ล้มเหลวในการค้นหารูทที่กำหนดค่าซึ่งมี / พา ธ ไฟล์ของฉันคือ /storage/emulated/0/GSMManage_DCIM/Documents/Device7298file_74.pdf คุณช่วยได้ไหม
Jagdish Bhavsar

1
ไม่ทราบว่าทำไม แต่ฉันต้องเพิ่มทั้งสิทธิ์การอ่านและการเขียน: target.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION) target.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
กล่อง

160

หากแอปของคุณกำหนดเป้าหมาย API 24+ และคุณยังต้องการ / จำเป็นต้องใช้ไฟล์: // intents คุณสามารถใช้วิธีแฮ็กเพื่อปิดใช้งานการตรวจสอบรันไทม์:

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

วิธีการStrictMode.disableDeathOnFileUriExposureถูกซ่อนและบันทึกเป็น:

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

ปัญหาคือแอพของฉันไม่ง่อย แต่ไม่ต้องการที่จะพิการด้วยการใช้เนื้อหา: // intents ที่แอพจำนวนมากไม่เข้าใจ ตัวอย่างเช่นการเปิดไฟล์ mp3 ที่มีเนื้อหา: // scheme ให้แอปน้อยกว่าเมื่อเปิดไฟล์เดียวกันด้วย: // scheme ฉันไม่ต้องการจ่ายสำหรับความผิดพลาดในการออกแบบของ Google โดย จำกัด การทำงานของแอพของฉัน

Google ต้องการให้นักพัฒนาซอฟต์แวร์ใช้รูปแบบเนื้อหา แต่ระบบไม่ได้เตรียมไว้สำหรับสิ่งนี้เป็นเวลาหลายปีที่แอพพลิเคชั่นถูกสร้างขึ้นเพื่อใช้ไฟล์ที่ไม่ "เนื้อหา" สามารถแก้ไขและบันทึกไฟล์กลับได้ในขณะที่ไฟล์ที่แสดง พวกเขา?)


3
"ในขณะที่ไฟล์ที่แสดงในรูปแบบเนื้อหาไม่สามารถเป็นได้ (ทำได้หรือไม่" - แน่นอนถ้าคุณมีสิทธิ์ในการเขียนเนื้อหา ContentResolverมีทั้งและopenInputStream() openOutputStream()วิธีที่ไม่ยุ่งยากในการทำเช่นนี้คือเพียงแค่กำหนดค่ากฎ VM ด้วยตนเองและอย่าเปิดใช้งานfile Uriกฎ
CommonsWare

1
เผง มันเป็นงานที่ยากเมื่อคุณสร้างแอพทั้งหมดของคุณจากนั้นค้นหาหลังจากกำหนดเป้าหมาย 25 วิธีการใช้งานกล้องทั้งหมดของคุณพัง สิ่งนี้ใช้ได้กับฉันจนกว่าฉันจะมีเวลาทำอย่างถูกวิธี
Matt W

5
ทำงานบน Android 7. ขอบคุณ
Anton Kizema

4
ทำงานบน Android 8 ด้วยทดสอบกับ Huawei Nexus 6P
Gonzalo Ledezma Torres

4
ฉันยืนยันว่านี่ใช้งานได้จริง (ฉันมีผู้ใช้มากกว่า 500,000 ราย) ปัจจุบันเวอร์ชัน 8.1 เป็นเวอร์ชันสูงสุดและใช้งานได้
อีไล

90

หากคุณtargetSdkVersionเป็น 24 หรือสูงกว่าคุณไม่สามารถใช้file: Uriค่าในIntentsบนอุปกรณ์ Android 7.0 ขึ้นไป

ทางเลือกของคุณคือ:

  1. ดรอปของคุณtargetSdkVersionถึง 23 หรือต่ำกว่าหรือ

  2. ใส่เนื้อหาของคุณในที่จัดเก็บข้อมูลภายในจากนั้นใช้FileProviderเพื่อทำให้แอปอื่น ๆ

ตัวอย่างเช่น:

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

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

(จากโครงการตัวอย่างนี้ )


ขอบคุณสำหรับคำตอบ. จะเกิดอะไรขึ้นเมื่อฉันใช้สิ่งนี้กับไฟล์บน/systemพาร์ติชัน แอปทุกตัวควรจะสามารถเข้าถึงพาร์ติชันนี้ได้โดยไม่ต้องรูท
โทมัสชราบ

2
@ SuperThomasLab: ฉันจะไม่พึ่งพาทุกสิ่งใน/systemการอ่านได้ทั่วโลก ที่ถูกกล่าวว่าฉันเดาว่าคุณจะยังคงได้รับข้อยกเว้นนี้ ฉันสงสัยว่าพวกเขากำลังตรวจสอบโครงร่างและไม่พยายามตรวจสอบว่าไฟล์นั้นสามารถอ่านได้ทั่วโลกหรือไม่ อย่างไรก็ตามFileProviderจะไม่ช่วยคุณเพราะคุณไม่สามารถสอนให้บริการ/systemได้ คุณสามารถสร้างกลยุทธ์ที่กำหนดเองสำหรับฉันStreamProviderหรือทำให้เป็นของคุณเองContentProviderเพื่อแก้ไขปัญหา
CommonsWare

ยังคงคิดว่าฉันจะแก้ปัญหาอย่างไร .. แอพที่ฉันอัพเดตด้วยการรองรับ Android N เป็นเบราว์เซอร์รูท แต่ตอนนี้คุณไม่สามารถเปิดไฟล์ใด ๆ ในไดเรกทอรีรากได้อีกต่อไป ( /data, /system) เนื่องจาก "การเปลี่ยนแปลงที่ดี" นี้
โธมัส Vos

1
อะไรคือข้อเสียที่สำคัญที่สุดในการทิ้ง targetSdkVersion ไปที่ 23 ขอบคุณ
rommex

2
@rommex: ฉันไม่ทราบว่าสิ่งที่มีคุณสมบัติเป็น "สำคัญที่สุด" ตัวอย่างเช่นผู้ใช้ที่ทำงานในโหมดแยกหน้าจอหรืออุปกรณ์หลายหน้าต่างอิสระ (Chromebooks, Samsung DeX) จะได้รับแจ้งว่าแอปของคุณอาจไม่ทำงานกับหลายหน้าต่าง ไม่ว่าจะสำคัญหรือไม่ก็ตามก็ขึ้นอยู่กับคุณ
CommonsWare

47

ก่อนอื่นคุณต้องเพิ่มผู้ให้บริการลงใน AndroidManifest ของคุณ

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

ตอนนี้สร้างไฟล์ในโฟลเดอร์ทรัพยากร xml (หากใช้ android studio คุณสามารถกด Alt + Enter หลังจากไฮไลต์ file_paths และเลือกสร้างตัวเลือกทรัพยากร xml)

ถัดไปในไฟล์ file_paths

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

ตัวอย่างนี้ใช้สำหรับพา ธ ภายนอกคุณสามารถอ้างอิงได้ที่นี่เพื่อดูตัวเลือกเพิ่มเติม นี่จะช่วยให้คุณสามารถแชร์ไฟล์ที่อยู่ในโฟลเดอร์นั้นและโฟลเดอร์ย่อย

ตอนนี้สิ่งที่เหลือคือการสร้างความตั้งใจดังนี้

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

แก้ไข : ฉันเพิ่มโฟลเดอร์รากของ sd card ใน file_paths ฉันทดสอบโค้ดนี้แล้วและใช้งานได้


1
ขอบคุณสำหรับสิ่งนี้. ฉันต้องการแจ้งให้คุณทราบว่ามีวิธีที่ดีกว่าในการรับนามสกุลไฟล์ String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString()); นอกจากนี้ฉันขอแนะนำให้ทุกคนที่กำลังมองหาคำตอบให้อ่านผ่านFileProviderก่อนและทำความเข้าใจกับสิ่งที่คุณกำลังทำที่นี่ด้วยสิทธิ์ของไฟล์ใน Android N ขึ้นไป มีตัวเลือกสำหรับการจัดเก็บข้อมูลภายในกับการจัดเก็บข้อมูลภายนอกและสำหรับเส้นทางไฟล์และแคชเส้นทาง
praneetloke

2
ผมได้รับข้อยกเว้นต่อไปนี้: java.lang.IllegalArgumentException: Failed to find configured root ...และสิ่งเดียวที่ทำงานเป็น<files-path path="." name="files_root" />ในไฟล์ xml <external-path ...แทน ไฟล์ของฉันถูกบันทึกในที่จัดเก็บข้อมูลภายใน
steliosf

26

@palash k คำตอบถูกต้องและใช้งานได้กับไฟล์จัดเก็บข้อมูลภายใน แต่ในกรณีของฉันฉันต้องการเปิดไฟล์จากที่จัดเก็บข้อมูลภายนอกแอปของฉันล้มเหลวเมื่อเปิดไฟล์จากที่จัดเก็บข้อมูลภายนอกเช่น sdcard และ usb แต่ฉันจัดการเพื่อแก้ไขปัญหาด้วยการแก้ไขProvider_paths.xmlจากคำตอบที่ยอมรับ

เปลี่ยนprovider_paths.xmlเหมือนด้านล่าง

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

และในคลาส java (ไม่มีการเปลี่ยนแปลงเนื่องจากคำตอบที่ยอมรับเป็นเพียงการแก้ไขเล็กน้อย)

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

สิ่งนี้ช่วยฉันในการแก้ไขข้อผิดพลาดของไฟล์จากการจัดเก็บข้อมูลภายนอกหวังว่าจะช่วยให้ผู้ที่มีปัญหาเช่นเดียวกับฉัน :)


1
คุณพบ<root-pathกรุณาเกี่ยวกับที่ไหน มันใช้งานได้ดี <external-path path="Android/data/${applicationId}/" name="files_root" />ไม่มีผลกับไฟล์ที่เปิดจากที่จัดเก็บข้อมูลภายนอก
t0m

ฉันพบสิ่งนี้จากผลการค้นหาที่หลากหลายให้ฉันตรวจสอบอีกครั้งและกลับไปที่คุณโดยเร็ว
Ramz

นอกจากนี้ยังมีการจัดเก็บข้อมูลภายนอกที่คุณพูดถึงคือการ์ด SD หรือการจัดเก็บ inbuilt?
Ramz

ขออภัยในความไม่ถูกต้อง ฉันหมายถึงAndroid/data/${applicationId}/ใน SDcard
t0m

1
จำเป็นต้องเพิ่มสิ่งนี้เข้ากับเจตนา: intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
s-hunter

26

โซลูชันของฉันคือ 'Uri.parse' เส้นทางของไฟล์เป็นสตริงแทนที่จะใช้ 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);

ดูเหมือนว่า fromFile () ใช้ตัวชี้ไฟล์ซึ่งฉันคิดว่าอาจไม่ปลอดภัยเมื่อที่อยู่หน่วยความจำสัมผัสกับแอพทั้งหมด แต่เส้นทางไฟล์ String ไม่เคยทำร้ายใครดังนั้นมันจึงทำงานได้โดยไม่ต้องโยน FileUriExposedException

ทดสอบกับระดับ API 9 ถึง 27! เปิดไฟล์ข้อความเพื่อแก้ไขในแอปอื่นได้สำเร็จ ไม่ต้องการ FileProvider หรือห้องสมุดสนับสนุน Android เลย


ฉันหวังว่าฉันจะเห็นสิ่งนี้ก่อน ฉันไม่ได้พิสูจน์ว่ามันใช้งานได้สำหรับฉัน แต่มันยุ่งยากน้อยกว่า FileProvider มาก
Dale

หมายเหตุเกี่ยวกับสาเหตุที่ใช้งานได้จริง: ไม่ใช่ตัวชี้ไฟล์ที่ทำให้เกิดปัญหา แต่ความจริงที่ว่าข้อยกเว้นจะเกิดขึ้นหากคุณมีพา ธ ที่มี 'file: //' เท่านั้น .
Xmister

3
สิ่งนี้ไม่ได้รับการยกเว้น แต่ไม่สามารถส่งไฟล์ไปยังแอพที่เกี่ยวข้องได้ ดังนั้นไม่ทำงานสำหรับฉัน
Serdar Samancıoğlu

1
สิ่งนี้จะล้มเหลวใน Android 10 และสูงกว่าเนื่องจากคุณไม่สามารถสันนิษฐานได้ว่าแอปอื่นสามารถเข้าถึงที่เก็บข้อมูลภายนอกผ่านระบบไฟล์
CommonsWare

24

เพียงวางรหัสด้านล่างในกิจกรรมบนสร้าง ()

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

มันจะละเว้นการสัมผัส URI


1
นี่คือหนึ่งในโซลูชัน แต่ไม่ใช่โซลูชันมาตรฐาน คนที่มีคำตอบไม่ถูกต้องนั้นเป็นรหัสที่ใช้งานได้ดี
saksham

23

onCreate()เพียงแค่วางรหัสด้านล่างในกิจกรรม

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

มันจะละเว้นการสัมผัส URI

การเข้ารหัสที่มีความสุข :-)


1
ข้อเสียของเรื่องนี้คืออะไร?
James F

1
สิ่งนี้จะล้มเหลวใน Android 10 และสูงกว่าเนื่องจากคุณไม่สามารถสันนิษฐานได้ว่าแอปอื่นสามารถเข้าถึงที่เก็บข้อมูลภายนอกผ่านระบบไฟล์
CommonsWare

18

การใช้ fileProvider เป็นวิธีที่จะไป แต่คุณสามารถใช้วิธีแก้ไขง่ายๆนี้:

คำเตือน : จะได้รับการแก้ไขในรุ่น Android ครั้งถัดไป - https://issuetracker.google.com/issues/37122890#comment4

แทนที่:

startActivity(intent);

โดย

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

7
ผู้เลือกจะได้รับการแก้ไขโดย Google ในเร็ว ๆ นี้เพื่อให้มีเช็คเดียวกัน นี่ไม่ใช่วิธีแก้ปัญหา
ตัวชี้ Null

อันนี้ใช้งานได้ แต่จะไม่สามารถใช้งานได้ใน Android รุ่นต่อไป
Diljeet

13

ฉันใช้คำตอบของ Palash ที่ระบุไว้ด้านบน แต่มันค่อนข้างไม่สมบูรณ์ฉันต้องขออนุญาตแบบนี้

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

เพียงวางรหัสด้านล่างในกิจกรรมบนสร้าง ()

StrictMode.VmPolicy.Builder builder = ใหม่ StrictMode.VmPolicy.Builder (); StrictMode.setVmPolicy (builder.build ());

มันจะละเว้นการสัมผัส URI


สิ่งนี้จะลบนโยบายเข้มงวดโหมด และจะเพิกเฉยต่อคำเตือนความปลอดภัย ไม่ใช่ทางออกที่ดี
Inspire_coding

นอกจากนี้ยังจะล้มเหลวใน Android 10 และสูงกว่าเนื่องจากคุณไม่สามารถสันนิษฐานได้ว่าแอปอื่นสามารถเข้าถึงที่เก็บข้อมูลภายนอกผ่านระบบไฟล์
CommonsWare

7

เพิ่มสองบรรทัดนี้ใน onCreate

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

วิธีการแบ่งปัน

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

สิ่งนี้จะล้มเหลวใน Android 10 และสูงกว่าเนื่องจากคุณไม่สามารถสันนิษฐานได้ว่าแอปอื่นสามารถเข้าถึงที่เก็บข้อมูลภายนอกผ่านระบบไฟล์
CommonsWare

7

นี่คือทางออกของฉัน:

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

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

ในส่วนของฉันฉันมีรหัสต่อไป:

 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คือทั้งหมดที่คุณต้องการ

ยังไม่จำเป็นต้องสร้าง

public class GenericFileProvider extends FileProvider {}

ฉันทดสอบกับ Android 5.0, 6.0 และ Android 9.0 และมันประสบความสำเร็จ


ฉันได้ทดสอบโซลูชันนี้และใช้งานได้ดีกับการเปลี่ยนแปลงเล็กน้อย: intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.putExtra (Intent.EXTRA_STREAM, myPhotoFileUri) intent.type = "image / png" startActivity แบ่งปันภาพผ่าน ")) มันทำงานได้ดีบน Android 7 และ 8
inspire_coding

4

สำหรับการดาวน์โหลด pdf จากเซิร์ฟเวอร์ให้เพิ่มรหัสด้านล่างในคลาสบริการของคุณ หวังว่านี่จะเป็นประโยชน์สำหรับคุณ

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

และใช่อย่าลืมเพิ่มสิทธิ์และผู้ให้บริการในรายการของคุณ

<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
คือ@xml/provider_pathsอะไร
adityasnl

1
@Heisenberg โปรดอ้างอิง Rahul Upadhyay โพสต์จาก url: stackoverflow.com/questions/38555301/…
Bhoomika Chauhan

3

ฉันไม่รู้ว่าทำไมฉันทำทุกอย่างเหมือนกับ Pkosta ( https://stackoverflow.com/a/38858040 ) แต่ได้รับข้อผิดพลาด:

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

ฉันเสียเวลาหลายชั่วโมงกับปัญหานี้ ผู้ร้าย? Kotlin

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

intentจริง ๆ แล้วคือการตั้งค่าgetIntent().addFlagsแทนที่จะปฏิบัติการบน playIntent ที่เพิ่งประกาศใหม่ของฉัน


2

ฉันใส่วิธีนี้เพื่อให้เส้นทาง imageuri รับเนื้อหาได้อย่างง่ายดาย

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

มี 3 ขั้นตอนหลักที่นี่ดังกล่าวด้านล่าง

ขั้นตอนที่ 1: รายการอย่างชัดแจ้ง

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

ขั้นตอนที่ 2: สร้างไฟล์ 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>

ขั้นตอนที่ 3: การเปลี่ยนแปลงรหัส

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

ฉันรู้ว่านี่เป็นคำถามที่ค่อนข้างเก่า แต่คำตอบนี้มีไว้สำหรับผู้ชมในอนาคต ดังนั้นฉันจึงพบปัญหาที่คล้ายกันและหลังจากการวิจัยฉันพบทางเลือกอื่นสำหรับวิธีการนี้

เจตนาของคุณที่นี่เช่น: หากต้องการดูภาพของคุณจากเส้นทางของคุณใน 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)

ฟังก์ชั่นหลักด้านล่าง

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

ในทำนองเดียวกันแทนที่จะเป็นรูปภาพคุณสามารถใช้รูปแบบไฟล์อื่น ๆ เช่น pdf และในกรณีของฉันมันใช้งานได้ดี


0

ฉันใช้เวลาเกือบวันพยายามคิดออกว่าทำไมฉันถึงได้รับข้อยกเว้นนี้ หลังจากการดิ้นรนมากมายการกำหนดค่านี้ทำงานได้อย่างสมบูรณ์แบบ ( 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>

แสดงเจตนา

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
}

ฉันอธิบายกระบวนการทั้งหมดที่นี่


-1

https://stackoverflow.com/a/38858040/395097คำตอบนี้เสร็จสมบูรณ์

คำตอบนี้มีไว้สำหรับ - คุณมีแอปที่กำหนดเป้าหมายต่ำกว่า 24 และตอนนี้คุณกำลังอัปเกรดเป็น targetSDKVersion> = 24

ใน Android N เฉพาะไฟล์ uri ที่สัมผัสกับแอปของบุคคลที่สามเท่านั้นที่มีการเปลี่ยนแปลง (ไม่ใช่วิธีที่เราใช้มาก่อน) ดังนั้นเปลี่ยนเฉพาะสถานที่ที่คุณแชร์เส้นทางด้วยแอพของบุคคลที่สาม (กล้องในกรณีของฉัน)

ในแอพของเราเรากำลังส่ง uri ไปยังแอพ Camera ในตำแหน่งนั้นเราคาดว่าแอพกล้องจะเก็บภาพที่ถ่ายไว้

  1. สำหรับ android N เราสร้างเนื้อหาใหม่: // uri ตาม url ที่ชี้ไปที่ไฟล์
  2. เราสร้างเส้นทางตามปกติของไฟล์ api เหมือนกัน (โดยใช้วิธีเก่ากว่า)

ตอนนี้เรามี uri 2 ไฟล์ที่ต่างกัน # 1 แชร์กับแอปกล้องถ่ายรูป หากความตั้งใจของกล้องประสบความสำเร็จเราสามารถเข้าถึงภาพจาก # 2

หวังว่านี่จะช่วยได้


1
คุณกำลังอ้างอิงคำตอบที่โพสต์ไว้แล้วที่นี่หากคุณจำเป็นต้องกรอกให้สมบูรณ์แสดงความคิดเห็นในคำตอบ
IgniteCoders

1
@IgniteCoders ตามที่ฉันพูดไว้อย่างชัดเจนในข้อความคำตอบของฉันครอบคลุมถึงกรณีการใช้ที่เกี่ยวข้อง
Aram

-1

Xamarin.Android

หมายเหตุ:เส้นทางxml / provider_paths.xml (.axml) ไม่สามารถแก้ไขได้แม้หลังจากสร้างโฟลเดอร์xmlภายใต้ทรัพยากร (อาจจะสามารถใส่ในตำแหน่งที่มีอยู่เช่นค่าไม่ได้ลอง) ดังนั้นฉันจึงหันไปใช้ สิ่งนี้ใช้ได้ผลในตอนนี้ การทดสอบแสดงให้เห็นว่าจำเป็นต้องเรียกใช้เพียงครั้งเดียวต่อการเรียกใช้แอปพลิเคชัน (ซึ่งสมเหตุสมผลว่าจะเปลี่ยนสถานะการทำงานของโฮสต์ VM)

หมายเหตุ: xmlต้องเป็นตัวพิมพ์ใหญ่ดังนั้น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);

-1

@Pkosta คำตอบคือวิธีหนึ่งในการทำสิ่งนี้

นอกจากการใช้งานFileProviderแล้วคุณยังสามารถแทรกไฟล์ลงในMediaStore(โดยเฉพาะไฟล์รูปภาพและวิดีโอ) เนื่องจากไฟล์ใน MediaStore สามารถเข้าถึงได้จากทุกแอพ

MediaStore มีวัตถุประสงค์หลักที่ประเภทวิดีโอเสียงและภาพ MIME แต่เริ่มต้นด้วย Android 3.0 (ระดับ API 11) มันยังสามารถเก็บประเภทที่ไม่ใช่สื่อ (ดู MediaStore.Files สำหรับข้อมูลเพิ่มเติม) สามารถแทรกไฟล์ลงใน MediaStore โดยใช้ scanFile () หลังจากนั้น content: // style Uri ที่เหมาะสมสำหรับการแบ่งปันจะถูกส่งผ่านไปยัง callback ที่ระบุไว้ใน โปรดทราบว่าเมื่อเพิ่มลงในระบบ MediaStore แล้วแอปใด ๆ บนอุปกรณ์สามารถเข้าถึงเนื้อหาได้

ตัวอย่างเช่นคุณสามารถแทรกไฟล์วิดีโอไปยัง MediaStore เช่นนี้:

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

contentUriเป็นเช่นcontent://media/external/video/media/183473ซึ่งสามารถส่งโดยตรงไปที่Intent.putExtra:

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

FileProviderงานนี้สำหรับฉันและประหยัดยุ่งยากของการใช้


-1

เพียงแค่ปล่อยให้มันละเว้น URI Exposure ... เพิ่มเข้าไปหลังจากที่สร้าง

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

สิ่งนี้จะล้มเหลวใน Android 10 และสูงกว่าเนื่องจากคุณไม่สามารถสันนิษฐานได้ว่าแอปอื่นสามารถเข้าถึงที่เก็บข้อมูลภายนอกผ่านระบบไฟล์
CommonsWare

ไม่ต้องใช้ในแอปการผลิต
Jorgesys

-1

ลองวิธีนี้

ใส่ใบอนุญาตเหล่านี้ในรูปแบบ

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

เจตนาที่จะถ่ายภาพ

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

รับภาพที่บันทึกไว้ใน ONACTIVITYRESULT

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

วิธีรับ 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);
    }

ทุกคนสามารถบอกฉันได้ว่าทำไมถึงลงคะแนน นี่เป็นวิธีการแก้ปัญหาการทำงาน 100%
Abdul Basit Rishi

นี่ทำให้คุณได้ภาพขนาดย่อไม่ใช่ภาพเต็ม
Build3r

-2

ในกรณีของผมผมได้กำจัดของข้อยกเว้นโดยการแทนที่ที่มีเพียงSetDataAndTypeSetData

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.