feat: unify native API invocation with async callbacks (#1476)

This commit is contained in:
Levi authored and GitHub committed 2026-09-08 23:47:38 +08:00
1 parent 481675107f
commit 0efc85d3c0
21 files changed
+1017 -184

No files matched your search

@@ -11,6 +11,7 @@ import 'task_event.dart';
class LibgopeedChannel implements LibgopeedInterface {
static const _channel = MethodChannel('gopeed.com/libgopeed');
final _taskEvents = StreamController<TaskEvent>.broadcast();
int _requestID = 0;
LibgopeedChannel() {
_channel.setMethodCallHandler((call) async {
@@ -50,7 +51,9 @@ class LibgopeedChannel implements LibgopeedInterface {
@override
Future<String> invoke(String method, String path, {String query = '', String body = ''}) async {
final requestID = _requestID++;
final result = await _channel.invokeMethod<String>('invoke', {
'requestID': requestID,
'method': method,
'path': path,
'query': query,
+10 -64
View File
@@ -1,10 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import '../ffi/libgopeed_bind.dart';
import '../ffi/libgopeed_worker.dart';
import 'api_server_state.dart';
import 'libgopeed_interface.dart';
@@ -12,51 +8,22 @@ import 'start_config.dart';
import 'task_event.dart';
class LibgopeedFFi implements LibgopeedInterface {
late LibgopeedBind _libgopeed;
LibgopeedFFi() {
_worker.taskEvents.listen(_onTaskEvent, onError: _taskEvents.addError);
}
final _taskEvents = StreamController<TaskEvent>.broadcast();
final _worker = LibgopeedWorker();
late final NativeCallable<Void Function(Pointer<Char>)> _taskEventCallback;
LibgopeedFFi(LibgopeedBind libgopeed) {
_libgopeed = libgopeed;
_taskEventCallback = NativeCallable<Void Function(Pointer<Char>)>.listener(_onTaskEvent);
}
void _onTaskEvent(Pointer<Char> payload) {
try {
final decoded = jsonDecode(payload.cast<Utf8>().toDartString()) as Map<String, dynamic>;
_taskEvents.add(TaskEvent.fromJson(decoded));
} finally {
_libgopeed.FreeCString(payload);
}
void _onTaskEvent(Map<String, dynamic> payload) {
_taskEvents.add(TaskEvent.fromJson(payload));
}
@override
Future<int> start(StartConfig cfg) {
var completer = Completer<int>();
final cfgPtr = jsonEncode(cfg).toNativeUtf8();
try {
final result = _libgopeed.Start(cfgPtr.cast());
if (result.r1 != nullptr) {
final message = result.r1.cast<Utf8>().toDartString();
_libgopeed.FreeCString(result.r1);
completer.completeError(Exception(message));
} else {
completer.complete(result.r0);
}
} finally {
malloc.free(cfgPtr);
}
return completer.future;
}
Future<int> start(StartConfig cfg) => _worker.start(jsonEncode(cfg));
@override
Future<void> stop() {
var completer = Completer<void>();
_libgopeed.Stop();
completer.complete();
return completer.future;
}
Future<void> stop() => _worker.stop();
Future<ApiServerOperationResult> _apiServerOperation(String operation) async {
final payload = await _worker.apiServer(operation);
@@ -77,33 +44,12 @@ class LibgopeedFFi implements LibgopeedInterface {
@override
Future<String> invoke(String method, String path, {String query = '', String body = ''}) async {
final methodPtr = method.toNativeUtf8();
final pathPtr = path.toNativeUtf8();
final queryPtr = query.toNativeUtf8();
final bodyPtr = body.toNativeUtf8();
Pointer<Char>? resultPtr;
try {
resultPtr = _libgopeed.Invoke(methodPtr.cast(), pathPtr.cast(), queryPtr.cast(), bodyPtr.cast());
if (resultPtr == nullptr) {
throw StateError('Gopeed Invoke returned a null response');
}
return resultPtr.cast<Utf8>().toDartString();
} finally {
malloc.free(methodPtr);
malloc.free(pathPtr);
malloc.free(queryPtr);
malloc.free(bodyPtr);
if (resultPtr != null && resultPtr != nullptr) {
_libgopeed.FreeCString(resultPtr);
}
}
return jsonEncode(await _worker.invoke(method, path, query, body));
}
@override
Stream<TaskEvent> get taskEvents => _taskEvents.stream;
@override
Future<void> subscribeTaskEvents(Set<TaskEventType> events) async {
_libgopeed.SubscribeTaskEvents(events.mask, events.isEmpty ? 0 : _taskEventCallback.nativeFunction.address);
}
Future<void> subscribeTaskEvents(Set<TaskEventType> events) => _worker.subscribeTaskEvents(events.mask);
}