Cordova 10 和 Android SDK 30:无法访问本地文件(“不允许加载本地资源:file:///storage/emulated/0/Android/data...”)

Posted

技术标签:

【中文标题】Cordova 10 和 Android SDK 30:无法访问本地文件(“不允许加载本地资源:file:///storage/emulated/0/Android/data...”)【英文标题】:Cordova 10 and Android SDK 30 : cannot access local file ("Not allowed to load local resource: file:///storage/emulated/0/Android/data...") 【发布时间】:2021-11-05 05:39:29 【问题描述】:

我正在尝试更新我的 cordova 应用程序以使用 SDK 30 而不是 29,但我遇到了一个无法解决的问题。

显然,在 android SDK 29 和 30 之间,webview 的 setAllowAccessFile 的默认值从 true 更改为 false。问题是,在我的 cordova 应用程序中,我无法访问 WebView API,因此需要更改它config.xml 文件的值。但我找不到任何合适的选项。

或者至少,我认为问题来自这里。

App 是一个使用 Cordova 10 为 Android 打包的 Angular 应用程序。我尝试从 Android 8.1.0 切换到 Android 10,以添加新的 MANAGE_EXTERNAL_STORAGE 权限,以及一些我很确定不会工作的其他东西但是,嗯...但是什么都没有改变。

如果您对如何做到这一点有任何想法,或者问题可能出在哪里,我很乐意听取您的建议。

非常感谢。

config.xml:

<?xml version='1.0.0' encoding='utf-8'?>
<widget xmlns:android="http://schemas.android.com/apk/res/android" id="my.package.name" version="1.0.6" xmlns="http://www.w3.org/ns/widgets">
  <name>My package name</name>
  <description>

  </description>
  <author email="my.email@email.com" href="https://my.website">
    My website
  </author>

  <content src="index.html"/>

  <plugin name="cordova-plugin-whitelist" spec="1"/>
  <plugin name="cordova-plugin-wkwebview-engine" spec="https://github.com/driftyco/cordova-plugin-wkwebview-engine.git"/>
  <plugin name="cordova-plugin-wkwebview-engine" spec="1.2.1"/> 
  <plugin name="cordova-plugin-device" spec="~1.1.5"/>
  <plugin name="cordova-plugin-statusbar" spec="~2.2.2"/>
  <plugin name="cordova-plugin-splashscreen" spec="~4.0.2"/>
  <plugin name="cordova-plugin-file" spec="~6.0.0"/>
  <plugin name="cordova-plugin-zip" spec="~3.1.0"/>
  <plugin name="cordova-plugin-app-version" spec="~0.1.9"/>
  <plugin name="cordova-plugin-network-information" spec="~3.0.0"/>

  <plugin name="cordova-plugin-save-image" spec="0.2.5"/> 
  
  <plugin name="cordova-plugin-customurlscheme" spec="https://github.com/EddyVerbruggen/Custom-URL-scheme.git">
    <variable name="URL_SCHEME" value="my.package.name" />
  </plugin>

  <plugin name="cordova-plugin-intent" spec="https://github.com/napolitano/cordova-plugin-intent.git" />
  
  <feature name="StatusBar">
    <param name="ios-package" value="CDVStatusBar" onload="true"/>
  </feature>
  
  <preference name="Fullscreen" value="true" /> 
  <preference name="StatusBarOverlaysWebView" value="false"/>
  <preference name="DisallowOverscroll" value="true"/>
  <preference name="SplashScreen" value="screen"/>
  <preference name="SplashScreenDelay" value="3000"/>
  <preference name="SplashMaintainAspectRatio" value="true"/>
  <preference name="SplashScreenBackgroundColor" value="white"/>


  <preference name="android-minSdkVersion" value="21"/>
  <preference name="android-targetSdkVersion" value="30"/>


  <preference name="Orientation" value="landscape"/>

  <access origin="*"/>

  <allow-navigation href="http://localhost:8080/*"/>
  <feature name="CDVWKWebViewEngine">
    <param name="ios-package" value="CDVWKWebViewEngine"/>
  </feature>

  <preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine"/>

  <allow-intent href="http://*/*"/>
  <allow-intent href="https://*/*"/>
  <allow-intent href="tel:*"/>
  <allow-intent href="sms:*"/>
  <allow-intent href="mailto:*"/>
  <allow-intent href="geo:*"/>

  <platform name="android">
    <edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
      <application android:allowBackup="false"/>
    </edit-config>
    <edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
      <application android:fullBackupContent="false"/>
    </edit-config>
    <edit-config file="AndroidManifest.xml" target="/manifest/application" mode="merge">
      <application android:largeHeap="true" />
    </edit-config>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    <preference name="AndroidLaunchMode" value="singleTask"/>
    <allow-intent href="market:*"/>
    <intent-filter>
      <action name="android.intent.action.SEND"/>
      <action name="android.intent.action.SEND_MULTIPLE"/>
      <category name="android.intent.category.DEFAULT"/>
      <data mimeType="*/*"/>
    </intent-filter>

    <intent-filter label="@string/launcher_name">
        <action name="android.intent.action.MAIN" />
        <category name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </platform>

  <platform name="ios">
    <allow-intent href="itms:*"/>
    <allow-intent href="itms-apps:*"/>
     <preference name="WKWebViewOnly" value="true" />

    <feature name="CDVWKWebViewEngine">
        <param name="ios-package" value="CDVWKWebViewEngine" />
    </feature>

    <preference name="CordovaWebViewEngine" value="CDVWKWebViewEngine" />
  </platform>

  <engine name="ios" spec="~5.1.1"/>
  <engine name="android" spec="~10.0.0"/>
</widget>

【问题讨论】:

其他几个问题:(1) 您是否还要求在您的应用中获得 Android 运行时权限? (2) 在您的应用程序中,您是否只是在范围内的存储沙箱(即 cordova.plugin.file dataDirectory 和 externalDataDirectory)中寻找文件? 1:不知道您所说的“Android 运行时权限”是什么意思,抱歉。您的意思是在运行时询问清单中的其他权限吗?如果是这样,不,我不知道。 2:不行,我只读取externalApplicationStorageDirectory里面的文件,我试过改成externalDataDirectory也没用。 【参考方案1】:

我们最近在 Cordova 应用中处理了 Android 11 存储/安全问题。以下是我们发现的一些事情——这可能是也可能不是您的应用程序中正在发生的事情。更多信息可以在我的回答中找到(How to get documents in an android directory that PhoneGap will see——我会尽量保持更新):

如果您能提供帮助,请远离MANAGE_EXTERNAL_STORAGE 权限。 Google 为需要扫描文件系统的应用程序(我猜是病毒扫描程序等)保留该权限——其他应用程序将被 Play 商店拒绝。

我不知道还能怎么说,但 Android 6.x 添加了一组额外的授权权限,您的应用需要在运行时请求这些权限(而不是在 AndroidManifext.xml 文件中)。我目前正在使用cordova.plugins.diagnostic 插件,ANDROIDX_VERSION 标志设置为1.0.0。以下是相关代码:

                  // request read access to the external storage if we don't have it
                  cordova.plugins.diagnostic.getExternalStorageAuthorizationStatus(function (status) 
                      if (status === cordova.plugins.diagnostic.permissionStatus.GRANTED) 
                          console.log("External storage use is authorized");
                       else 
                          cordova.plugins.diagnostic.requestExternalStorageAuthorization(function (result) 
                              console.log("Authorization request for external storage use was " + (result === cordova.plugins.diagnostic.permissionStatus.GRANTED ? "granted" : "denied"));
                          , function (error) 
                              console.error(error);
                          );
                      
                  , function (error) 
                      console.error("The following error occurred: " + error);
                  );
                  // request runtime permissions if needed
                  cordova.plugins.diagnostic.getPermissionsAuthorizationStatus(function(statuses)
                      for (var permission in statuses)
                          switch(statuses[permission])
                              case cordova.plugins.diagnostic.permissionStatus.GRANTED:
                                  console.log("Permission granted to use "+permission);
                                  break;
                              case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED:
                                  console.log("Permission to use "+permission+" has not been requested yet; asking now");
                                  cordova.plugins.diagnostic.requestRuntimePermission(function(status)
                                      console.log("Runtime permission request result: " + status.toString());
                                  , function(error)
                                      console.error("The following error occurred: "+error);
                                  , permission);
                                  break;
                              case cordova.plugins.diagnostic.permissionStatus.DENIED_ONCE:
                                  console.log("Permission denied to use "+permission+" - ask again?");
                                  break;
                              case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS:
                                  console.log("Permission permanently denied to use "+permission+" - guess we won't be using it then!");
                                  break;
                          
                      
                  , function(error)
                      console.error("The following error occurred: "+error);
                  ,[
                      cordova.plugins.diagnostic.permission.WRITE_EXTERNAL_STORAGE,
                      cordova.plugins.diagnostic.permission.READ_EXTERNAL_STORAGE
                  ]);

请注意,还有其他插件可以要求适当的运行时权限/授权。诊断是我第一次遇到这个问题时遇到的第一个问题。

希望这会有所帮助。如果您需要比较笔记,我们的应用在 GitHub 上:https://github.com/adapt-it/adapt-it-mobile

【讨论】:

感谢您的帮助。我在研究中也偶然发现了这个插件,但我的测试并没有改变任何东西。该插件告诉我我没有外部存储权限(getExternalStorageAuthorizationStatus),我成功请求并得到它(requestExternalStorageAuthorization),插件告诉我我做对了(getExternalStorageAuthorizationStatus),但加载我的图像没有任何改变.不过,我会试试你的代码,以防我做的事情与你不同。谢谢。

以上是关于Cordova 10 和 Android SDK 30:无法访问本地文件(“不允许加载本地资源:file:///storage/emulated/0/Android/data...”)的主要内容,如果未能解决你的问题,请参考以下文章

cordova build android in windows 10 显示以下问题

如何避免在cordova android应用程序上处理两次深层链接?

极光推送cordova插件修改android原生sdk依赖的方法

cordova 和 java ( JDK ) 和 android-studio (SDK)的初始安装和配置

科尔多瓦:“Android SDK:未安装”

在 Android SDK for Cordova 7.0.1 中安装有啥要求