Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
e4ddb287d5 Fix some issues
Update core
2025-09-24 21:46:14 +08:00
19 changed files with 66 additions and 124 deletions

View File

@@ -27,9 +27,9 @@ jobs:
- platform: macos
os: macos-latest
arch: arm64
# - platform: windows
# os: windows-11-arm
# arch: arm64
- platform: windows
os: windows-11-arm
arch: arm64
- platform: linux
os: ubuntu-24.04-arm
arch: arm64

16
.gitignore vendored
View File

@@ -45,19 +45,11 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
/android/**/.cxx
/android/**/build
/android/common/**/.**/
/android/common/local.*
/android/core/**/includes/
/android/core/**/cmake-build-*/
/android/core/**/jniLibs/
#FlClash
#libclash
/libclash/
/android/app/src/main/jniLibs/
/services/helper/target
/macos/**/Package.resolved
devtools_options.yaml
#jniLibs
/android/app/src/main/jniLibs/

View File

@@ -1,13 +1,3 @@
## v0.8.89
- Fix some issues
- Optimize Windows service mode
- Update core
- Update changelog
## v0.8.88
- Add android separates the core process

View File

@@ -66,7 +66,6 @@
<activity
android:name=".TempActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@style/TransparentTheme">
<intent-filter>

View File

@@ -92,7 +92,6 @@ object State {
suspend fun destroyServiceEngine() {
runLock.withLock {
GlobalState.log("Destroy service engine")
withContext(Dispatchers.Main) {
runCatching {
serviceFlutterEngine?.destroy()
@@ -104,12 +103,10 @@ object State {
suspend fun startServiceWithEngine() {
runLock.withLock {
if (runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) {
if (serviceFlutterEngine != null || runStateFlow.value == RunState.PENDING || runStateFlow.value == RunState.START) {
return
}
GlobalState.log("Create service engine")
withContext(Dispatchers.Main) {
serviceFlutterEngine?.destroy()
serviceFlutterEngine = FlutterEngine(GlobalState.application)
serviceFlutterEngine?.plugins?.add(ServicePlugin())
serviceFlutterEngine?.plugins?.add(AppPlugin())

View File

@@ -30,6 +30,6 @@ class TempActivity : Activity(),
}
}
}
finish()
finishAndRemoveTask()
}
}

View File

@@ -33,8 +33,6 @@ var (
)
func handleInitClash(paramsString string) bool {
runLock.Lock()
defer runLock.Unlock()
var params = InitParams{}
err := json.Unmarshal([]byte(paramsString), &params)
if err != nil {

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
class Debouncer {
@@ -69,7 +68,7 @@ Future<T> retry<T>({
required Future<T> Function() task,
int maxAttempts = 3,
required bool Function(T res) retryIf,
Duration delay = midDuration,
Duration delay = Duration.zero,
}) async {
int attempts = 0;
while (attempts < maxAttempts) {
@@ -79,7 +78,7 @@ Future<T> retry<T>({
}
attempts++;
}
throw 'retry error';
throw 'unknown error';
}
final debouncer = Debouncer();

View File

@@ -186,26 +186,26 @@ class Windows {
logLevel: LogLevel.warning,
);
if (result <= 32) {
if (result < 42) {
return false;
}
return true;
}
// Future<void> _killProcess(int port) async {
// final result = await Process.run('netstat', ['-ano']);
// final lines = result.stdout.toString().trim().split('\n');
// for (final line in lines) {
// if (!line.contains(':$port') || !line.contains('LISTENING')) {
// continue;
// }
// final parts = line.trim().split(RegExp(r'\s+'));
// final pid = int.tryParse(parts.last);
// if (pid != null) {
// await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
// }
// }
// }
Future<void> _killProcess(int port) async {
final result = await Process.run('netstat', ['-ano']);
final lines = result.stdout.toString().trim().split('\n');
for (final line in lines) {
if (!line.contains(':$port') || !line.contains('LISTENING')) {
continue;
}
final parts = line.trim().split(RegExp(r'\s+'));
final pid = int.tryParse(parts.last);
if (pid != null) {
await Process.run('taskkill', ['/PID', pid.toString(), '/F']);
}
}
}
Future<WindowsHelperServiceStatus> checkService() async {
// final qcResult = await Process.run('sc', ['qc', appHelperService]);
@@ -231,18 +231,16 @@ class Windows {
return true;
}
await _killProcess(helperPort);
final command = [
'/c',
if (status == WindowsHelperServiceStatus.presence) ...[
'taskkill',
'/F',
'/IM',
'$appHelperService.exe'
' & '
'sc',
'sc',
'delete',
appHelperService,
'&',
'/force',
'&&',
],
'sc',
'create',
@@ -258,12 +256,8 @@ class Windows {
final res = runas('cmd.exe', command);
await Future.delayed(Duration(milliseconds: 300));
final retryStatus = await retry(
task: checkService,
retryIf: (status) => status == WindowsHelperServiceStatus.running,
delay: commonDuration,
);
return res && retryStatus == WindowsHelperServiceStatus.running;
return res;
}
Future<bool> registerTask(String appName) async {

View File

@@ -322,15 +322,12 @@ class Utils {
return SingleActivator(trigger, control: control, meta: !control);
}
FutureOr<T> handleWatch<T>({
required Function function,
required void Function(T data, int elapsedMilliseconds) onWatch,
}) async {
FutureOr<T> handleWatch<T>(Function function) async {
if (kDebugMode) {
final stopwatch = Stopwatch()..start();
final res = await function();
stopwatch.stop();
onWatch(res, stopwatch.elapsedMilliseconds);
commonPrint.log('耗时:${stopwatch.elapsedMilliseconds} ms');
return res;
}
return await function();

View File

@@ -962,7 +962,7 @@ class AppController {
final res = await futureFunction();
return res;
} catch (e) {
commonPrint.log('$title===> $e', logLevel: LogLevel.warning);
commonPrint.log('$futureFunction ===> $e', logLevel: LogLevel.warning);
if (realSilence) {
globalState.showNotifier(e.toString());
} else {

View File

@@ -5,7 +5,6 @@ import 'dart:isolate';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/foundation.dart';
mixin CoreInterface {
Future<bool> init(InitParams params);
@@ -87,18 +86,7 @@ abstract class CoreHandlerInterface with CoreInterface {
Duration? timeout,
}) async {
await connected;
if (kDebugMode) {
commonPrint.log('Invoke ${method.name} ${DateTime.now()} $data');
}
return utils.handleWatch(
function: () async {
return await invoke(method: method, data: data, timeout: timeout);
},
onWatch: (data, elapsedMilliseconds) {
commonPrint.log('Invoke ${method.name} ${elapsedMilliseconds}ms');
},
);
return invoke(method: method, data: data, timeout: timeout);
}
Future<T?> invoke<T>({
@@ -157,7 +145,7 @@ abstract class CoreHandlerInterface with CoreInterface {
@override
Future<Result> getConfig(String path) async {
return await _invoke<Result>(method: ActionMethod.getConfig, data: path) ??
Result.success({});
Result<Map<String, dynamic>>.success({});
}
@override

View File

@@ -38,29 +38,23 @@ class CoreService extends CoreHandlerInterface {
completer?.complete(data);
}
Future<void> _initServer() async {
final server = await retry(
task: () async {
try {
final address = !system.isWindows
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
: InternetAddress(localhost, type: InternetAddressType.IPv4);
await _deleteSocketFile();
final server = await ServerSocket.bind(address, 0, shared: true);
server.listen((socket) async {
await _attachSocket(socket);
});
return server;
} catch (_) {
return null;
void _initServer() {
runZonedGuarded(
() async {
final address = !system.isWindows
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
: InternetAddress(localhost, type: InternetAddressType.IPv4);
await _deleteSocketFile();
final server = await ServerSocket.bind(address, 0, shared: true);
_serverCompleter.complete(server);
await for (final socket in server) {
await _attachSocket(socket);
}
},
retryIf: (server) => server == null,
(error, stack) async {
commonPrint.log('Service error: $error', logLevel: LogLevel.warning);
},
);
if (server == null) {
exit(0);
}
_serverCompleter.complete(server);
}
Future<void> _attachSocket(Socket socket) async {

View File

@@ -33,14 +33,15 @@ Future<void> _service(List<String> flags) async {
},
),
);
app?.tip(appLocalizations.startVpn);
final version = await system.version;
await coreController.init(version);
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
enable: false,
);
coreController.setupConfig(clashConfig).then((_) async {
Future(() async {
app?.tip(appLocalizations.startVpn);
final version = await system.version;
await coreController.init(version);
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
enable: false,
);
await globalState.handleStart();
await coreController.setupConfig(clashConfig);
});
}

View File

@@ -192,7 +192,7 @@ extension ActionResultExt on ActionResult {
if (code == ResultType.success) {
return Result.success(data);
} else {
return Result.error('$data');
return Result.error(data);
}
}
}

View File

@@ -304,13 +304,7 @@ class GlobalState {
Future<void> genConfigFile(ClashConfig pathConfig) async {
final configFilePath = await appPath.configFilePath;
var config = {};
try {
config = await patchRawConfig(patchConfig: pathConfig);
} catch (e) {
globalState.showNotifier(e.toString());
config = {};
}
final config = await patchRawConfig(patchConfig: pathConfig);
final res = await Isolate.run<String>(() async {
try {
final res = json.encode(config);

View File

@@ -147,8 +147,7 @@ class ProviderItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 4),
if (provider.updateAt.microsecondsSinceEpoch > 0)
Text(_buildProviderDesc()),
Text(_buildProviderDesc()),
const SizedBox(height: 4),
if (provider.subscriptionInfo != null)
SubscriptionInfoView(subscriptionInfo: provider.subscriptionInfo),

View File

@@ -1,7 +1,7 @@
name: fl_clash
description: A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.
publish_to: 'none'
version: 0.8.90+2025100101
version: 0.8.89+2025092402
environment:
sdk: '>=3.8.0 <4.0.0'

View File

@@ -15,8 +15,8 @@ SolidCompression=yes
SetupIconFile={{SETUP_ICON_FILE}}
WizardStyle=modern
PrivilegesRequired={{PRIVILEGES_REQUIRED}}
ArchitecturesAllowed=x64 arm64
ArchitecturesInstallIn64BitMode=x64 arm64
ArchitecturesAllowed={{ARCH}}
ArchitecturesInstallIn64BitMode={{ARCH}}
[Code]
procedure KillProcesses;