5

Cordova插件如何实现JavaScript与Java的通信

 3 years ago
source link: https://my.oschina.net/u/4956408/blog/5068868
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Cordova插件如何实现JavaScript与Java的通信 - 华为移动服务的个人空间 - OSCHINA - 中文开源技术交流社区

Cordova平台是开源的跨平台开发框架,被广泛应用于移动应用开发领域,可以开发跨安卓、iOS等系统的应用。Cordova平台是基于HTML/JavaScript语言,它是如何在不同平台上能够运行并实现相应功能的呢?这里就用到了Cordova提供的丰富的插件,Cordova的大量插件结合自身的框架,为应用开发者提供了跨平台的能力,开发者不需要与操作系统层面的接口进行交互,可以关注于应用功能本身。HMS Core为了方便Cordova开发者能够更方便快速的接入HMS Core的能力,也针对各能力提供了Cordova的插件

这里将结合最常用的华为推送服务Cordova插件,介绍HMS Core用到的JS-Java消息交互方式,讲解在JS侧如何调用Java侧接口,最终实现HMS Core能力。当然,在进行问题定位时也可以参考本文进行分析。

Cordova基本结构

App启动时,在MainActivity中调用loadUrl函数,即触发了CordovaWebView的初始化,进行Cordova的启动。此时CordovaWebView会创建PluginManager对象,NativeToJsMessageQueue对象以及JavascriptInterface的ExposedJsApi对象。在后续的消息交互中,则主要通过ExposedJsApi和NativeToJsMessageQueue进行。

插件的加载分两步。首先,在PluginManager对象创建时,读取配置文件中的所有Plugin,并创建映射;而在第一次调用该插件时,进行实例化,并执行相关功能。

消息返回模式包含同步和异步两种模式,Cordova中通过在函数中配置async关键字进行区分。

对于同步模式,系统会从NativeToJsMessageQueue队列头获取数据,并返回。然后再根据callbackID查找到请求,返回给success函数。

对于异步模式,执行后会启动循环函数不停的获取队列中的数据。查找到对应的请求时,返回给success函数。

华为推送服务的插件中,采用的是同步的方式。

以推送为样例说明插件调用方式

如果以上没有看懂,没有关系,只要了解下面的过程即可。

1. 插件安装

执行命令 cordova plugin add @hmscore/cordova-plugin-hms-push 安装最新插件。执行后会在plugins中增加插件信息。

其中在plugin.xml中记录了所有用到的js类,android类等信息,在plugin初始化时会将其加载到系统中,如果某个函数或接口未在其中进行配置,会导致无法使用。

2. 消息映射

插件提供了4种消息映射方式:

(1)    HmsMessaging消息调用

在HmsPush.js中通过runHmsMessaging接口将消息转给Android平台,通过异步调用方式,Android返回的结果将通过Promise返回。

消息将会转到HmsPushMessaging类中,在HmsPushMessaging的execute函数中,根据各功能的不同,转向不同的函数进行处理:

public void execute(String action, final JSONArray args, final CallbackContext callbackContext)
        throws JSONException {
    hmsLogger.startMethodExecutionTimer(action);
    switch (action) {
        case "isAutoInitEnabled":
            isAutoInitEnabled(callbackContext);
            break;
        case "setAutoInitEnabled":
            setAutoInitEnabled(args.getBoolean(1), callbackContext);
            break;
        case "turnOffPush":
            turnOffPush(callbackContext);
            break;
        case "turnOnPush":
            turnOnPush(callbackContext);
            break;
        case "subscribe":
            subscribe(args.getString(1), callbackContext);
            break;

在各函数调用中,通过设置结果的方式,将结果返回给JS层。内容将被写入nativeToJsMessageQueue队列中。

callBack.sendPluginResult(new PluginResult(PluginResult.Status.OK,autoInit));

(2)    HmsInstanceId消息调用

在HmsPush.js中通过runHmsInstance接口将消息转给Android平台,通过异步调用方式,平台将结果通过Promise返回。

消息将会转到HmsPushInstanceId类中,在HmsPushInstanceId的execute函数中,根据各功能不同,转向不同的函数处理。

public void execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
    if (!action.equals("init"))
        hmsLogger.startMethodExecutionTimer(action);

    switch (action) {
        case "init":
            Log.i("HMSPush", "HMSPush initialized ");
            break;
        case "enableLogger":
            enableLogger(callbackContext);
            break;
        case "disableLogger":
            disableLogger(callbackContext);
            break;
        case "getToken":
            getToken(args.length() > 1 ? args.getString(1) : Core.HCM, callbackContext);
            break;
        case "getAAID":
            getAAID(callbackContext);
            break;
        case "getCreationTime":
            getCreationTime(callbackContext);
            break;

最后同样使用设置结果的方式,将结果返回给JS层。内容将被写入nativeToJsMessageQueue队列中。

callBack.sendPluginResult(new PluginResult(PluginResult.Status.OK,autoInit));

过程与HmsPushMessaging非常相似。主要区别在于HmsPushInstanceId用于映射HmsInstanceId相关的接口,HmsPushMessaging用于映射HmsMessaging相关接口。

(3)    localNotification消息调用

在HmsLocalNotification.js中通过run接口将消息传递给Android平台,通过异步调用方式,平台将结果通过Promise返回。

消息将会转到HmsLocalNotification类中,在HmsLocalNotification的execute函数中,根据各功能不同,转向不同的函数处理。

public void execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
    switch (action) {
        case "localNotification":
            localNotification(args, callbackContext);
            break;
        case "localNotificationSchedule":
            localNotificationSchedule(args.getJSONObject(1), callbackContext);
            break;
        case "cancelAllNotifications":
            cancelAllNotifications(callbackContext);
            break;
        case "cancelNotifications":
            cancelNotifications(callbackContext);
            break;
        case "cancelScheduledNotifications":
            cancelScheduledNotifications(callbackContext);
            break;
        case "cancelNotificationsWithId":
            cancelNotificationsWithId(args.getJSONArray(1), callbackContext);
            break;

同样会通过sendPluginResult接口返回处理结果。但是localNotification的消息涉及消息发送,会在发送完成后进行消息返回。

(4)    Push事件回调

在Push消息中除了函数调用外,还有很多的事件监听,比如收到普通消息,收到透传消息,收到Token等,因此在Push中还有对于各种事件的监听。

对于回调流程的介绍从Android侧开始。

在Android侧,Push回调消息的函数定义在HmsPushMessageService.java中。

根据SDK的要求,复写需要用到的回调函数:onMessageReceived,onDeletedMessages,onNewToken等。

当事件被触发时,会向JS层发送事件通知:

public static void runJS(final CordovaPlugin plugin, final String jsCode) {
    if (plugin == null)
        return;
    Log.d(TAG, "runJS()");

    plugin.cordova.getActivity().runOnUiThread(() -> {
        CordovaWebViewEngine engine = plugin.webView.getEngine();
        if (engine == null) {
            plugin.webView.loadUrl("javascript:" + jsCode);

        } else {
            engine.evaluateJavascript(jsCode, (result) -> {

            });
        }
    });
}

JS侧的监听定义:

在HmsPushEvent.js中对每个事件都进行了定义及注册

exports.REMOTE_DATA_MESSAGE_RECEIVED = "REMOTE_DATA_MESSAGE_RECEIVED";
exports.TOKEN_RECEIVED_EVENT = "TOKEN_RECEIVED_EVENT";
exports.ON_TOKEN_ERROR_EVENT = "ON_TOKEN_ERROR_EVENT";
exports.NOTIFICATION_OPENED_EVENT = "NOTIFICATION_OPENED_EVENT";
exports.LOCAL_NOTIFICATION_ACTION_EVENT = "LOCAL_NOTIFICATION_ACTION_EVENT";
exports.ON_PUSH_MESSAGE_SENT = "ON_PUSH_MESSAGE_SENT";
exports.ON_PUSH_MESSAGE_SENT_ERROR = "ON_PUSH_MESSAGE_SENT_ERROR";
exports.ON_PUSH_MESSAGE_SENT_DELIVERED = "ON_PUSH_MESSAGE_SENT_DELIVERED";
function onPushMessageSentDelivered(result) {
  window.registerHMSEvent(exports.ON_PUSH_MESSAGE_SENT_DELIVERED, result);
}
exports.onPushMessageSentDelivered = onPushMessageSentDelivered;

需要注意的是,这里的定义需要在应用开发时主动调用,否则不会生效。可以参考Demo中的eventListeners.js。

如果发现已经在Java侧触发回调,但是没有接收到,就需要检查一下Cordova中是否定义有相关调用事件。

这样,在Android侧触发事件时,JS侧就可以收到并处理相关的消息了。如果需要增加新的事件,也可以参考该流程。

通过以上的方式,Push插件实现了JS侧-Java侧的消息互通。在大部分的服务函数接口调用中都采用的类似形式。但是对于某些服务,如广告、地图等需要显示图片或视频的场景(如地图,原生广告等),会用到其他的方式,使用上也更复杂一些,这些会在其他的文档中做详细的介绍。

>>访问HMS Core官网,了解更多相关内容
>>获取HMS Core Cordova插件开发指导文档
>>华为HMS Core官方论坛
>>华为HMS Core Cordova插件开源仓库地址:GitHub

点击右上角头像右方的关注,第一时间了解华为移动服务最新技术~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK