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);
}
}