Merge branch 'main' of https://git.shuto.cn/public/cordova-shuto-api
This commit is contained in:
166
README.md
166
README.md
@@ -40,6 +40,26 @@ cordova.plugins.ShutoApi.getUserInfo(
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
#### ionicReady()
|
||||
标识前端应用已就绪。调用此方法后,原生代码中的事件管理器会触发 `ionicReady` 事件。
|
||||
|
||||
```javascript
|
||||
cordova.plugins.ShutoApi.ionicReady();
|
||||
```
|
||||
|
||||
|
||||
建议在应用初始化完成后调用:
|
||||
|
||||
```javascript
|
||||
document.addEventListener('deviceready', function() {
|
||||
// 应用初始化逻辑...
|
||||
|
||||
// 通知原生代码前端已就绪
|
||||
cordova.plugins.ShutoApi.ionicReady();
|
||||
}, false);
|
||||
```
|
||||
|
||||
### 事件
|
||||
|
||||
#### navigate
|
||||
@@ -169,8 +189,86 @@ cordova.plugins.ShutoApi.removeEventListener('navigate', handleNavigate);
|
||||
|
||||
## 原生代码使用
|
||||
|
||||
### 独立事件管理器(ShutoEventManager)
|
||||
|
||||
插件提供了独立的事件管理器 `ShutoEventManager`,用于在原生代码中进行事件的监听和触发。这与Cordova插件分离,可以在任何原生代码中使用。
|
||||
|
||||
#### 事件列表
|
||||
|
||||
- `ionicReady`: 当前端调用 `ionicReady()` 方法时触发
|
||||
|
||||
---
|
||||
|
||||
### iOS
|
||||
|
||||
#### 1. 导入头文件
|
||||
|
||||
```objective-c
|
||||
#import "ShutoEventManager.h"
|
||||
```
|
||||
|
||||
|
||||
#### 2. 注册事件监听
|
||||
|
||||
```objective-c
|
||||
// 让类遵循 ShutoEventListener 协议
|
||||
@interface YourViewController () <ShutoEventListener>
|
||||
@end
|
||||
|
||||
@implementation YourViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// 注册 ionicReady 事件监听
|
||||
[[ShutoEventManager sharedInstance] addListenerForEvent:@"ionicReady" listener:self];
|
||||
}
|
||||
|
||||
// 实现协议方法
|
||||
- (void)onEvent:(NSString *)eventName data:(NSDictionary *)data {
|
||||
if ([eventName isEqualToString:@"ionicReady"]) {
|
||||
NSLog(@"前端已就绪,可以执行后续操作");
|
||||
// 在这里处理前端就绪后的逻辑
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
// 移除所有事件监听
|
||||
[[ShutoEventManager sharedInstance] removeListener:self];
|
||||
}
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
|
||||
#### 3. 移除事件监听
|
||||
|
||||
```objective-c
|
||||
// 移除特定事件的监听
|
||||
[[ShutoEventManager sharedInstance] removeListenerForEvent:@"ionicReady" listener:self];
|
||||
|
||||
// 移除所有事件的监听
|
||||
[[ShutoEventManager sharedInstance] removeListener:self];
|
||||
```
|
||||
|
||||
|
||||
#### 4. 触发自定义事件
|
||||
|
||||
```objective-c
|
||||
// 触发事件(可带数据)
|
||||
NSDictionary *eventData = @{
|
||||
@"key": @"value",
|
||||
@"timestamp": @([NSDate timeIntervalSinceReferenceDate])
|
||||
};
|
||||
[[ShutoEventManager sharedInstance] fireEvent:@"customEvent" data:eventData];
|
||||
|
||||
// 触发事件(不带数据)
|
||||
[[ShutoEventManager sharedInstance] fireEvent:@"customEvent" data:nil];
|
||||
```
|
||||
|
||||
|
||||
#### 5. 通过插件实例触发事件(Cordova相关)
|
||||
|
||||
在iOS原生代码中,可以通过以下方式触发事件:
|
||||
|
||||
```objective-c
|
||||
@@ -233,6 +331,74 @@ NSDictionary* fetchData = @{
|
||||
|
||||
### Android
|
||||
|
||||
#### 1. 导入类
|
||||
|
||||
```java
|
||||
import cn.shuto.feishuapi.ShutoEventManager;
|
||||
import cn.shuto.feishuapi.ShutoEventListener;
|
||||
```
|
||||
|
||||
|
||||
#### 2. 注册事件监听
|
||||
|
||||
```java
|
||||
public class YourActivity extends AppCompatActivity implements ShutoEventListener {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
// 注册 ionicReady 事件监听
|
||||
ShutoEventManager.getInstance().addListener("ionicReady", this);
|
||||
}
|
||||
|
||||
// 实现接口方法
|
||||
@Override
|
||||
public void onEvent(String eventName, Map<String, Object> data) {
|
||||
if ("ionicReady".equals(eventName)) {
|
||||
Log.d("YourActivity", "前端已就绪,可以执行后续操作");
|
||||
// 在这里处理前端就绪后的逻辑
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// 移除所有事件监听
|
||||
ShutoEventManager.getInstance().removeListener(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### 3. 移除事件监听
|
||||
|
||||
```java
|
||||
// 移除特定事件的监听
|
||||
ShutoEventManager.getInstance().removeListener("ionicReady", this);
|
||||
|
||||
// 移除所有事件的监听
|
||||
ShutoEventManager.getInstance().removeListener(this);
|
||||
```
|
||||
|
||||
|
||||
#### 4. 触发自定义事件
|
||||
|
||||
```java
|
||||
// 触发事件(可带数据)
|
||||
Map<String, Object> eventData = new HashMap<>();
|
||||
eventData.put("key", "value");
|
||||
eventData.put("timestamp", System.currentTimeMillis());
|
||||
ShutoEventManager.getInstance().fireEvent("customEvent", eventData);
|
||||
|
||||
// 触发事件(不带数据)
|
||||
ShutoEventManager.getInstance().fireEvent("customEvent");
|
||||
```
|
||||
|
||||
|
||||
#### 5. 通过插件实例触发事件(Cordova相关)
|
||||
|
||||
在Android原生代码中,可以通过以下方式触发事件:
|
||||
|
||||
```java
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
</feature>
|
||||
</config-file>
|
||||
<source-file src="src/android/ShutoApi.java" target-dir="src/cn/shuto/feishuapi" />
|
||||
<source-file src="src/android/ShutoEventManager.java" target-dir="src/cn/shuto/feishuapi" />
|
||||
<source-file src="src/android/ShutoEventListener.java" target-dir="src/cn/shuto/feishuapi" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<config-file parent="/*" target="config.xml">
|
||||
@@ -21,5 +23,7 @@
|
||||
</feature>
|
||||
</config-file>
|
||||
<source-file src="src/ios/ShutoApi.m" />
|
||||
<source-file src="src/ios/ShutoEventManager.h" />
|
||||
<source-file src="src/ios/ShutoEventManager.m" />
|
||||
</platform>
|
||||
</plugin>
|
||||
@@ -14,6 +14,9 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class implements a Cordova plugin for Shuto API.
|
||||
*/
|
||||
@@ -27,6 +30,9 @@ public class ShutoApi extends CordovaPlugin {
|
||||
// 保存带回调的事件回调映射
|
||||
private Map<String, EventCallback> eventCallbacks = new HashMap<>();
|
||||
|
||||
// 标识前端是否就绪
|
||||
private boolean isIonicReady = false;
|
||||
|
||||
// 事件回调接口
|
||||
public interface EventCallback {
|
||||
void onResult(JSONObject result);
|
||||
@@ -48,6 +54,9 @@ public class ShutoApi extends CordovaPlugin {
|
||||
this.eventCallback(args);
|
||||
callbackContext.success();
|
||||
return true;
|
||||
} else if (action.equals("ionicReady")) {
|
||||
this.ionicReady(callbackContext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -178,4 +187,19 @@ public class ShutoApi extends CordovaPlugin {
|
||||
LOG.e(TAG, "error", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理前端就绪状态
|
||||
private void ionicReady(CallbackContext callbackContext) {
|
||||
isIonicReady = true;
|
||||
LOG.d(TAG, "Ionic is ready");
|
||||
|
||||
ShutoEventManager.getInstance().fireEvent("ionicReady");
|
||||
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
// 提供给原生代码查询就绪状态的接口
|
||||
public boolean isIonicReady() {
|
||||
return isIonicReady;
|
||||
}
|
||||
}
|
||||
|
||||
10
src/android/ShutoEventListener.java
Normal file
10
src/android/ShutoEventListener.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package cn.shuto.feishuapi;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 事件监听器接口
|
||||
*/
|
||||
public interface ShutoEventListener {
|
||||
void onEvent(String eventName, Map<String, Object> data);
|
||||
}
|
||||
89
src/android/ShutoEventManager.java
Normal file
89
src/android/ShutoEventManager.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package cn.shuto.feishuapi;
|
||||
|
||||
import org.apache.cordova.LOG;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* 独立的事件管理类,用于在原生代码中进行事件的监听注册与触发
|
||||
*/
|
||||
public class ShutoEventManager {
|
||||
private static final String TAG = "ShutoEventManager";
|
||||
|
||||
private static ShutoEventManager instance;
|
||||
|
||||
private Map<String, Set<ShutoEventListener>> eventListeners;
|
||||
|
||||
private ShutoEventManager() {
|
||||
eventListeners = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static synchronized ShutoEventManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ShutoEventManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void addListener(String eventName, ShutoEventListener listener) {
|
||||
if (eventName == null || listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
eventListeners.computeIfAbsent(eventName, k -> new CopyOnWriteArraySet<>()).add(listener);
|
||||
LOG.d(TAG, "Added listener for event: " + eventName + ", total listeners: " + eventListeners.get(eventName).size());
|
||||
}
|
||||
|
||||
public void removeListener(String eventName, ShutoEventListener listener) {
|
||||
if (eventName == null || listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<ShutoEventListener> listeners = eventListeners.get(eventName);
|
||||
if (listeners != null) {
|
||||
listeners.remove(listener);
|
||||
LOG.d(TAG, "Removed listener for event: " + eventName + ", remaining listeners: " + listeners.size());
|
||||
|
||||
if (listeners.isEmpty()) {
|
||||
eventListeners.remove(eventName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(ShutoEventListener listener) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Set<ShutoEventListener> listeners : eventListeners.values()) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
eventListeners.entrySet().removeIf(entry -> entry.getValue().isEmpty());
|
||||
LOG.d(TAG, "Removed listener from all events");
|
||||
}
|
||||
|
||||
public void fireEvent(String eventName, Map<String, Object> data) {
|
||||
if (eventName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.d(TAG, "Firing event: " + eventName + ", data: " + data);
|
||||
|
||||
Set<ShutoEventListener> listeners = eventListeners.get(eventName);
|
||||
if (listeners != null && !listeners.isEmpty()) {
|
||||
for (ShutoEventListener listener : listeners) {
|
||||
listener.onEvent(eventName, data);
|
||||
}
|
||||
} else {
|
||||
LOG.d(TAG, "No listeners for event: " + eventName);
|
||||
}
|
||||
}
|
||||
|
||||
public void fireEvent(String eventName) {
|
||||
fireEvent(eventName, null);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#import <Cordova/CDV.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
#import "SGGC_Log.h"
|
||||
#import "ShutoEventManager.h"
|
||||
|
||||
@protocol ShutoApiProtocol <NSObject>
|
||||
- (void)fireEvent:(NSString*)type parameters:(NSDictionary*)parameters;
|
||||
@@ -16,6 +17,8 @@
|
||||
NSDictionary* _userInfo;
|
||||
// 保存带回调的事件回调ID映射
|
||||
NSMutableDictionary* _eventCallbacks;
|
||||
// 标识前端是否就绪
|
||||
BOOL _isIonicReady;
|
||||
}
|
||||
|
||||
- (void)close:(CDVInvokedUrlCommand*)command;
|
||||
@@ -26,6 +29,8 @@
|
||||
- (void)fireEvent:(NSString*)type parameters:(NSDictionary*)parameters;
|
||||
- (void)fireEventWithCallback:(NSString*)eventName parameters:(NSDictionary*)parameters callback:(void (^)(NSDictionary* result, NSError* error))callback;
|
||||
- (void)eventCallback:(CDVInvokedUrlCommand*)command;
|
||||
- (void)ionicReady:(CDVInvokedUrlCommand*)command;
|
||||
- (BOOL)isIonicReady;
|
||||
@end
|
||||
|
||||
@implementation ShutoApi
|
||||
@@ -209,4 +214,20 @@
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (void)ionicReady:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
_isIonicReady = YES;
|
||||
[SGGC_Log log:@"SGGC_CDVFile" message:@"ShutoApi: Ionic is ready"];
|
||||
|
||||
[[ShutoEventManager sharedInstance] fireEvent:@"ionicReady" data:nil];
|
||||
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (BOOL)isIonicReady
|
||||
{
|
||||
return _isIonicReady;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
27
src/ios/ShutoEventManager.h
Normal file
27
src/ios/ShutoEventManager.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// ShutoEventManager.h
|
||||
// ShutoApi
|
||||
//
|
||||
// Created by Shuto Team.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol ShutoEventListener <NSObject>
|
||||
- (void)onEvent:(NSString *)eventName data:(NSDictionary *)data;
|
||||
@end
|
||||
|
||||
@interface ShutoEventManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)addListenerForEvent:(NSString *)eventName listener:(id<ShutoEventListener>)listener;
|
||||
- (void)removeListenerForEvent:(NSString *)eventName listener:(id<ShutoEventListener>)listener;
|
||||
- (void)removeListener:(id<ShutoEventListener>)listener;
|
||||
- (void)fireEvent:(NSString *)eventName data:(NSDictionary *)data;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
114
src/ios/ShutoEventManager.m
Normal file
114
src/ios/ShutoEventManager.m
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// ShutoEventManager.m
|
||||
// ShutoApi
|
||||
//
|
||||
// Created by Shuto Team.
|
||||
//
|
||||
|
||||
#import "ShutoEventManager.h"
|
||||
#import "SGGC_Log.h"
|
||||
|
||||
@interface ShutoEventManager ()
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSMutableSet<id<ShutoEventListener>> *> *eventListeners;
|
||||
@end
|
||||
|
||||
@implementation ShutoEventManager
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static ShutoEventManager *instance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
instance = [[ShutoEventManager alloc] init];
|
||||
});
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_eventListeners = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addListenerForEvent:(NSString *)eventName listener:(id<ShutoEventListener>)listener {
|
||||
if (!eventName || !listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized(self.eventListeners) {
|
||||
if (!self.eventListeners[eventName]) {
|
||||
self.eventListeners[eventName] = [[NSMutableSet alloc] init];
|
||||
}
|
||||
[self.eventListeners[eventName] addObject:listener];
|
||||
[SGGC_Log log:@"ShutoEventManager" message:[NSString stringWithFormat:@"Added listener for event: %@, total listeners: %lu", eventName, (unsigned long)self.eventListeners[eventName].count]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeListenerForEvent:(NSString *)eventName listener:(id<ShutoEventListener>)listener {
|
||||
if (!eventName || !listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized(self.eventListeners) {
|
||||
if (self.eventListeners[eventName]) {
|
||||
[self.eventListeners[eventName] removeObject:listener];
|
||||
[SGGC_Log log:@"ShutoEventManager" message:[NSString stringWithFormat:@"Removed listener for event: %@, remaining listeners: %lu", eventName, (unsigned long)self.eventListeners[eventName].count]];
|
||||
|
||||
if (self.eventListeners[eventName].count == 0) {
|
||||
[self.eventListeners removeObjectForKey:eventName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeListener:(id<ShutoEventListener>)listener {
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
|
||||
@synchronized(self.eventListeners) {
|
||||
NSMutableArray *emptyEvents = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSString *eventName in self.eventListeners.allKeys) {
|
||||
NSMutableSet *listeners = self.eventListeners[eventName];
|
||||
if ([listeners containsObject:listener]) {
|
||||
[listeners removeObject:listener];
|
||||
[SGGC_Log log:@"ShutoEventManager" message:[NSString stringWithFormat:@"Removed listener from event: %@, remaining listeners: %lu", eventName, (unsigned long)listeners.count]];
|
||||
|
||||
if (listeners.count == 0) {
|
||||
[emptyEvents addObject:eventName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self.eventListeners removeObjectsForKeys:emptyEvents];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fireEvent:(NSString *)eventName data:(NSDictionary *)data {
|
||||
if (!eventName) {
|
||||
return;
|
||||
}
|
||||
|
||||
[SGGC_Log log:@"ShutoEventManager" message:[NSString stringWithFormat:@"Firing event: %@, data: %@", eventName, data]];
|
||||
|
||||
NSArray *listeners = nil;
|
||||
@synchronized(self.eventListeners) {
|
||||
if (self.eventListeners[eventName]) {
|
||||
listeners = [self.eventListeners[eventName] allObjects];
|
||||
}
|
||||
}
|
||||
|
||||
if (listeners && listeners.count > 0) {
|
||||
for (id<ShutoEventListener> listener in listeners) {
|
||||
if ([listener respondsToSelector:@selector(onEvent:data:)]) {
|
||||
[listener onEvent:eventName data:data];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
[SGGC_Log log:@"ShutoEventManager" message:[NSString stringWithFormat:@"No listeners for event: %@", eventName]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -12,6 +12,11 @@ var ShutoApi = {
|
||||
// 执行带回调的事件
|
||||
executeEvent: function (eventName, params, success, error) {
|
||||
exec(success, error, 'ShutoApi', 'executeEvent', [eventName, params]);
|
||||
},
|
||||
|
||||
// 标识前端已就绪
|
||||
ionicReady: function (success, error) {
|
||||
exec(success, error, 'ShutoApi', 'ionicReady', []);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user