Search

Android - N(API 24) 的 FileUriExposedException

2016-10-01 10:43 AM

由於 Android N 加入了 intent 開啟檔案的權限判斷

所有使用 file:// 開頭的 uri 將會出現 FileUriExposedException

必須改為使用 content:// 開頭的 uri

也就是會用到 FileProvider

以下將以點擊通知開啟外部檔案為範例說明須修改的部分

首先在 res/xml 內建立一個 file_paths.xml 的檔案(名稱自訂)

程式碼範例
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="folderName" path="."/>
</paths>

// external-path: 等同於 Environment.getExternalStorageDirectory()
// name="folderName": 任意名稱 點擊後顯示的路徑資料夾名稱
// path=".": 實際要分享權限的資料夾路徑 使用 "." 代表根目錄

建立完成後再到 AndroidManifest.xml 加入 porvider 節點

<application>
...

<provider
        android:name="android.support.v4.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_paths" />
</provider>

...
</application>

接著建立 Uri Intent

Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

File file = new File(dir, filename);

// API 24 之前使用的方式 會出現 FileUriExposedException
// Uri.fromFile(file)

// API 24 使用的方式
Uri uri = FileProvider.getUriForFile(getActivity(), "your.package.name.fileprovider", file);
intent.setDataAndType(uri, MimeTypeMap.getSingleton().getMimeTypeFromExtension("html"));

int flags = PendingIntent.FLAG_UPDATE_CURRENT;
PendingIntent pIntent = PendingIntent.getActivity(getActivity(), REQEUST_CODE, intent, flags);

// 取得系統的通知服務
NotificationManagerCompat notificationManager = NotificationManagerCompat.from( getActivity() );

// 建立通知
final Notification notification = new NotificationCompat.Builder( getActivity() )
        .setSmallIcon( R.drawable.ic_notification )
        .setLargeIcon( BitmapFactory.decodeResource(getResources(), R.drawable.ic_notification_large) )
        .setContentTitle( notificationTitle )
        .setContentText( filename )
        .setContentIntent(pIntent)
        .setOngoing(false)
        .setDefaults(Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS)
        .setPriority(Notification.PRIORITY_MAX)
        .build();

// 發送通知
notificationManager.notify(NID, notification);
各項資料連結
Android - FileProvider