【问题标题】:Asynchronous communication between Javascript and Phonegap PluginJavascript 和 Phonegap 插件之间的异步通信
【发布时间】:2013-10-11 06:16:02
【问题描述】:

所以,每个人都知道我们创建了一个扩展 CordovaPlugin 的类并覆盖了 execute(),然后在 JS 和原生 Java(对于 Android)之间建立了一座桥梁。此外,我们使用PluginResult 将结果返回给 JS。

所以,当 JS 向 Java 插件发出请求时,所有这些都会发生。我的问题是,如何将结果发送回 JS(因此发送回 HTML)异步?

我不知道异步这个词是否在这里。问题是我想突然向 JS 发送一些东西(比如,当 wifi 变为启用/禁用时)。

我已经对此进行了研究,但没有任何适合我的情况。

我试过的是-

  • 使用WifiManager 类创建了一个BroadcastReceiver 监听WiFi 事件。
  • 注册接收器。
  • 最后,在启用/禁用WiFi 时弹出Toast,并使用CallbackContext

    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, "Wifi Connected")) 发送结果,并使用不同的消息断开连接。

MyPlugin.java

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;

...

public class MyPlugin extends CordovaPlugin {
private WifiReceiver wifiBroadcastReceiver = null;
private CallbackContext callbackContext = null;

...

    public MyPlugin() {     
        wifiBroadcastReceiver = new WifiReceiver();
    ...
    }
    ...
    public boolean execute(String action, final JSONArray args,
            final CallbackContext callbackId) throws JSONException {
        IntentFilter wifiFilter = new IntentFilter(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
        cordova.getActivity().registerReceiver(wifiBroadcastReceiver, wifiFilter);
        this.callbackContext = callbackId;

    ...
    }
    public class WifiReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
                if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)) {
                    Toast.makeText(cordova.getActivity(), "Wifi Connected", Toast.LENGTH_SHORT).show();
                    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, "Wifi Connected"));
                } else {
                    Toast.makeText(cordova.getActivity(), "Wifi Disconnected", Toast.LENGTH_SHORT).show();
                    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "Wifi Disconnected"));
                }
            }           
        }

}

Toast 弹出,但 PluginResult 未发送到 JS。


PS : 监听 WiFi 事件不是我的实际问题,我想在 Phonegap 中复制 Android Bluetooth Chat 应用程序。因此,它本质上必须是异步的。

【问题讨论】:

    标签: java android cordova phonegap-plugins


    【解决方案1】:

    您快到了,但您需要在 PluginResult 上将KeepCallback 设置为 true。如果不这样做,Java 端的后续结果将不会在 JavaScript 端有回调。这种类型的编码最好的例子是 Cordova 核心中的网络插件。以下是来源链接:

    https://git-wip-us.apache.org/repos/asf?p=cordova-plugin-network-information.git;a=blob;f=src/android/NetworkManager.java;h=e2ac500ccc885db641d5df6dab8eae23026a5828;hb=HEAD

    所以你应该更新你的代码:

    public boolean execute(String action, final JSONArray args,
            final CallbackContext callbackId) throws JSONException {
        IntentFilter wifiFilter = new IntentFilter(
                WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
        cordova.getActivity().registerReceiver(wifiBroadcastReceiver,
                wifiFilter);
        this.callbackContext = callbackId;
        PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
        result.setKeepCallback(true);
        this.callbackContext.sendPluginResult(result);
        return true;
    }
    
    public class WifiReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
                PluginResult result;
                if (intent.getBooleanExtra(
                        WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)) {
                    Toast.makeText(cordova.getActivity(), "Wifi Connected",
                            Toast.LENGTH_SHORT).show();
                    result = new PluginResult(PluginResult.Status.OK,
                            "Wifi Connected");
                } else {
                    Toast.makeText(cordova.getActivity(), "Wifi Disconnected",
                            Toast.LENGTH_SHORT).show();
                    result = new PluginResult(PluginResult.Status.ERROR,
                            "Wifi Disconnected");
                }
    
                result.setKeepCallback(false);
                if (callbackContext != null) {
                    callbackContext.sendPluginResult(result);
                    callbackContext = null;
                }
            }
        }
    }
    

    【讨论】:

    • 谢谢你的回答,我去看看。
    • 我在我原来的蓝牙聊天应用中实现了这个。 JS 确实收到了结果,但它是Second Callback 的形式。我是怎么注意到的?它在 Logcat 中反映了W/CordovaPlugin(6976): Attempted to send a second callback for ID: BluetoothPlugin1980589494<BR>W/CordovaPlugin(6976): Result was: "Hello"。那么,现在的问题是,如何在 JS 或 HTML 中处理这些 Second Callbacks
    • 请回复西蒙,我正在拼命等待。
    • 嘿西蒙,你一定很忙吧?这就是你不回复的原因。我希望一切都好。你能建议我使用sendJavascript() 将结果发送回Javascript吗,我没有找到任何适合这种方法的文档。请告诉我它的含义可能是什么,而不是使用回调。
    • 如果只有一个 exec 从 Javascript 调用到 Java 插件的 execute(),则您建议的解决方案很好。我的问题是,我有许多从 Javascript 到 Java 的 exec 调用,所以,我想知道在哪个 exec 调用的 callback 中我将处理那些 Second Callbacks。这就是我切换到sendJavascript() 的原因,这样我就可以随时调用 Javascript 函数 [比如说,当一个新的聊天消息到达时]。这是我的东西,我坚持下去。而且没有人回复。我应该使用什么,second callbacksendJavascript()
    【解决方案2】:

    回答“第二次回调”警告..​​.

    触发此警告的 Cordova 源代码可在此处的第 57 行找到:

    https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CallbackContext.java

    因此 - 警告是因为您的 CallbackContext 对象具有“finished=true”。

    最可能的原因是你打电话给:callbackContext.sendPluginResult(pluginResult);

    没有第一次调用:pluginResult.setKeepCallback(true);

    如果不是……您很可能是无意中缓存了 CallbackContext 对象。

    您的 execute() 函数应在每次调用时分配 CallbackContext。请参阅code Simon linked to 中的第 125-127 行:

    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
    
    if (action.equals("getConnectionInfo")) {`
    
    this.connectionCallbackContext = callbackContext;
    
    ...
    

    完整的事件顺序:

    1. 对插件进行初始调用。

    2. 插件保存对传入 CallbackContext 对象的引用。

    3. 保留 CallbackContext 对象引用,同时使用 setKeepCallback(true) 返回结果。

    4. 序列结束后,返回 setKeepCallback(false)(默认)

    然后……

    1. 再次调用插件。

    2. 插件覆盖保存的 CallbackContext 引用,替换为传入的对象。

    然后步骤3-4同上。

    希望有帮助:)

    【讨论】:

    • 哇,这个答案有很多见解......也许你应该写 Cordova 文档!
    • 我发现了一件困难的事情是,如果你在execute方法中返回false,就会为你生成一个结果,你不能再返回任何结果。所以总是在那里返回真实,然后它就像一个魅力!这可能是“第二次回调警告”的另一个原因。
    • @Dunc - 当然 somebody 应该尝试编写适当的文档来编写 Cordova 插件。严重地。文档非常糟糕,至少在 Android 方面是这样。例如,它指出如果您需要在 web 视图启动时执行任何初始化,您应该实现 initialize(CordovaInterface, CordovaWebView) ...但是如果您阅读源代码,那里有一条评论指出插件不应该真的这样做,但应改为实现pluginInitialize()
    猜你喜欢
    • 2011-02-13
    • 2015-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    相关资源
    最近更新 更多