Android Intent FileProvider“权限被拒绝”在谷歌地球中打开KML文件?

Posted

技术标签:

【中文标题】Android Intent FileProvider“权限被拒绝”在谷歌地球中打开KML文件?【英文标题】:Android Intent FileProvider "Permision denied" to open KML file in Google Earth? 【发布时间】:2018-07-19 10:14:36 【问题描述】:

android Nougat (7.0) 之前,在外部存储中创建 KML 文件并使用文件 uri 和 KML 的 Google 地球 mime 类型启动意图很容易。 Google 地球应用神奇地打开并飞到了我的 KML。

在 Android Nougat (7.0) 上,您必须使用 FileProvider 来表示意图。您必须将提供程序添加到 AndroidManifest.xml 和 /res/xml/provider_paths.xml。

使用 FileProvider 意图,安装的 Google 地球 应用将启动,然后在 Google 地球 中显示“加载文件时出错”消息。

Logcat 显示“FileNotFoundException Permission denied”。

文件:AndroidManifest.xml

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <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>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

文件:/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>
// File: MainActivity.java

// I used Android Device Monitor to push mooCow.kml to
//     /storage/emulated/0/Download/mooCow.kml
public void onClick(View arg0) 
    File folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
    File kmlFile =  new File(folder, "mooCow.kml");
    String pathToLocalKmlFile = kmlFile.getAbsolutePath();
    openKmlInGoogleEarth(pathToLocalKmlFile);

// This works with KML and file extensions supported by getMimeTypeFromExtension
public void openKmlInGoogleEarth(String pathToFileInExternalStorage) 
    try 
        String[] splits = pathToFileInExternalStorage.split("\\.");
        String ext = "";
        if(splits.length >= 2) 
            ext = splits[splits.length-1];
        
        MimeTypeMap mime = MimeTypeMap.getSingleton();
        String mimeType = mime.getMimeTypeFromExtension(ext);
        File file = new File(pathToFileInExternalStorage);
        Uri uri;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) 
            // Use file Uri before Android Nougat ( 7.0 )
            uri = Uri.fromFile(file);
         else 
            // Android Nougat ( 7.0 ) and later,
            // use a FileProvider, AndroidManifest provider, and /res/xml/provider_paths.xml
            uri = FileProvider.getUriForFile(MainActivity.this,
                    BuildConfig.APPLICATION_ID + ".provider",
                    file);
        
        final String type = "application/vnd.google-earth.kml+xml";
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(uri, mimeType);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivity(intent);
    
    catch(IllegalArgumentException e) 
        Log.e(TAG, "Unexpected exception openKmlInGoogleEarth", e);
    

adb logcat指向com.google.android.apps.earth权限问题。

华硕文件管理器应用程序如何在 Google 地球中打开完全相同的文件,而我的应用程序却不能?我的非 FileProvider 在 Android 5.2 上运行良好,但在 Android 6 上它只启动 Google 地球?

E Class   : Error reading file from URI: content://com.subsite.googleearthbutton.provider/external_files/Documents/mooCow.kml
E Class   : java.io.FileNotFoundException: Permission denied
E Class   :     at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:144)
E Class   :     at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
E Class   :     at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1104)
E Class   :     at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:942)
E Class   :     at android.content.ContentResolver.openInputStream(ContentResolver.java:662)
E Class   :     at com.google.android.apps.earth.p.m.a(FileUtil.java:6)

什么可能导致文件访问“权限被拒绝”?

【问题讨论】:

相关问题***.com/questions/38200282/… “在 Android 6 上,我认为我需要使用 FileProvider”——不,您仍然可以使用 Uri.fromFile(),至少就操作系统而言。在 Android 7.0+ 上,需要 FileProvider。您的 &lt;uses-permission&gt; 元素位于错误的位置(需要在 &lt;application&gt; 之外)。除此之外,也许pathToLocalKmlFile 不存在。另外,您的 LogCat 条目是来自您的应用还是来自 Google 地球? "$applicationId.provider" 是否解析为"com.subsite.googleearthbutton.provider" intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 如果您不使用文件提供程序,请尝试不使用。 @CommonsWare 所以我确实不需要在 6.0 上需要 FileProvider。我将pathToLocalKmlFile 作为参数传递给openKmlInGoogleEarth。我更正了帖子错误并将&lt;uses-permission&gt; 移到&lt;application&gt; 之外。 “权限被拒绝”来自 com.google.android.apps.earth.p.m.a(FileUtil.java:6)。 Asus File Manager 可以以某种方式打开 KML 文件。我不能有或没有 FileProvider 但相同的代码在 Android 5.1 上工作正常? 【参考方案1】:

解决了。

我忘记添加运行时检查并请求 WRITE_EXTERNAL_STORAGE 权限。

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

我认为在旧版本的 Android 上,我所要做的就是在 AndroidManifest.xml 中添加一个 &lt;uses-permission&gt; 元素

在我检查并请求之前,我既没有 READ_EXTERNAL_STORAGE 也没有 WRITE_EXTERNAL_STORAGE

感谢大家的帮助。

【讨论】:

【参考方案2】:

对我来说,现在这是有效的:

Uri uriFromFile = FileProvider.getUriForFile(context, GenericFileProvider.class.getName(), file); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(uriFromFile, "application/vnd.google-earth.kml+xml"); intent.putExtra("com.google.earth.EXTRA.tour_feature_id", "my_track"); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity(intent);

【讨论】:

putExtra "com.google.earth.EXTRA.tour_feature_id" 添加了什么? 请参阅***.com/a/11943958/257948@EdoftheMountain

以上是关于Android Intent FileProvider“权限被拒绝”在谷歌地球中打开KML文件?的主要内容,如果未能解决你的问题,请参考以下文章

PendingIntent劫持导致app任意文件读写漏洞

Android 7.0适配

android intent 可以传递enum 吗

怎么获取android系统服务,如Uri,intent参数等,或者说去哪里查看

Android开发之Intent.Action Android中Intent的各种常见作用【转】

Android学习笔记 Intent