Асинхронная связь между Javascript и плагин Phonegap

Итак, всем известно, что мы создаем класс, расширяющий CordovaPlugin и переопределяем execute(), а затем создаем мост между JS и родной Java (для Android). Далее мы используем PluginResult, чтобы вернуть результат в JS.

Итак, все это происходит, когда есть запрос, запущенный из JS в Java-плагин. Мой вопрос: как отправить результат обратно в JS (и, следовательно, в HTML) асинхронно?

Я не знаю, будет ли слово асинхронным прямо здесь. Дело в том, что я хочу отправить что-то обратно в JS из синего цвета (скажем, когда wifi становится включенным/отключенным).

Я уже исследовал это, но не получил ничего, что подходит моему делу.

То, что я пробовал, -

  • Создал BroadcastReceiver, слушая события WiFi, используя класс WifiManager.
  • Зарегистрировать приемник.
  • И, наконец, при нажатии Toast, когда WiFi включено/отключено, и отправляет результат с помощью 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 не является моей реальной проблемой, я хочу реплицировать приложение Android Bluetooth Chat в Phonegap. Таким образом, он должен быть асинхронным по своей природе.

Ответ 1

Вы почти у вас есть, но вам нужно, чтобы setKeepCallback был true на вашем PluginResult. Если вы не получите последующие результаты со стороны 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;
            }
        }
    }
}

Ответ 2

Ответ на предупреждение "второго обратного вызова"...

Исходный код Кордовы, который запускает это предупреждение, можно найти в строке 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 каждый раз при вызове. См. Строки 125-127 в код Саймон, связанный с:

public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {

if (action.equals("getConnectionInfo")) {`

this.connectionCallbackContext = callbackContext;

...

Собственная последовательность событий в полном объеме:

  • Сделать первоначальный вызов плагина.

  • Плагин сохраняет ссылку на переданный объект CallbackContext.

  • Сохранять ссылку на объект CallbackContext, возвращая результаты с помощью setKeepCallback (true).

  • Когда последовательность закончена, верните с помощью setKeepCallback (false) (по умолчанию)

Затем позже...

  • Сделайте еще один вызов плагина.

  • Плагин перезаписывает сохраненную ссылку CallbackContext, заменяет переданный объект.

Затем шаги 3-4, как указано выше.

Надеюсь, что помогает:)