From af361a086ace28384c963d50fe27b770e377c822 Mon Sep 17 00:00:00 2001 From: chen08209 Date: Fri, 25 Apr 2025 19:21:53 +0800 Subject: [PATCH] Optimize startTun performance --- .../clash/services/FlClashVpnService.kt | 47 +++++++++++++------ core/lib_android.go | 19 ++++---- lib/enum/enum.dart | 1 + lib/fragments/config/network.dart | 11 +++-- .../dashboard/widgets/start_button.dart | 23 +++++---- lib/models/clash_config.dart | 1 + .../generated/clash_config.freezed.dart | 27 ++++++++++- lib/models/generated/clash_config.g.dart | 2 + lib/models/generated/selector.freezed.dart | 41 ++++++++++++---- lib/models/selector.dart | 1 + lib/providers/generated/state.g.dart | 2 +- lib/providers/state.dart | 8 ++-- lib/state.dart | 9 ++-- pubspec.yaml | 2 +- 14 files changed, 143 insertions(+), 51 deletions(-) diff --git a/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt b/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt index d8742f5..1e4881f 100644 --- a/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt +++ b/android/app/src/main/kotlin/com/follow/clash/services/FlClashVpnService.kt @@ -34,6 +34,10 @@ class FlClashVpnService : VpnService(), BaseServiceInterface { if (options.ipv4Address.isNotEmpty()) { val cidr = options.ipv4Address.toCIDR() addAddress(cidr.address, cidr.prefixLength) + Log.d( + "addAddress", + "address: ${cidr.address} prefixLength:${cidr.prefixLength}" + ) val routeAddress = options.getIpv4RouteAddress() if (routeAddress.isNotEmpty()) { try { @@ -50,26 +54,39 @@ class FlClashVpnService : VpnService(), BaseServiceInterface { } else { addRoute("0.0.0.0", 0) } + } else { + addRoute("0.0.0.0", 0) } - if (options.ipv6Address.isNotEmpty()) { - val cidr = options.ipv6Address.toCIDR() - addAddress(cidr.address, cidr.prefixLength) - val routeAddress = options.getIpv6RouteAddress() - if (routeAddress.isNotEmpty()) { - try { - routeAddress.forEach { i -> - Log.d( - "addRoute6", - "address: ${i.address} prefixLength:${i.prefixLength}" - ) - addRoute(i.address, i.prefixLength) + try { + if (options.ipv6Address.isNotEmpty()) { + val cidr = options.ipv6Address.toCIDR() + Log.d( + "addAddress6", + "address: ${cidr.address} prefixLength:${cidr.prefixLength}" + ) + addAddress(cidr.address, cidr.prefixLength) + val routeAddress = options.getIpv6RouteAddress() + if (routeAddress.isNotEmpty()) { + try { + routeAddress.forEach { i -> + Log.d( + "addRoute6", + "address: ${i.address} prefixLength:${i.prefixLength}" + ) + addRoute(i.address, i.prefixLength) + } + } catch (_: Exception) { + addRoute("::", 0) } - } catch (_: Exception) { + } else { addRoute("::", 0) } - } else { - addRoute("::", 0) } + }catch (_:Exception){ + Log.d( + "addAddress6", + "IPv6 is not supported." + ) } addDnsServer(options.dnsServerAddress) setMtu(9000) diff --git a/core/lib_android.go b/core/lib_android.go index 97587de..9ed8541 100644 --- a/core/lib_android.go +++ b/core/lib_android.go @@ -98,13 +98,13 @@ func handleStopTun() { } } -func handleStartTun(fd int, callback unsafe.Pointer) bool { +func handleStartTun(fd int, callback unsafe.Pointer) { handleStopTun() + tunLock.Lock() + defer tunLock.Unlock() now := time.Now() runTime = &now if fd != 0 { - tunLock.Lock() - defer tunLock.Unlock() tunHandler = &TunHandler{ callback: callback, limit: semaphore.NewWeighted(4), @@ -113,13 +113,11 @@ func handleStartTun(fd int, callback unsafe.Pointer) bool { tunListener, _ := t.Start(fd, currentConfig.General.Tun.Device, currentConfig.General.Tun.Stack) if tunListener != nil { log.Infoln("TUN address: %v", tunListener.Address()) + tunHandler.listener = tunListener } else { removeTunHook() - return false } - tunHandler.listener = tunListener } - return true } func handleGetRunTime() string { @@ -228,7 +226,10 @@ func quickStart(initParamsChar *C.char, paramsChar *C.char, stateParamsChar *C.c //export startTUN func startTUN(fd C.int, callback unsafe.Pointer) bool { - return handleStartTun(int(fd), callback) + go func() { + handleStartTun(int(fd), callback) + }() + return true } //export getRunTime @@ -238,7 +239,9 @@ func getRunTime() *C.char { //export stopTun func stopTun() { - handleStopTun() + go func() { + handleStopTun() + }() } //export getCurrentProfileName diff --git a/lib/enum/enum.dart b/lib/enum/enum.dart index 595ec5b..ba196e3 100644 --- a/lib/enum/enum.dart +++ b/lib/enum/enum.dart @@ -293,6 +293,7 @@ enum WindowsHelperServiceStatus { enum DebounceTag { updateClashConfig, + updateStatus, updateGroups, addCheckIpNum, applyProfile, diff --git a/lib/fragments/config/network.dart b/lib/fragments/config/network.dart index 1826ad9..10f6a82 100644 --- a/lib/fragments/config/network.dart +++ b/lib/fragments/config/network.dart @@ -301,8 +301,11 @@ class RouteAddressItem extends ConsumerWidget { title: appLocalizations.routeAddress, widget: Consumer( builder: (_, ref, __) { - final routeAddress = ref.watch(patchClashConfigProvider - .select((state) => state.tun.routeAddress)); + final routeAddress = ref.watch( + patchClashConfigProvider.select( + (state) => state.tun.routeAddress, + ), + ); return ListInputPage( title: appLocalizations.routeAddress, items: routeAddress, @@ -371,7 +374,9 @@ class NetworkListView extends ConsumerWidget { return; } ref.read(vpnSettingProvider.notifier).updateState( - (state) => defaultVpnProps, + (state) => defaultVpnProps.copyWith( + accessControl: state.accessControl, + ), ); ref.read(patchClashConfigProvider.notifier).updateState( (state) => state.copyWith( diff --git a/lib/fragments/dashboard/widgets/start_button.dart b/lib/fragments/dashboard/widgets/start_button.dart index 350b36c..4e56c54 100644 --- a/lib/fragments/dashboard/widgets/start_button.dart +++ b/lib/fragments/dashboard/widgets/start_button.dart @@ -1,4 +1,5 @@ import 'package:fl_clash/common/common.dart'; +import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/providers/providers.dart'; import 'package:fl_clash/state.dart'; @@ -35,11 +36,15 @@ class _StartButtonState extends State } handleSwitchStart() { - if (isStart == globalState.appState.isStart) { - isStart = !isStart; - updateController(); - globalState.appController.updateStatus(isStart); - } + isStart = !isStart; + updateController(); + debouncer.call( + DebounceTag.updateStatus, + () { + globalState.appController.updateStatus(isStart); + }, + duration: moreDuration, + ); } updateController() { @@ -126,9 +131,11 @@ class _StartButtonState extends State final text = utils.getTimeText(runTime); return Text( text, - style: Theme.of(context).textTheme.titleMedium?.toSoftBold.copyWith( - color: context.colorScheme.onPrimaryContainer - ), + style: Theme.of(context) + .textTheme + .titleMedium + ?.toSoftBold + .copyWith(color: context.colorScheme.onPrimaryContainer), ); }, ), diff --git a/lib/models/clash_config.dart b/lib/models/clash_config.dart index 9b40834..23209c6 100644 --- a/lib/models/clash_config.dart +++ b/lib/models/clash_config.dart @@ -147,6 +147,7 @@ class Tun with _$Tun { const factory Tun({ @Default(false) bool enable, @Default(appName) String device, + @JsonKey(name: "auto-route") @Default(false) bool autoRoute, @Default(TunStack.gvisor) TunStack stack, @JsonKey(name: "dns-hijack") @Default(["any:53"]) List dnsHijack, @JsonKey(name: "route-address") @Default([]) List routeAddress, diff --git a/lib/models/generated/clash_config.freezed.dart b/lib/models/generated/clash_config.freezed.dart index 43c9b43..388afa0 100644 --- a/lib/models/generated/clash_config.freezed.dart +++ b/lib/models/generated/clash_config.freezed.dart @@ -660,6 +660,8 @@ Tun _$TunFromJson(Map json) { mixin _$Tun { bool get enable => throw _privateConstructorUsedError; String get device => throw _privateConstructorUsedError; + @JsonKey(name: "auto-route") + bool get autoRoute => throw _privateConstructorUsedError; TunStack get stack => throw _privateConstructorUsedError; @JsonKey(name: "dns-hijack") List get dnsHijack => throw _privateConstructorUsedError; @@ -683,6 +685,7 @@ abstract class $TunCopyWith<$Res> { $Res call( {bool enable, String device, + @JsonKey(name: "auto-route") bool autoRoute, TunStack stack, @JsonKey(name: "dns-hijack") List dnsHijack, @JsonKey(name: "route-address") List routeAddress}); @@ -704,6 +707,7 @@ class _$TunCopyWithImpl<$Res, $Val extends Tun> implements $TunCopyWith<$Res> { $Res call({ Object? enable = null, Object? device = null, + Object? autoRoute = null, Object? stack = null, Object? dnsHijack = null, Object? routeAddress = null, @@ -717,6 +721,10 @@ class _$TunCopyWithImpl<$Res, $Val extends Tun> implements $TunCopyWith<$Res> { ? _value.device : device // ignore: cast_nullable_to_non_nullable as String, + autoRoute: null == autoRoute + ? _value.autoRoute + : autoRoute // ignore: cast_nullable_to_non_nullable + as bool, stack: null == stack ? _value.stack : stack // ignore: cast_nullable_to_non_nullable @@ -742,6 +750,7 @@ abstract class _$$TunImplCopyWith<$Res> implements $TunCopyWith<$Res> { $Res call( {bool enable, String device, + @JsonKey(name: "auto-route") bool autoRoute, TunStack stack, @JsonKey(name: "dns-hijack") List dnsHijack, @JsonKey(name: "route-address") List routeAddress}); @@ -760,6 +769,7 @@ class __$$TunImplCopyWithImpl<$Res> extends _$TunCopyWithImpl<$Res, _$TunImpl> $Res call({ Object? enable = null, Object? device = null, + Object? autoRoute = null, Object? stack = null, Object? dnsHijack = null, Object? routeAddress = null, @@ -773,6 +783,10 @@ class __$$TunImplCopyWithImpl<$Res> extends _$TunCopyWithImpl<$Res, _$TunImpl> ? _value.device : device // ignore: cast_nullable_to_non_nullable as String, + autoRoute: null == autoRoute + ? _value.autoRoute + : autoRoute // ignore: cast_nullable_to_non_nullable + as bool, stack: null == stack ? _value.stack : stack // ignore: cast_nullable_to_non_nullable @@ -795,6 +809,7 @@ class _$TunImpl implements _Tun { const _$TunImpl( {this.enable = false, this.device = appName, + @JsonKey(name: "auto-route") this.autoRoute = false, this.stack = TunStack.gvisor, @JsonKey(name: "dns-hijack") final List dnsHijack = const ["any:53"], @@ -813,6 +828,9 @@ class _$TunImpl implements _Tun { @JsonKey() final String device; @override + @JsonKey(name: "auto-route") + final bool autoRoute; + @override @JsonKey() final TunStack stack; final List _dnsHijack; @@ -835,7 +853,7 @@ class _$TunImpl implements _Tun { @override String toString() { - return 'Tun(enable: $enable, device: $device, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress)'; + return 'Tun(enable: $enable, device: $device, autoRoute: $autoRoute, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress)'; } @override @@ -845,6 +863,8 @@ class _$TunImpl implements _Tun { other is _$TunImpl && (identical(other.enable, enable) || other.enable == enable) && (identical(other.device, device) || other.device == device) && + (identical(other.autoRoute, autoRoute) || + other.autoRoute == autoRoute) && (identical(other.stack, stack) || other.stack == stack) && const DeepCollectionEquality() .equals(other._dnsHijack, _dnsHijack) && @@ -858,6 +878,7 @@ class _$TunImpl implements _Tun { runtimeType, enable, device, + autoRoute, stack, const DeepCollectionEquality().hash(_dnsHijack), const DeepCollectionEquality().hash(_routeAddress)); @@ -882,6 +903,7 @@ abstract class _Tun implements Tun { const factory _Tun( {final bool enable, final String device, + @JsonKey(name: "auto-route") final bool autoRoute, final TunStack stack, @JsonKey(name: "dns-hijack") final List dnsHijack, @JsonKey(name: "route-address") final List routeAddress}) = @@ -894,6 +916,9 @@ abstract class _Tun implements Tun { @override String get device; @override + @JsonKey(name: "auto-route") + bool get autoRoute; + @override TunStack get stack; @override @JsonKey(name: "dns-hijack") diff --git a/lib/models/generated/clash_config.g.dart b/lib/models/generated/clash_config.g.dart index a7f4351..1def52e 100644 --- a/lib/models/generated/clash_config.g.dart +++ b/lib/models/generated/clash_config.g.dart @@ -66,6 +66,7 @@ Map _$$RuleProviderImplToJson(_$RuleProviderImpl instance) => _$TunImpl _$$TunImplFromJson(Map json) => _$TunImpl( enable: json['enable'] as bool? ?? false, device: json['device'] as String? ?? appName, + autoRoute: json['auto-route'] as bool? ?? false, stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? TunStack.gvisor, dnsHijack: (json['dns-hijack'] as List?) @@ -81,6 +82,7 @@ _$TunImpl _$$TunImplFromJson(Map json) => _$TunImpl( Map _$$TunImplToJson(_$TunImpl instance) => { 'enable': instance.enable, 'device': instance.device, + 'auto-route': instance.autoRoute, 'stack': _$TunStackEnumMap[instance.stack]!, 'dns-hijack': instance.dnsHijack, 'route-address': instance.routeAddress, diff --git a/lib/models/generated/selector.freezed.dart b/lib/models/generated/selector.freezed.dart index 0e18577..34db08f 100644 --- a/lib/models/generated/selector.freezed.dart +++ b/lib/models/generated/selector.freezed.dart @@ -3522,6 +3522,7 @@ mixin _$ClashConfigState { bool get overrideDns => throw _privateConstructorUsedError; ClashConfig get clashConfig => throw _privateConstructorUsedError; OverrideData get overrideData => throw _privateConstructorUsedError; + RouteMode get routeMode => throw _privateConstructorUsedError; /// Create a copy of ClashConfigState /// with the given fields replaced by the non-null parameter values. @@ -3537,7 +3538,10 @@ abstract class $ClashConfigStateCopyWith<$Res> { _$ClashConfigStateCopyWithImpl<$Res, ClashConfigState>; @useResult $Res call( - {bool overrideDns, ClashConfig clashConfig, OverrideData overrideData}); + {bool overrideDns, + ClashConfig clashConfig, + OverrideData overrideData, + RouteMode routeMode}); $ClashConfigCopyWith<$Res> get clashConfig; $OverrideDataCopyWith<$Res> get overrideData; @@ -3561,6 +3565,7 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState> Object? overrideDns = null, Object? clashConfig = null, Object? overrideData = null, + Object? routeMode = null, }) { return _then(_value.copyWith( overrideDns: null == overrideDns @@ -3575,6 +3580,10 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState> ? _value.overrideData : overrideData // ignore: cast_nullable_to_non_nullable as OverrideData, + routeMode: null == routeMode + ? _value.routeMode + : routeMode // ignore: cast_nullable_to_non_nullable + as RouteMode, ) as $Val); } @@ -3608,7 +3617,10 @@ abstract class _$$ClashConfigStateImplCopyWith<$Res> @override @useResult $Res call( - {bool overrideDns, ClashConfig clashConfig, OverrideData overrideData}); + {bool overrideDns, + ClashConfig clashConfig, + OverrideData overrideData, + RouteMode routeMode}); @override $ClashConfigCopyWith<$Res> get clashConfig; @@ -3632,6 +3644,7 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res> Object? overrideDns = null, Object? clashConfig = null, Object? overrideData = null, + Object? routeMode = null, }) { return _then(_$ClashConfigStateImpl( overrideDns: null == overrideDns @@ -3646,6 +3659,10 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res> ? _value.overrideData : overrideData // ignore: cast_nullable_to_non_nullable as OverrideData, + routeMode: null == routeMode + ? _value.routeMode + : routeMode // ignore: cast_nullable_to_non_nullable + as RouteMode, )); } } @@ -3656,7 +3673,8 @@ class _$ClashConfigStateImpl implements _ClashConfigState { const _$ClashConfigStateImpl( {required this.overrideDns, required this.clashConfig, - required this.overrideData}); + required this.overrideData, + required this.routeMode}); @override final bool overrideDns; @@ -3664,10 +3682,12 @@ class _$ClashConfigStateImpl implements _ClashConfigState { final ClashConfig clashConfig; @override final OverrideData overrideData; + @override + final RouteMode routeMode; @override String toString() { - return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData)'; + return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData, routeMode: $routeMode)'; } @override @@ -3680,12 +3700,14 @@ class _$ClashConfigStateImpl implements _ClashConfigState { (identical(other.clashConfig, clashConfig) || other.clashConfig == clashConfig) && (identical(other.overrideData, overrideData) || - other.overrideData == overrideData)); + other.overrideData == overrideData) && + (identical(other.routeMode, routeMode) || + other.routeMode == routeMode)); } @override - int get hashCode => - Object.hash(runtimeType, overrideDns, clashConfig, overrideData); + int get hashCode => Object.hash( + runtimeType, overrideDns, clashConfig, overrideData, routeMode); /// Create a copy of ClashConfigState /// with the given fields replaced by the non-null parameter values. @@ -3701,7 +3723,8 @@ abstract class _ClashConfigState implements ClashConfigState { const factory _ClashConfigState( {required final bool overrideDns, required final ClashConfig clashConfig, - required final OverrideData overrideData}) = _$ClashConfigStateImpl; + required final OverrideData overrideData, + required final RouteMode routeMode}) = _$ClashConfigStateImpl; @override bool get overrideDns; @@ -3709,6 +3732,8 @@ abstract class _ClashConfigState implements ClashConfigState { ClashConfig get clashConfig; @override OverrideData get overrideData; + @override + RouteMode get routeMode; /// Create a copy of ClashConfigState /// with the given fields replaced by the non-null parameter values. diff --git a/lib/models/selector.dart b/lib/models/selector.dart index fe66525..11d1d83 100644 --- a/lib/models/selector.dart +++ b/lib/models/selector.dart @@ -228,6 +228,7 @@ class ClashConfigState with _$ClashConfigState { required bool overrideDns, required ClashConfig clashConfig, required OverrideData overrideData, + required RouteMode routeMode, }) = _ClashConfigState; } diff --git a/lib/providers/generated/state.g.dart b/lib/providers/generated/state.g.dart index 2e2c04f..9ba4d17 100644 --- a/lib/providers/generated/state.g.dart +++ b/lib/providers/generated/state.g.dart @@ -78,7 +78,7 @@ final coreStateProvider = AutoDisposeProvider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element typedef CoreStateRef = AutoDisposeProviderRef; -String _$clashConfigStateHash() => r'848f6b2f734d99fb11ec05f73d614be415e9658f'; +String _$clashConfigStateHash() => r'fbbcd7221b0b9b18db523e59c9021e8e56e119ca'; /// See also [clashConfigState]. @ProviderFor(clashConfigState) diff --git a/lib/providers/state.dart b/lib/providers/state.dart index 2c5f111..8410c69 100644 --- a/lib/providers/state.dart +++ b/lib/providers/state.dart @@ -75,13 +75,15 @@ CoreState coreState(Ref ref) { ClashConfigState clashConfigState(Ref ref) { final clashConfig = ref.watch(patchClashConfigProvider); final overrideDns = ref.watch(overrideDnsProvider); - final overrideData = ref.watch(currentProfileProvider.select( - (state) => state?.overrideData, - )); + final overrideData = + ref.watch(currentProfileProvider.select((state) => state?.overrideData)); + final routeMode = + ref.watch(networkSettingProvider.select((state) => state.routeMode)); return ClashConfigState( overrideDns: overrideDns, clashConfig: clashConfig, overrideData: overrideData ?? OverrideData(), + routeMode: routeMode, ); } diff --git a/lib/state.dart b/lib/state.dart index 9840020..221aaf4 100644 --- a/lib/state.dart +++ b/lib/state.dart @@ -258,14 +258,17 @@ class GlobalState { getUpdateConfigParams([bool? isPatch]) { final currentProfile = config.currentProfile; final clashConfig = config.patchClashConfig; + final routeAddress = + config.networkProps.routeMode == RouteMode.bypassPrivate + ? defaultBypassPrivateRouteAddress + : clashConfig.tun.routeAddress; return UpdateConfigParams( profileId: config.currentProfileId ?? "", config: clashConfig.copyWith( globalUa: ua, tun: clashConfig.tun.copyWith( - routeAddress: config.networkProps.routeMode == RouteMode.bypassPrivate - ? defaultBypassPrivateRouteAddress - : clashConfig.tun.routeAddress, + autoRoute: routeAddress.isEmpty ? true : false, + routeAddress: routeAddress, ), rule: currentProfile?.overrideData.runningRule ?? [], ), diff --git a/pubspec.yaml b/pubspec.yaml index baf07ab..628579c 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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.83+202504252 +version: 0.8.83+202504254 environment: sdk: '>=3.1.0 <4.0.0'