開發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);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章