使用 PhoneGap 从 Android 调用 Javascript

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15852499/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 02:19:34  来源:igfitidea点击:

Javascript calls from Android using PhoneGap

javascriptandroidcordova

提问by stealthjong

I have and application built with PhoneGap, and I'm trying to communicate with Javascriptfrom native code.

我有一个用 构建的应用程序PhoneGap,我正在尝试Javascript从本机代码进行通信。

in my DroidGapextending class:

在我的DroidGap扩展课上:

@Override
public void onCreate(Bundle savedInstanceState) {
    Logger.log("oncreate");
    super.onCreate(savedInstanceState);
    super.init();
    super.appView.getSettings().setJavaScriptEnabled(true);
    super.appView.getSettings().setSupportZoom(true);
    super.appView.getSettings().setBuiltInZoomControls(true);
    super.appView.getSettings().setDisplayZoomControls(false);
    jsinterface = new CommunicationInterface(this, appView);
    super.appView.addJavascriptInterface(jsinterface, "communicationinterface"); 
}

the javascriptinterface:

javascript接口:

public class CommunicationInterface {
    private WebView mAppView;
    private DroidGap mGap;

    public CommunicationInterface(DroidGap gap, WebView view)  {
        mAppView = view;
        mGap = gap;
    }

    public String getTestString() {
        return "teststring";
    }

    public void parse(Object o) {
        Logger.log(o);
    }
}

The Javacriptis located in an external file (I create an HTML file which has this line in the header: <script type="text/javascript" src="scripts.js"></script>)

Javacript位于外部文件(I创建,其具有在此行的HTML文件header<script type="text/javascript" src="scripts.js"></script>

Scripts.js:

Scripts.js

function sendToInterface() {
    alert("alert");
    var map = new Object();
    (...)
    window.communicationinterface.parse(map); //communication js -> android seems to work.
}

I read in other posts that it's possible to communicate between PhoneGap and Android, but thusfar I've not had any success. I did manage to create an alert, but that was with loadUrl("javascript:alert('Alert');"), but I've also read that you shouldn't do because that's what sendJavascript()is for (and it causes leaks, reloads page, etc). I've tried to shoot a couple of Strings through the sendJavascript()method, but to no avail:

我在其他帖子中读到可以在 PhoneGap 和 Android 之间进行通信,但到目前为止我还没有取得任何成功。我确实设法创建了一个警报,但那是使用loadUrl("javascript:alert('Alert');"),但我也读到你不应该这样做,因为这sendJavascript()是为了(它会导致泄漏、重新加载页面等)。我尝试通过该sendJavascript()方法拍摄几个字符串,但无济于事:

  • sendJavascript("javascript:alert('Alert');")
  • sendJavascript("javascript:sendToInterface();")
  • sendJavascript("sendToInterface();")
  • sendJavascript("window.sendToInterface();")
  • sendJavascript("javascript:alert('Alert');")
  • sendJavascript("javascript:sendToInterface();")
  • sendJavascript("sendToInterface();")
  • sendJavascript("window.sendToInterface();")

How to communicate from native -> PhoneGap (or what's wrong with what I already have)? Thusfar other posts and questions haven't helped me with this particular problem.

如何从本机 - > PhoneGap(或我已有的东西有什么问题)进行通信?到目前为止,其他帖子和问题并没有帮助我解决这个特定问题。

Read:

读:

EDIT

编辑

I wrote a working project:

我写了一个工作项目:

Java part

Java部分

import org.apache.cordova.DroidGap;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.Bundle;
import android.util.Log;

public class App extends DroidGap {
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.loadUrl("file:///sdcard/ds/index.html");
    System.out.println("loading from sdcard");
    Thread t = new Thread() {
      public void run() {
        try {
          for (int i = 0; i < 3; i++) {
            sleep(2000);
            sendValue("value " + i, "another vlaue " + i);
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
      };
    };
    t.start();
  }

  public void sendValue(String value1, String value2) {

    System.out.println("sendvalue in app");
    JSONObject data = new JSONObject();
    try {
      data.put("value1", value1);
      data.put("value2", value2);
    } catch (JSONException e) {
      Log.e("CommTest", e.getMessage());
    }
    String js = String.format("window.plugins.appcomm.updateValues('%s');",
        data.toString());
    this.sendJavascript(js);
  }
}

import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

public class AppComm extends Plugin{

  private static AppComm instance;

  public AppComm () {
    instance = this;
  }

  public static AppComm getInstance() {
    return instance;
  }

  @Override
  public PluginResult execute(String action, JSONArray args, String callbackId) {
    System.out.println("in execute from appcomm");


    return null;
  }

  public void sendValue(String value1, String value2) {
    System.out.println("sendvalue in appComm");
    JSONObject data = new JSONObject();
    try {
      data.put("value1", value1);
      data.put("value2", value2);
    } catch (JSONException e) {
      Log.e("CommTest", e.getMessage());
    }
    String js = String.format(
        "window.plugins.commtest.updateValues('%s');",
        data.toString());
    this.sendJavascript(js);
  }
}

res/xml/plugins.xml

res/xml/plugins.xml

<plugins>
    <plugin name="App" value="org.apache.cordova.App"/>
    <plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
    <plugin name="Device" value="org.apache.cordova.Device"/>
    <plugin name="Accelerometer" value="org.apache.cordova.AccelListener"/>
    <plugin name="Compass" value="org.apache.cordova.CompassListener"/>
    <plugin name="Media" value="org.apache.cordova.AudioHandler"/>
    <plugin name="Camera" value="org.apache.cordova.CameraLauncher"/>
    <plugin name="Contacts" value="org.apache.cordova.ContactManager"/>
    <plugin name="File" value="org.apache.cordova.FileUtils"/>
    <plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager"/>
    <plugin name="Notification" value="org.apache.cordova.Notification"/>
    <plugin name="Storage" value="org.apache.cordova.Storage"/>
    <plugin name="Temperature" value="org.apache.cordova.TempListener"/>
    <plugin name="FileTransfer" value="org.apache.cordova.FileTransfer"/>
    <plugin name="Capture" value="org.apache.cordova.Capture"/>
    <plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
    <plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>

    <plugin name="AppComm" value="com.example.plugin.AppComm"/>
</plugins>

cordova.xml

科尔多瓦.xml

<?xml version="1.0" encoding="utf-8"?>
<cordova>
    <access origin=".*"/> <!-- allow local pages -->
    <log level="DEBUG"/>
    <preference name="classicRender" value="true" />
</cordova>

Index.html header

Index.html 标题

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0" />
    <title>
    </title>
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.css" />
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
    </script>
    <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js">
    </script>
    <script type="text/javascript" charset="utf-8" src="cordova.js"></script>   
    <script type="text/javascript" charset="utf-8">
    var AppComm=function(){};

    AppComm.prototype.updateValues=function(a){
    var map = new Object();
    map["X1"] = "hallo";
    map["X2"] = "hi";
    cordova.exec(null, null, null);
    };

    cordova.addConstructor(function(){cordova.addPlugin("appcomm",new AppComm)});
    </script>
</head>

One of the problems was that javascript was in a separate file (I think that was one of the problems). If it isn't to much to ask, how can I properly call java back, and with what values? How to implement the execute method and how to call it (I'm really bad at JQuery)?

问题之一是 javascript 位于一个单独的文件中(我认为这是问题之一)。如果没有太多要问,我该如何正确调用 java,并使用什么值?如何实现execute方法以及如何调用它(我在JQuery上真的很糟糕)?

回答by Shade

Firstly, you are using a Pluginsubclass. Pluginhas been deprecated and has been replaced with CordovaPlugin. If you're using an old version of PhoneGap, I would recommend that you upgrade.

首先,您使用的是Plugin子类。Plugin已被弃用并已替换为CordovaPlugin. 如果您使用的是旧版本的 PhoneGap,我建议您升级。

Secondly, your exec call is wrong. The docs for plugin development clearly state that you have to pass 5 parameters, while you're passing 3 nulls. How do you expect that to be handled?

其次,您的 exec 调用是错误的。插件开发文档明确指出您必须传递 5 个参数,同时传递 3 个空值。您希望如何处理?

cordova.exec(function(winParam) {}, function(error) {}, "service",
             "action", ["firstArgument", "secondArgument", 42,
             false]);

Here, the service, actionand the array of parameters determine what will happen in your Java code. The first two determine what will happen in JavaScript under certain conditions. So, while you can use null for the first two, you have to specify the last three.

在这里,serviceaction和参数数组确定在Java代码中会发生什么。前两个决定了在特定条件下 JavaScript 会发生什么。因此,虽然您可以对前两个使用 null,但您必须指定后三个。

I have a working example plugin that works with PhoneGap 2.3.0. See the code below:

我有一个适用于 PhoneGap 2.3.0 的工作示例插件。请参阅下面的代码:

public class ExampleJSCommunicator extends CordovaPlugin {

    public boolean execute (final String action, final JSONArray args, CallbackContext callbackContext) throws JSONException {
        PluginResult.Status status = PluginResult.Status.OK;
        String result = "";

        cordova.getActivity ().runOnUiThread (new Runnable () {
            @Override
            public void run() {
                try {
                    String displayText = "";
                    if (action.equals ("buttonClicked")) {
                        displayText = args.getString(0) + " was clicked";
                    }

                    else if (action.equals ("animationRunning")) {
                        displayText = args.getBoolean(0) ? "Animation started running" : "Animation stopped running";
                    }

                    TextView label = (TextView) cordova.getActivity().findViewById (R.id.textView);
                    label.setText (displayText + " and the Activity knows it!");
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });

        return true;
    }
}

With the code above, you have your Java-side plugin capable of handling two custom "actions" - buttonClickedand animationRunning. These actions serve my purposes, but you could name them otherwise.

使用上面的代码,您的 Java 端插件能够处理两个自定义“操作” -buttonClickedanimationRunning. 这些操作符合我的目的,但您可以以其他方式命名它们。

Now, you still need to register your plugin, so that Cordova will know about it. This is done in the xml/config.xmlfile. Under plugins, you have to add the following:

现在,您仍然需要注册您的插件,以便 Cordova 知道它。这是在xml/config.xml文件中完成的。在 下plugins,您必须添加以下内容:

<plugin name="ExampleJSCommunicator" value="com.example.phonegap.ExampleJSCommunicator"/>

Then you can pass data (or "actions") from JavaScript as follows. Note that the parameters (which.idand animationRunningare passed in an array):

然后您可以按如下方式从 JavaScript 传递数据(或“动作”)。请注意参数(which.idanimationRunning以数组形式传递):

cordova.exec (null, null, "ExampleJSCommunicator", "buttonClicked", [which.id]);  // my first action
cordova.exec (null, null, "ExampleJSCommunicator", "animationRunning", [animationRunning]);  // my second action

These two execcalls will trigger the execute method in the ExampleJSCommunicatorclass and will get handled in the respective if blocks. It doesn't matter where you call exec, as long as you declare your JavaScript code after you include the cordova.js file. My JavaScript is contained within a separate main.js file and it works just fine:

这两个exec调用将触发类中的 execute 方法ExampleJSCommunicator,并将在相应的 if 块中得到处理。您在何处调用 exec 并不重要,只要您在包含 cordova.js 文件后声明您的 JavaScript 代码即可。我的 JavaScript 包含在一个单独的 main.js 文件中,它工作得很好:

<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="main.js"></script>

回答by Joe B

So far you're not actually using any of the tools that Apache Cordova gives you for making plugins and are just trying to bolt on a class with the standard Android SDK. If you are looking to add functionality, I recommend writing a plugin, because of the following reasons:

到目前为止,您实际上并未使用 Apache Cordova 为您提供的任何工具来制作插件,而只是尝试使用标准 Android SDK 固定一个类。如果您想添加功能,我建议您编写一个插件,原因如下:

  • Apache Cordova has alternate ways of communication between the WebView and Java so that it still works even when addJavascriptInterface fails
  • You don't need to override things
  • Apache Cordova 具有 WebView 和 Java 之间的替代通信方式,因此即使在 addJavascriptInterface 失败时它仍然可以工作
  • 你不需要覆盖的东西

I highly recommend you read this and move your custom Java code to a plugin: http://docs.phonegap.com/en/2.5.0/guide_plugin-development_android_index.md.html

我强烈建议您阅读本文并将您的自定义 Java 代码移至插件:http: //docs.phonegap.com/en/2.5.0/guide_plugin-development_android_index.md.html

However, the main bug in you example is by using a WebView in your class instead of a CordovaWebView. A WebView doesn't have a working sendJavascript method and the only simple way of sending the Javascript back is by using loadUrl.

但是,您示例中的主要错误是在您的类中使用 WebView 而不是 CordovaWebView。WebView 没有可用的 sendJavascript 方法,将 Javascript 发回的唯一简单方法是使用 loadUrl。