如何从远程页面访问PhoneGap API

26
我有以下情况: 我有一个已经存在的远程网页,我想开发一个应用程序来使用这个页面。 到目前为止还好。当我启动应用程序时,本地的index.html被加载并重定向(window.open target: _self)到外部网站。这个网站在phonegap webview中打开。在外部网站上,我添加了cordova.js 以便访问本机phonegap API。但它没有正确工作。 deviceReady 事件被正确触发,但我无法访问phonegap API,例如navigator.camera。
我该如何才能访问API呢?
请不要评论说它会被AppStore等拒绝。
感谢您的帮助!

你使用的是哪个PhoneGap版本? - Michael Kunst
你正在使用InAppBrowser吗?如果是的话,那么你无法访问Cordova API。请查看此链接:http://docs.phonegap.com/en/3.3.0/cordova_inappbrowser_inappbrowser.md.html#InAppBrowser - Divesh Salian
我正在使用版本3.3.0,外部网站在Cordova WebView中加载。 - riedelinho
我正在使用 window.location="http://your.website";<content src="http://your.website"" /> 遇到相同的问题。 - MichaelS
7个回答

13

对我而言,解决方案源于多个渠道,但其中大部分在这里找到了。

你应该执行以下步骤:

  1. Define your config.xml to point directly to the remote index.html.

    <content src="http://your-remote-location/index.html" />
    
  2. In your index.html any reference to a local android device resource prepend with some unique prefix like **injection**. For instance for cordova.js you'll come up with something like:

    <script type="text/javascript" src="**injection**www/cordova.js"></script>
    
  3. Find SystemWebViewClient.java under the following location: your-project-location\platforms\android\CordovaLib\src\org\apache\cordova\engine.

  4. Add the following enum declaration in the private members section of the class at the top:

    private enum WebExtension {
        PNG, MP3, MP4, TTF, SVG, JS, ICO, HTML, CSS, EOT, WOFF, JSON;
    }
    
  5. Locate the shouldInterceptRequest method and add the following right after the try { line:

    if(url != null && url.contains(INJECTION_TOKEN)) {
        String assetPath = url.substring(url.indexOf(INJECTION_TOKEN) + INJECTION_TOKEN.length(), url.length());
        try {
            String mimeType = "text/plain";
    
            String ext = assetPath.substring(assetPath.lastIndexOf(".") + 1, assetPath.length());
            WebExtension extension = WebExtension.valueOf(ext.toUpperCase());
    
            switch(extension) {
                case PNG:
                    mimeType = "image/png";
                    break;
                case MP3:
                    mimeType = "audio/mpeg";
                    break;
                case MP4:
                    mimeType = "video/mp4";
                    break;
                case TTF:
                    mimeType = "application/x-font-ttf";
                    break;
                case SVG:
                    mimeType = "image/svg+xml";
                    break;
                case JS:
                    mimeType = "application/javascript";
                    break;
                case ICO:
                    mimeType = "image/x-icon";
                    break;
                case HTML:
                    mimeType = "text/html";
                    break;
                case CSS:
                    mimeType = "text/css";
                    break;
                case EOT:
                    mimeType = "application/vnd.ms-fontobject";
                    break;
                case WOFF:
                    mimeType = "application/x-font-woff";
                    break;
                case JSON:
                    mimeType = "application/json";
                    break;
            }
    
            WebResourceResponse response = new WebResourceResponse(
                mimeType,
                "UTF-8",
                parentEngine.webView.getContext().getAssets().open(assetPath)
            );
            return response;
        } catch (IOException e) {
            e.printStackTrace(); // Failed to load asset file
        }
    }
    

所有这一切的结果将是拦截每个资源请求,并在其中包含**injection**字符串的情况下,削减资源位置,并从应用程序运行的本地设备位置请求它。mimeType是通过应用程序浏览器以正确的方式加载资源所必需的。

希望对某些人有所帮助。


这个解决方案对我来说是最好的事情,它为Cordova开发打开了许多可能性。下一个Cordova版本应该默认包含它。 - shubh jaiswal

11
在远程网站中包含cordova.js脚本将是棘手的,因为每个平台都有不同的cordova.js。您可以修改服务器以便根据用户代理返回正确的cordova.js,但这也很棘手,因为当您从移动浏览器查看站点时,它会包括此脚本,这是不希望出现的,因为可能会向用户显示JavaScript错误。从桌面计算机查看站点时情况也一样,不应包括cordova.js。
在我看来,您有一个本地网页(包含cordova脚本),然后从那里切换到远程页面(也包含脚本)。我不确定这个页面更改是否会起作用。如果起作用,您可能需要等待第二个deviceready事件。
但是,您可以将远程网站页面设置为cordova应用程序中的根页面,无需中间的“加载器”页面。只需在config.xml文件中进行设置即可:
<content src="http://your.website.fake/index.html" />
你需要确保允许在应用程序中加载你的网站。在同一文件中,你应该添加:
<access origin="http://your.website.fake" subdomains="true"/> 

我已经尝试过了,这种方式可以加载远程页面,但无法访问Phongap API。 - MichaelS
你正在使用哪个API?如果你正在尝试使用第三方插件,并且已经将插件脚本包含在网站中,它应该可以工作。 - Mister Smith
正如我在问题本身的评论中所提到的,我已经尝试了您建议的操作。我还在控制台中进行了检查。 Cordova 被定义了,但所有插件都未被定义。关于 iframe 的解决方案可以在这里找到:https://hackerluddite.wordpress.com/2012/04/15/getting-access-to-a-phones-camera-from-a-web-page/ - MichaelS
1
我已经仔细检查了所有配置,相同的配置和代码在本地页面中运行正常。正如Rivera所建议的那样,看起来它被Cordova故意屏蔽了。 然而,使用Ajax加载远程页面似乎是可行的,您应该从那里开始:)我会尝试更新,非常感谢! - MichaelS
1
@AliNaciErdem:iframe方法完全可行,但会引起其他问题。请参见此处的详细信息。MisterSmith,我认为我会在我的当前项目中跳过phonegap,直接使用内置于Android的JS接口。我知道它可以与远程页面一起使用。 - MichaelS
显示剩余8条评论

10

这个插件可以解决问题,不需要自己编写Android解决方案。

https://www.npmjs.com/package/cordova-plugin-remote-injection

https://github.com/TruckMovers/cordova-plugin-remote-injection

远程注入插件允许远程网站在加载到您的 Cordova 应用程序中时与 Cordova 的 JavaScript API 进行交互。

  • 将 Cordova 和已安装插件 JS 注入到 WebView 中,以便任何远程浏览页面都可以像打包的 Cordova 应用一样访问 Cordova 对象及其插件。

  • 支持 iOS 和 Android 平台。

我测试了它,它运行良好。唯一需要记住的是,您需要等待 cordova 准备就绪,像这样:

<html>
  <head>
  </head>
  <body>
    <script>
      document.addEventListener("deviceready", function() {
          document.write("Now you can use plugins"); 
      }, false);
    </script>
  </body>
</html>

这还能用吗?该插件在Android上可以工作,但在iOS上我无法访问Cordova,并且deviceready事件从未被触发。 - supernova

7
我也遇到过这个问题,但在config.xml(内容和访问标签)中更改并没有起作用。我在手机上运行应用程序时检查了它,发现当我加载远程站点时有一些文件丢失。
首先是名为cordova_plugins.js的文件,在每个平台的platform文件夹中都可以找到。然后,您还需要一些特定于插件的文件。您可以通过构建并从中提取来找到这些文件。对于android,路径如下APK/assets/www/plugins。只需将内容复制到您的服务器上即可。
注意:您也可以在平台文件夹中找到特定于插件的文件,但它们不完整,因为它们缺少cordova.define(“…”开头。这导致必需品和模块未定义,所以只需构建并从那里获取它们。

这很棒。但是如何处理特定于平台的插件呢?我的意思是,如何处理从iPhone和Android手机访问插件的问题? - Heisenberg
当HTML代码是本地加载时,你如何处理?我想你需要检查你正在运行的平台,以知道插件是否可用。Cordova本身应该根据从配置文件生成的plugin.xml文件(我认为是这个文件名)知道它需要加载什么。您可以进行常规的Android构建,并打开APK文件以查看文件结构。 - sorin.silaghi
这很棒,对我帮助很大。起初我使用了iframe的解决方案,但我对它不太满意,所以我尝试了@sorin建议的方法,效果非常好。在服务器端,可以使用userAgent嗅探来检测用户是否来自cordova应用程序,然后返回其他脚本(cordova.js、cordova_plugins.js和pluginSpecific.js..)。还可以在Cordova中添加特定于用户的"userAgent"字符串,以区分“普通”浏览器和cordova webview。 - Motoo

0

这是 Cordova/PhoneGap 强制限制 在这里

    if (startFilePath == nil) {
        loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage];
        NSLog(@"%@", loadErr);
        self.loadFromString = YES;
        appURL = nil;
    }

我过去曾禁用此检查,以便让Cordova与非本地文件地址一起使用。


我在哪里可以找到 Android 的等效物? - MichaelS
不确定,您需要提出一个单独的答案或问题,或者查看代码,它应该以类似的方式工作。 - Rivera

0

我已经花了很长时间来解决PhoneGap快速调试的问题,但是没有找到一种在没有将cordova.js与应用程序(不是在远程位置)放在一起的情况下使API工作的方法。

我不确定为什么这样不起作用。如果你知道内部运作方式,我期待听到你的意见...

我最后尝试的方法是在主HTML中放置一个100% x 100%的iframe,并在同一文档中加载本地的cordova.js。然后,我能够使用API,但在iOS上存在一些缩放问题,这是另一个问题...

我不记得我实现和构造这个的确切方式,但如果我找到了,我会进行编辑。


0

最简单的解决方案,允许从远程https页面加载本地文件而不会出现混合内容错误:

对于Android:使用以下方法禁用混合内容策略:https://developer.android.com/reference/android/webkit/WebSettings.html#MIXED_CONTENT_ALWAYS_ALLOW)

对于iOS:我向文件插件提交了一个PR,解决了iOS上的混合内容问题:apache/cordova-plugin-file#296 修复版本可在此处获取:https://github.com/guylando/cordova-plugin-file 如果您在Webview上加载远程站点 https://example.com,则可以使用URL https://example.com/cdvfile/bundle/www/cordova.js 而不是 cdvfile://localhost/bundle/www/cordova.js 来访问本地文件,并通过此方式解决混合内容问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接