feat(事件系统): 添加带回调的事件处理功能
实现原生与前端之间的双向事件通信机制,支持事件处理结果回调 添加 executeEvent 方法用于触发带回调的事件 完善 iOS 和 Android 平台的事件回调处理逻辑 更新文档说明带回调事件的使用方法
This commit is contained in:
191
README.md
191
README.md
@@ -50,11 +50,17 @@ cordova.plugins.ShutoApi.getUserInfo(
|
||||
- `params`: 导航参数(可选,对象类型)
|
||||
|
||||
#### uploadLog
|
||||
当后台需要前端上传日志时触发。
|
||||
当后台需要前端上传日志时触发。前端处理完成后需要通过 callback 返回上传结果。
|
||||
|
||||
**事件数据:**
|
||||
- `logType`: 日志类型(如 "error", "warning", "info" 等)
|
||||
- `message`: 日志消息内容
|
||||
- `timestamp`: 日志时间戳(可选)
|
||||
- `extra`: 额外的日志数据(可选,对象类型)
|
||||
|
||||
## 事件监听示例
|
||||
|
||||
### 基本用法
|
||||
### 基本用法(无回调事件)
|
||||
|
||||
```javascript
|
||||
// 监听导航事件
|
||||
@@ -73,13 +79,65 @@ cordova.plugins.ShutoApi.addEventListener('navigate', function(data) {
|
||||
// navigate(data.route, { state: data.params || {} });
|
||||
});
|
||||
|
||||
// 监听日志上传事件
|
||||
cordova.plugins.ShutoApi.addEventListener('uploadLog', function() {
|
||||
console.log('触发日志上传');
|
||||
// 监听日志上传事件(需要回调)
|
||||
cordova.plugins.ShutoApi.addEventListener('uploadLog', function(data, callback) {
|
||||
console.log('日志类型:', data.logType);
|
||||
console.log('日志消息:', data.message);
|
||||
console.log('时间戳:', data.timestamp);
|
||||
console.log('额外数据:', data.extra);
|
||||
|
||||
// 在这里实现日志上传逻辑
|
||||
// 例如,发送到日志服务器
|
||||
// uploadToLogServer();
|
||||
uploadToLogServer(data)
|
||||
.then(function(result) {
|
||||
// 上传成功,返回结果
|
||||
callback({
|
||||
success: true,
|
||||
uploadedAt: new Date().toISOString(),
|
||||
recordId: result.recordId
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
// 上传失败,返回错误
|
||||
callback(null, error.message);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 带回调的事件
|
||||
|
||||
如果事件需要前端处理后返回结果,可以使用带回调的事件:
|
||||
|
||||
```javascript
|
||||
// 监听需要回调的事件
|
||||
cordova.plugins.ShutoApi.addEventListener('confirm', function(data, callback) {
|
||||
console.log('确认消息:', data.message);
|
||||
|
||||
// 显示确认对话框
|
||||
if (confirm(data.message)) {
|
||||
// 用户确认,返回成功
|
||||
callback({ confirmed: true });
|
||||
} else {
|
||||
// 用户取消,返回错误
|
||||
callback(null, 'User cancelled');
|
||||
}
|
||||
});
|
||||
|
||||
// 监听需要异步处理的事件
|
||||
cordova.plugins.ShutoApi.addEventListener('fetchData', function(params, callback) {
|
||||
console.log('需要获取数据:', params.url);
|
||||
|
||||
// 异步获取数据
|
||||
fetch(params.url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// 成功回调
|
||||
callback(data);
|
||||
})
|
||||
.catch(error => {
|
||||
// 错误回调
|
||||
callback(null, error.message);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
@@ -119,8 +177,55 @@ NSDictionary* params = @{
|
||||
};
|
||||
[pluginInstance navigateToRoute:@"/user/profile" parameters:params];
|
||||
|
||||
// 触发日志上传事件
|
||||
[pluginInstance fireEvent:@"uploadLog" parameters:nil];
|
||||
// 触发带回调的事件 - 日志上传
|
||||
NSDictionary* logData = @{
|
||||
@"logType": @"error",
|
||||
@"message": @"Network request failed",
|
||||
@"timestamp": @([[NSDate date] timeIntervalSince1970]),
|
||||
@"extra": @{
|
||||
@"url": @"https://api.example.com/data",
|
||||
@"statusCode": @500
|
||||
}
|
||||
};
|
||||
[pluginInstance fireEventWithCallback:@"uploadLog" parameters:logData callback:^(NSDictionary* result, NSError* error) {
|
||||
if (error) {
|
||||
NSLog(@"日志上传失败: %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"日志上传成功: %@", result);
|
||||
BOOL success = [result[@"success"] boolValue];
|
||||
NSString* recordId = result[@"recordId"];
|
||||
// 处理上传结果
|
||||
}
|
||||
}];
|
||||
|
||||
// 触发带回调的事件 - 确认对话框
|
||||
NSDictionary* confirmData = @{
|
||||
@"message": @"确定要删除吗?"
|
||||
};
|
||||
[pluginInstance fireEventWithCallback:@"confirm" parameters:confirmData callback:^(NSDictionary* result, NSError* error) {
|
||||
if (error) {
|
||||
NSLog(@"确认失败: %@", error.localizedDescription);
|
||||
} else {
|
||||
BOOL confirmed = [result[@"confirmed"] boolValue];
|
||||
if (confirmed) {
|
||||
NSLog(@"用户已确认");
|
||||
} else {
|
||||
NSLog(@"用户取消");
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
// 触发带回调的事件 - 获取数据
|
||||
NSDictionary* fetchData = @{
|
||||
@"url": @"https://api.example.com/data"
|
||||
};
|
||||
[pluginInstance fireEventWithCallback:@"fetchData" parameters:fetchData callback:^(NSDictionary* result, NSError* error) {
|
||||
if (error) {
|
||||
NSLog(@"获取数据失败: %@", error.localizedDescription);
|
||||
} else {
|
||||
NSLog(@"获取数据成功: %@", result);
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
### Android
|
||||
@@ -141,8 +246,74 @@ try {
|
||||
params.put("showDetails", true);
|
||||
pluginInstance.navigateToRoute("/user/profile", params);
|
||||
|
||||
// 触发日志上传事件
|
||||
pluginInstance.fireEvent("uploadLog", null);
|
||||
// 触发带回调的事件 - 日志上传
|
||||
JSONObject logData = new JSONObject();
|
||||
logData.put("logType", "error");
|
||||
logData.put("message", "Network request failed");
|
||||
logData.put("timestamp", System.currentTimeMillis() / 1000.0);
|
||||
|
||||
JSONObject extra = new JSONObject();
|
||||
extra.put("url", "https://api.example.com/data");
|
||||
extra.put("statusCode", 500);
|
||||
logData.put("extra", extra);
|
||||
|
||||
pluginInstance.fireEventWithCallback("uploadLog", logData, new ShutoApi.EventCallback() {
|
||||
@Override
|
||||
public void onResult(JSONObject result) {
|
||||
try {
|
||||
boolean success = result.getBoolean("success");
|
||||
String recordId = result.getString("recordId");
|
||||
Log.d("ShutoApi", "日志上传成功: " + result.toString());
|
||||
// 处理上传结果
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorMessage) {
|
||||
Log.e("ShutoApi", "日志上传失败: " + errorMessage);
|
||||
}
|
||||
});
|
||||
|
||||
// 触发带回调的事件 - 确认对话框
|
||||
JSONObject confirmData = new JSONObject();
|
||||
confirmData.put("message", "确定要删除吗?");
|
||||
pluginInstance.fireEventWithCallback("confirm", confirmData, new ShutoApi.EventCallback() {
|
||||
@Override
|
||||
public void onResult(JSONObject result) {
|
||||
try {
|
||||
boolean confirmed = result.getBoolean("confirmed");
|
||||
if (confirmed) {
|
||||
Log.d("ShutoApi", "用户已确认");
|
||||
} else {
|
||||
Log.d("ShutoApi", "用户取消");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorMessage) {
|
||||
Log.e("ShutoApi", "确认失败: " + errorMessage);
|
||||
}
|
||||
});
|
||||
|
||||
// 触发带回调的事件 - 获取数据
|
||||
JSONObject fetchData = new JSONObject();
|
||||
fetchData.put("url", "https://api.example.com/data");
|
||||
pluginInstance.fireEventWithCallback("fetchData", fetchData, new ShutoApi.EventCallback() {
|
||||
@Override
|
||||
public void onResult(JSONObject result) {
|
||||
Log.d("ShutoApi", "获取数据成功: " + result.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorMessage) {
|
||||
Log.e("ShutoApi", "获取数据失败: " + errorMessage);
|
||||
}
|
||||
});
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,15 @@ public class ShutoApi extends CordovaPlugin {
|
||||
|
||||
// 保存事件回调上下文
|
||||
private CallbackContext eventCallbackContext;
|
||||
|
||||
// 保存带回调的事件回调映射
|
||||
private Map<String, EventCallback> eventCallbacks = new HashMap<>();
|
||||
|
||||
// 事件回调接口
|
||||
public interface EventCallback {
|
||||
void onResult(JSONObject result);
|
||||
void onError(String errorMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
@@ -28,6 +37,10 @@ public class ShutoApi extends CordovaPlugin {
|
||||
} else if (action.equals("getUserInfo")) {
|
||||
this.getUserInfo(callbackContext);
|
||||
return true;
|
||||
} else if (action.equals("eventCallback")) {
|
||||
this.eventCallback(args);
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -92,4 +105,61 @@ public class ShutoApi extends CordovaPlugin {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 触发带回调的事件
|
||||
private void fireEventWithCallback(String eventName, JSONObject parameters, final EventCallback callback) {
|
||||
if (this.eventCallbackContext == null) {
|
||||
if (callback != null) {
|
||||
callback.onError("No event callback registered");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String callbackId = java.util.UUID.randomUUID().toString();
|
||||
|
||||
if (callback != null) {
|
||||
eventCallbacks.put(callbackId, callback);
|
||||
}
|
||||
|
||||
JSONObject eventData = new JSONObject();
|
||||
eventData.put("eventName", eventName);
|
||||
eventData.put("callbackId", callbackId);
|
||||
|
||||
if (parameters != null) {
|
||||
eventData.put("params", parameters);
|
||||
}
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, eventData);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.eventCallbackContext.sendPluginResult(pluginResult);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
if (callback != null) {
|
||||
callback.onError("Failed to create event data: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理前端回调
|
||||
private void eventCallback(JSONArray args) {
|
||||
try {
|
||||
String callbackId = args.getString(0);
|
||||
JSONObject result = args.optJSONObject(1);
|
||||
String errorMessage = args.optString(2);
|
||||
|
||||
EventCallback callback = eventCallbacks.get(callbackId);
|
||||
|
||||
if (callback != null) {
|
||||
if (errorMessage != null && !errorMessage.isEmpty()) {
|
||||
callback.onError(errorMessage);
|
||||
} else {
|
||||
callback.onResult(result);
|
||||
}
|
||||
eventCallbacks.remove(callbackId);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
NSString* _eventCallbackId;
|
||||
// 保存用户信息
|
||||
NSDictionary* _userInfo;
|
||||
// 保存带回调的事件回调ID映射
|
||||
NSMutableDictionary* _eventCallbacks;
|
||||
}
|
||||
|
||||
- (void)close:(CDVInvokedUrlCommand*)command;
|
||||
@@ -17,10 +19,16 @@
|
||||
- (void)setUserInfo:(CDVInvokedUrlCommand*)command;
|
||||
- (void)navigateToRoute:(NSString*)route parameters:(NSDictionary*)parameters __attribute__((objc_method_family(none)));
|
||||
- (void)fireEvent:(NSString*)type parameters:(NSDictionary*)parameters __attribute__((objc_method_family(none)));
|
||||
- (void)fireEventWithCallback:(NSString*)eventName parameters:(NSDictionary*)parameters callback:(void (^)(NSDictionary* result, NSError* error))callback __attribute__((objc_method_family(none)));
|
||||
- (void)eventCallback:(CDVInvokedUrlCommand*)command;
|
||||
@end
|
||||
|
||||
@implementation ShutoApi
|
||||
|
||||
- (void)pluginInitialize {
|
||||
_eventCallbacks = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
|
||||
- (void)close:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
// 调用JS方法通知前端关闭
|
||||
@@ -129,4 +137,53 @@
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:_eventCallbackId];
|
||||
}
|
||||
|
||||
// 触发带回调的事件
|
||||
- (void)fireEventWithCallback:(NSString*)eventName parameters:(NSDictionary*)parameters callback:(void (^)(NSDictionary* result, NSError* error))callback {
|
||||
if (!_eventCallbackId) {
|
||||
[SGGC_Log log:@"SGGC_CDVFile" message:(@"ShutoApi: No event callback registered")];
|
||||
if (callback) callback(nil, [NSError errorWithDomain:@"ShutoApi" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"No event callback registered"}]);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString* callbackId = [[NSUUID UUID] UUIDString];
|
||||
|
||||
if (callback) {
|
||||
_eventCallbacks[callbackId] = callback;
|
||||
}
|
||||
|
||||
NSMutableDictionary* eventData = [NSMutableDictionary dictionary];
|
||||
[eventData setObject:eventName forKey:@"eventName"];
|
||||
[eventData setObject:callbackId forKey:@"callbackId"];
|
||||
|
||||
if (parameters) {
|
||||
[eventData setObject:parameters forKey:@"params"];
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:eventData];
|
||||
[pluginResult setKeepCallbackAsBool:YES];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:_eventCallbackId];
|
||||
}
|
||||
|
||||
// 处理前端回调
|
||||
- (void)eventCallback:(CDVInvokedUrlCommand*)command {
|
||||
NSString* callbackId = command.arguments[0];
|
||||
NSDictionary* result = command.arguments[1];
|
||||
NSString* errorMessage = command.arguments[2];
|
||||
|
||||
void (^callback)(NSDictionary* result, NSError* error) = _eventCallbacks[callbackId];
|
||||
|
||||
if (callback) {
|
||||
NSError* error = nil;
|
||||
if (errorMessage) {
|
||||
error = [NSError errorWithDomain:@"ShutoApi" code:-1 userInfo:@{NSLocalizedDescriptionKey:errorMessage}];
|
||||
}
|
||||
|
||||
callback(result, error);
|
||||
[_eventCallbacks removeObjectForKey:callbackId];
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,11 @@ var ShutoApi = {
|
||||
},
|
||||
getUserInfo: function (success, error) {
|
||||
exec(success, error, 'ShutoApi', 'getUserInfo', []);
|
||||
},
|
||||
|
||||
// 执行带回调的事件
|
||||
executeEvent: function (eventName, params, success, error) {
|
||||
exec(success, error, 'ShutoApi', 'executeEvent', [eventName, params]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -49,7 +54,32 @@ ShutoApi.removeEventListener = function (eventName, callback) {
|
||||
// 内部事件处理函数,用于接收原生代码的事件通知
|
||||
function onEvent(eventData) {
|
||||
if (eventData && eventData.type) {
|
||||
// 无回调的事件
|
||||
eventEmitter.emit(eventData.type, eventData.params);
|
||||
} else if (eventData && eventData.eventName && eventData.callbackId) {
|
||||
// 带回调的事件
|
||||
var eventName = eventData.eventName;
|
||||
var params = eventData.params || {};
|
||||
var callbackId = eventData.callbackId;
|
||||
|
||||
var listeners = eventEmitter.listeners[eventName] || [];
|
||||
|
||||
if (listeners.length > 0) {
|
||||
var callback = function(result, error) {
|
||||
exec(null, null, 'ShutoApi', 'eventCallback', [callbackId, result, error]);
|
||||
};
|
||||
|
||||
listeners.forEach(function(listener) {
|
||||
try {
|
||||
listener(params, callback);
|
||||
} catch (error) {
|
||||
console.error('Error in event listener for ' + eventName + ': ' + error);
|
||||
callback(null, error.message);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
exec(null, null, 'ShutoApi', 'eventCallback', [callbackId, null, 'No listener for event: ' + eventName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user