merge: 合并 K54 iOS 网络修复
This commit is contained in:
commit
ae11846b63
3 files changed
+129
-26
No files matched your search
@@ -28,8 +28,41 @@
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
<key>NSExceptionDomains</key>
|
||||
<dict>
|
||||
<key>127.0.0.1</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>localhost</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>192.168.31.187</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>39.162.50.99</key>
|
||||
<dict>
|
||||
<key>NSExceptionAllowsInsecureHTTPLoads</key>
|
||||
<true/>
|
||||
<key>NSIncludesSubdomains</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>开发阶段需要访问同一局域网内的短剧内容服务。</string>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Author: fruit.com.cn
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -22,12 +24,17 @@ class AppConfig {
|
||||
/// 公网地址(远程/移动网络访问时使用)。
|
||||
static const String publicBaseUrl = 'http://39.162.50.99:8081';
|
||||
|
||||
static const String _loopbackBaseUrl = 'http://127.0.0.1:8081';
|
||||
|
||||
static const String _overrideBaseUrl = String.fromEnvironment('API_BASE_URL');
|
||||
|
||||
static String _runtimeBaseUrl = '';
|
||||
|
||||
static const Duration _probeTimeout = Duration(seconds: 2);
|
||||
static const List<String> _probeCandidates = [
|
||||
lanBaseUrl,
|
||||
devHostBaseUrl,
|
||||
publicBaseUrl,
|
||||
];
|
||||
|
||||
/// Build-time server URL supplied through `--dart-define=API_BASE_URL`.
|
||||
static String get compiledBaseUrl => _overrideBaseUrl;
|
||||
|
||||
@@ -40,7 +47,7 @@ class AppConfig {
|
||||
: (compiledBaseUrl.isNotEmpty ? compiledBaseUrl : defaultBaseUrl);
|
||||
|
||||
static final String _defaultBaseUrl = _runsOnDevMachine
|
||||
? _loopbackBaseUrl
|
||||
? lanBaseUrl
|
||||
: devHostBaseUrl;
|
||||
|
||||
/// 模拟器、桌面端与开发机同机,环回地址可用;iOS 真机不满足。
|
||||
@@ -66,29 +73,42 @@ class AppConfig {
|
||||
|
||||
static const Duration requestTimeout = Duration(seconds: 8);
|
||||
|
||||
/// 探测内网地址是否可达,可达则切换为内网直连,否则回落到公网地址。
|
||||
/// 按环回、开发机、公网顺序探测,写入首个返回 200 的地址。
|
||||
///
|
||||
/// Wi-Fi 下内网直连更快;移动数据下内网不可达,自动回落到公网端口映射。
|
||||
static Future<void> probeAndApplyBaseUrl() async {
|
||||
try {
|
||||
final client = HttpClient()
|
||||
..connectionTimeout = const Duration(seconds: 2);
|
||||
final request = await client.getUrl(
|
||||
Uri.parse('$lanBaseUrl/app/categories'),
|
||||
);
|
||||
final response = await request.close().timeout(const Duration(seconds: 2));
|
||||
await response.drain<void>();
|
||||
client.close(force: true);
|
||||
if (response.statusCode == 200) {
|
||||
applyRuntimeBaseUrl(lanBaseUrl);
|
||||
/// 全部不可达时使用平台默认地址,真机保留开发机地址,避免退回公网。
|
||||
static Future<void> probeAndApplyBaseUrl({
|
||||
Future<bool> Function(String baseUrl)? probe,
|
||||
String? fallbackBaseUrl,
|
||||
}) async {
|
||||
final candidateProbe = probe ?? _probeBaseUrl;
|
||||
for (final candidate in _probeCandidates) {
|
||||
if (await candidateProbe(candidate)) {
|
||||
applyRuntimeBaseUrl(candidate);
|
||||
return;
|
||||
}
|
||||
} catch (_) {
|
||||
// 内网不可达,回落到公网。
|
||||
}
|
||||
applyRuntimeBaseUrl(
|
||||
compiledBaseUrl.isNotEmpty ? compiledBaseUrl : publicBaseUrl,
|
||||
);
|
||||
applyRuntimeBaseUrl(fallbackBaseUrl ?? defaultBaseUrl);
|
||||
}
|
||||
|
||||
static Future<bool> _probeBaseUrl(String baseUrl) async {
|
||||
final client = HttpClient()..connectionTimeout = _probeTimeout;
|
||||
try {
|
||||
return await _requestCategories(
|
||||
client,
|
||||
Uri.parse('$baseUrl/app/categories'),
|
||||
).timeout(_probeTimeout);
|
||||
} catch (_) {
|
||||
return false;
|
||||
} finally {
|
||||
client.close(force: true);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> _requestCategories(HttpClient client, Uri uri) async {
|
||||
final request = await client.getUrl(uri);
|
||||
final response = await request.close();
|
||||
await response.drain<void>();
|
||||
return response.statusCode == 200;
|
||||
}
|
||||
|
||||
/// 后端返回的是相对路径(/files/...),这里补上服务地址。
|
||||
|
||||
@@ -60,6 +60,59 @@ void main() {
|
||||
expect(AppConfig.devHostBaseUrl, 'http://192.168.31.187:8081');
|
||||
});
|
||||
|
||||
test('should_keep_loopback_priority_when_probe_succeeds', () async {
|
||||
final attempts = <String>[];
|
||||
|
||||
await AppConfig.probeAndApplyBaseUrl(
|
||||
fallbackBaseUrl: AppConfig.devHostBaseUrl,
|
||||
probe: (baseUrl) async {
|
||||
attempts.add(baseUrl);
|
||||
return baseUrl == AppConfig.lanBaseUrl;
|
||||
},
|
||||
);
|
||||
|
||||
expect(attempts, [AppConfig.lanBaseUrl]);
|
||||
expect(AppConfig.apiBaseUrl, AppConfig.lanBaseUrl);
|
||||
});
|
||||
|
||||
test('should_probe_loopback_dev_host_then_public_in_order', () async {
|
||||
final attempts = <String>[];
|
||||
|
||||
await AppConfig.probeAndApplyBaseUrl(
|
||||
fallbackBaseUrl: AppConfig.devHostBaseUrl,
|
||||
probe: (baseUrl) async {
|
||||
attempts.add(baseUrl);
|
||||
return baseUrl == AppConfig.publicBaseUrl;
|
||||
},
|
||||
);
|
||||
|
||||
expect(attempts, [
|
||||
AppConfig.lanBaseUrl,
|
||||
AppConfig.devHostBaseUrl,
|
||||
AppConfig.publicBaseUrl,
|
||||
]);
|
||||
expect(AppConfig.apiBaseUrl, AppConfig.publicBaseUrl);
|
||||
});
|
||||
|
||||
test('should_use_platform_default_when_all_probes_fail', () async {
|
||||
final attempts = <String>[];
|
||||
|
||||
await AppConfig.probeAndApplyBaseUrl(
|
||||
fallbackBaseUrl: AppConfig.devHostBaseUrl,
|
||||
probe: (baseUrl) async {
|
||||
attempts.add(baseUrl);
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
expect(attempts, [
|
||||
AppConfig.lanBaseUrl,
|
||||
AppConfig.devHostBaseUrl,
|
||||
AppConfig.publicBaseUrl,
|
||||
]);
|
||||
expect(AppConfig.apiBaseUrl, AppConfig.devHostBaseUrl);
|
||||
});
|
||||
|
||||
test('should_apply_stored_url_only_when_debug_is_enabled', () async {
|
||||
SharedPreferencesAsyncPlatform.instance =
|
||||
InMemorySharedPreferencesAsync.withData({
|
||||
@@ -81,10 +134,7 @@ void main() {
|
||||
isNull,
|
||||
);
|
||||
|
||||
expect(
|
||||
await ServerEndpoint.save(' http://192.168.31.187:8081/ '),
|
||||
isTrue,
|
||||
);
|
||||
expect(await ServerEndpoint.save(' http://192.168.31.187:8081/ '), isTrue);
|
||||
expect(
|
||||
await SharedPreferencesAsync().getString(ServerEndpoint.preferenceKey),
|
||||
'http://192.168.31.187:8081',
|
||||
|
||||
Reference in new issue
Block a user