Асинхронная связь между 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, как указано выше.
Надеюсь, что помогает:)