开发Cordova插件如何保证callbackContext的活性

1、场景

开发一个cordova插件的时候,可能会遇到一个特别的action,需要调用后把java层获得的值多次返回到上端JS中。但是cordova默认是调用一次callbackContext.error或者success就会不再对上端JS继续传输数据

2、问题分析

查看源码后得知 cordova做了这些操作:
1. java 层通过 callbackContext 将收集到的数据封装进 PluginResult 类中
2. 再通过 toSuccessCallbackstring 或者 toErrorCallbackString 方法将封装的数据传递到 JS 层并且调用
3. 最后根据 keepCallback 的参数,来调整 JS 端的 callback 是否保持活性

具体可以大概参考一下以下两个代码片段

public class PluginResult {
    //将callbackContext返回的数据封装成一个JSON
    public String getJSONString() {
        return "{\"status\":" + this.status + ",\"message\":" + this.getMessage() + ",\"keepCallback\":" + this.keepCallback + "}";
    }

     //调用cordova.js文件中的callbackSuccess方法
     public String toSuccessCallbackString(String callbackId) {
        return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");";
    }

    //调用cordova.js文件中的callbackError方法
    public String toErrorCallbackString(String callbackId) {
        return "cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");";
    }
}

cordova.js

var cordova = {
    /**
     * Called by native code when returning successful result from an action.
     * PluginResult.java中toSuccessCallbackString实际调用的js方法
     */
    callbackSuccess: function(callbackId, args) {
        cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
    },

    /**
     * Called by native code when returning error result from an action.
     */
    callbackError: function(callbackId, args) {
        // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
        // Derive success from status.
        cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
    },

    /**
     * Called by native code when returning the result from an action.
     * 调用回调函数
     */
    callbackFromNative: function(callbackId, isSuccess, status, args, keepCallback) {
        try {
            //根据callbackId获取是success还是error回调
            var callback = cordova.callbacks[callbackId];
            if (callback) {
                if (isSuccess && status == cordova.callbackStatus.OK) {
                    //调用success的函数
                    callback.success && callback.success.apply(null, args);
                } else if (!isSuccess) {
                    //调用error的函数
                    callback.fail && callback.fail.apply(null, args);
                }
                // Clear callback if not expecting any more results
                //如果keepCallback为false,则回调一次立即停止(keepCallback默认是false)
                if (!keepCallback) {
                    delete cordova.callbacks[callbackId];
                }
            }
        }
        catch (err) {
            var msg = "Error in " + (isSuccess ? "Success" : "Error") + " callbackId: " + callbackId + " : " + err;
            console && console.log && console.log(msg);
            cordova.fireWindowEvent("cordovacallbackerror", { 'message': msg });
            throw err;
        }
    },
};

3、问题解决

我的思路是自定义一个customCallbackContext对原来的CallbackContext进行功能的扩展,以下我自定义的CustomCallbackContext.java,重点可以看sendPluginResult方法。
外部调用只需要调用callbackContext.success("msg",true),便可以一直保持JS端接口的活性,调用callbackContext.success("msg",false)或callbackContext.success("msg")就可以使用执行一次就失活的callback

    protected static class CustomCallbackContext extends CallbackContext {


        public static CustomCallbackContext newInstance(CallbackContext callbackContext) {
            CordovaWebView webView = null;
            Class cls = callbackContext.getClass();
            try {
                Field field = cls.getDeclaredField("webView");
                field.setAccessible(true);
                webView = (CordovaWebView) field.get(callbackContext);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return new CustomCallbackContext(callbackContext.getCallbackId(), webView);
        }

        public CustomCallbackContext(String callbackId, CordovaWebView webView) {
            super(callbackId, webView);
        }
        //重写sendPluginResult方法,添加是否保持活性的参数
        public void sendPluginResult(PluginResult pluginResult, boolean isKeep) {
            //最关键的地方,给pluginResult设置是否保证活性
            pluginResult.setKeepCallback(isKeep);
            super.sendPluginResult(pluginResult);
        }

        /********************以下是模仿CallbackContext写的success和error方法*************************/

        /**
         * success回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void success(JSONObject message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.OK, message), isKeep);
        }

        /**
         * success回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void success(String message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.OK, message), isKeep);
        }

        /**
         * success回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void success(JSONArray message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.OK, message), isKeep);
        }

        /**
         * success回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void success(byte[] message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.OK, message), isKeep);
        }

        /**
         * success回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void success(int message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.OK, message), isKeep);
        }

        /**
         * success回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param isKeep
         */
        public void success(boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.OK), isKeep);
        }

        /**
         * error回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void error(JSONObject message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), isKeep);
        }

        /**
         * error回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void error(String message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), isKeep);
        }

        /**
         * error回调,isKeep 为true的时候,可以持续把数据返回到js层中
         *
         * @param message
         * @param isKeep
         */
        public void error(int message, boolean isKeep) {
            sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), isKeep);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章