Try   HackMD

FileProvider 第三方應用開啟 (example: open pdf)

環境設定

AndroidManifest.xml 新增 <provider> section

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

YOUR_PACKAGE_NAME 為 App 唯一識別的 package name

res 資料夾底下新增 raw/sample_pdf.pdf

sample_pdf.pdf 為您要 demo 的 pdf

res 資料夾底下新增 xml/file_provider_paths.xml

<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="files_shared" path="."/> </paths>
  • <files-path> 設定檔案共享的目錄,此指定內部私有空間中的 files 資料夾 (data/../YOUR_PACKAGE_NAME/files);
  • path="."表示允許分享給應用程式的files的子目錄,"."表示 files 資料夾底下的所有檔案都允許分享;
  • name="files_shared" 表示子目錄名稱的別名,當傳分享檔案的位置給其他應用程式,產生 content:// URI 時,會使用此別名來代表實際的檔案路徑。

其他屬性代表的意義可參考

程式碼

因 Android 無法直接開啟既有的檔案(res/raw/sample_pdf),所以要先拷貝一份至 App 本身的儲存空間,再從那裡取出檔案位置,再開啟檔案。

public static void view_pdf(Context context) { // 檢查 App 本身的儲存空間是否存在 pdf 檔 // 這裡是使用的位置是 getFilesDir(), ex: /data/user/0/YOUR_PACKAGE_NAME/files File file = new File(context.getFilesDir(), "sample_pdf.pdf"); InputStream in = null; OutputStream out = null; if (!file.exists()) { try { // Demo 用: 從資料夾 res/raw/ 取得 pdf file in = context.getResources().openRawResource(R.raw.sample_pdf); out = new FileOutputStream(file.getAbsolutePath()); //out = context.openFileOutput(file.getName(), context.MODE_PRIVATE); // 同上 copyPdfFile(in, out); in.close(); in = null; out.flush(); out.close(); out = null; } catch (Exception e) { logger.error("exception " + e.getMessage()); } } Intent intent = new Intent(Intent.ACTION_VIEW); Uri uri; /** * 在 Android 7.0 之後, 跨 App 分享檔案路徑必須透過 FileProvider * 為了更注重安全性, FileProvider 會隱藏的檔案的實際路徑; 若沒有透過 FileProvider App 會拋出 FileUriExposedException * 而原使用 file:// 要改成 content:// */ // > API 24 (Android 7.0): 使用 FileProvider.getUriForFile() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 第二個參數: 須與 AndroidManifest 中 <provider> 的 android:authorities 屬性值一致 uri = FileProvider.getUriForFile( context, "com.maxkit.msicloud_android.fileprovider", file ); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } // < API 24: 使用 Uri.fromFile() else { uri = Uri.fromFile(file); } // 設定要分享的檔案類型 intent.setDataAndType(uri, "application/pdf"); try { context.startActivity(intent); } catch (ActivityNotFoundException e) { Toast.makeText( context, "不存在 Pdf 檢視器", Toast.LENGTH_LONG ).show(); } } private static void copyPdfFile(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } }

Note

  • 當沒有可支援的第三方應用程式,會跳 toast alert
  • 當只有一個可支援的第三方應用程式,會直接使用此應用程式開啟,不會詢問
  • 當存在兩個以上可支援的應用程式,會詢問要使用哪個開啟

Ref.

Reading .pdf from raw folder in project
How to open a pdf stored either in res/raw or assets folder?
File Provider
Android 跨 App 分享檔案 — FileProvider introduced on Android N

tags: File 相關