⚠️ 注意:本项目处于积极更新中,各项 API 可能会有变更。
一个基于阿里云 HTTPDNS的 Flutter 插件,提供智能域名解析和代理功能,支持 HTTP/1.1 和 HTTP/2 协议。
代码参考了Flutter接入移动解析HTTPDNS Android/iOS SDK实践方案和已经标记为discontinued(截止2025-09-01)的aliyun_httpdns。
支持平台: Android、iOS
支持解析域名对应的 IP,也支持代理网络请求并解析域名。
网络请求示意图:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Flutter App │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Dio │ │ HttpClient │ │ Other │ │ Other │ │
│ │ Client │ │ │ │ │ │ Clients │ │
│ └─────┬───────┘ └─────┬───────┘ └─────┬───────┘ └─────┬───────┘ │
│ │ │ │ │ │
│ └────────────────┼────────────────┼────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ Local Proxy Server (localhost:4041) │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ Request │ │ DNS Resolver │ │ Response │ │ │
│ │ │ Parser │ │ │ │ Forwarder │ │ │
│ │ │ │ │ ┌─────────────┐ │ │ │ │ │
│ │ │ • 解析请求协议 │ │ │ Cache │ │ │ • 转发响应 │ │ │
│ │ │ • 提取域名端口 │ │ │ │ │ │ • 错误处理 │ │ │
│ │ │ • 识别协议类型 │ │ │ • IP缓存 │ │ │ • 日志记录 │ │ │
│ │ └─────┬───────────┘ │ │ • TTL管理 │ │ └─────────────────┘ │ │
│ │ │ │ └─────┬───────┘ │ │ │
│ │ ▼ │ ▼ │ │ │
│ │ ┌─────────────────┐ │ ┌─────────────┐ │ ┌─────────────────┐ │ │
│ │ │ Protocol │ │ │ HTTPDNS │ │ │ Connection │ │ │
│ │ │ Detector │ │ │ Resolver │ │ │ Manager │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ • HTTP/1.1 │ │ │ • 阿里云 │ │ │ • 连接管理 │ │ │
│ │ │ • HTTP/2 │ │ │ HTTPDNS │ │ │ • 连接复用 │ │ │
│ │ │ • HTTPS │ │ │ • 系统DNS │ │ │ • 错误重试 │ │ │
│ │ │ • HTTP/2 │ │ │ 回退 │ │ │ • 超时处理 │ │ │
│ │ └─────────────────┘ │ └─────────────┘ │ └─────────────────┘ │ │
│ └─────────────────────────┼──────────────────┘ │ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────────────────────────────────────────────────────────────────────┐ │
│ │ Target Server Connection │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ Socket │ │ Request │ │ Response │ │ │
│ │ │ Connection │ │ Forwarding │ │ Processing │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ • 连接目标IP │ │ • 修改Host头 │ │ • 接收响应 │ │ │
│ │ │ • 处理连接错误 │ │ • 转发原始请求 │ │ • 解析响应头 │ │ │
│ │ │ • 重试机制 │ │ • 保持请求头 │ │ • 转发给客户端 │ │ │
│ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Internet │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 阿里云 │ │ 目标 │ │ 目标 │ │ 目标 │ │
│ │ HTTPDNS │ │ 服务器1 │ │ 服务器2 │ │ 服务器N │ │
│ │ 服务 │ │ (API) │ │ (CDN) │ │ (静态) │ │
│ │ │ │ │ │ │ │ │ │
│ │ • 域名解析 │ │ • 业务逻辑 │ │ • 内容分发 │ │ • 静态资源 │ │
│ │ • IP优化 │ │ • 数据处理 │ │ • 媒体流 │ │ • 图片文件 │ │
│ │ • 负载均衡 │ │ • 响应生成 │ │ • 缓存压缩 │ │ • CSS/JS │ │
│ │ • 故障转移 │ │ • 数据库查询 │ │ • 边缘计算 │ │ • 文档下载 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────┘
📋 详细流程说明(点击展开)
- Flutter 应用中的各种 HTTP 客户端(Dio、HttpClient 等)发起网络请求
- 请求被配置为通过本地代理服务器(localhost:4041)
- 请求解析器:解析 HTTP/HTTPS 请求,提取目标域名和端口
- 协议检测器:自动识别请求协议类型(HTTP/1.1、HTTP/2、HTTPS)
- DNS 解析器:使用阿里云 HTTPDNS 服务解析域名对应的 IP 地址
- 缓存检查:首先检查本地缓存中是否有有效的 IP 地址
- HTTPDNS 解析:调用阿里云 HTTPDNS 服务进行域名解析
- 系统 DNS 回退:如果 HTTPDNS 解析失败,自动回退到系统 DNS
- 结果缓存:将解析结果缓存到本地,提升后续请求速度
- Socket 连接:使用解析得到的 IP 地址建立到目标服务器的连接
- 请求转发:修改请求头中的 Host 信息,转发原始请求到目标服务器
- 响应处理:接收目标服务器的响应,转发回客户端
- 连接管理:管理 Socket 连接,支持连接复用和错误重试
- 错误处理:处理网络错误、超时、重试等异常情况,返回布尔值而非抛出异常
- 日志记录:记录详细的请求处理日志,便于调试和监控
- 智能缓存:DNS 解析结果缓存,减少重复解析
- 连接复用:复用 Socket 连接,减少连接建立开销
- 协议优化:支持 HTTP/2 协议,提升传输效率
- 负载均衡:利用阿里云 HTTPDNS 的基础负载均衡能力
- HTTPDNS 解析: 通过阿里云 HTTPDNS 服务解析域名,提升解析速度和准确性
- 智能代理: 自动启动本地代理服务器,支持 HTTP/1.1 和 HTTP/2 协议
- 端口管理: 智能端口分配和冲突处理,无需手动管理
- 多客户端支持: 支持 Dio、HttpClient 等主流 HTTP 客户端,支持 HTTP/1.1 和 HTTP/2 协议
- 智能缓存: 独立的缓存管理器,支持动态启用/禁用缓存,提升性能
dependencies:
flutter_ali_http_dns:
git:
url: https://github.com/jixiaoyong/flutter_ali_http_dns
ref: master # 或指定版本标签,如 v0.0.1# TODO: 插件发布到 pub.dev 后使用此方式
dependencies:
flutter_ali_http_dns: ^0.0.1如果你的应用需要支持 HTTP 协议(非HTTPS),必须在 ios/Runner/Info.plist 文件中添加以下配置:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>插件应该在用户同意隐私协议之后,第一次请求网络之前进行初始化, 在应用不再使用或退出时调用 dispose() 方法:
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late final FlutterAliHttpDns _dnsService;
bool _isPrivacyAccepted = false;
@override
void initState() {
super.initState();
_dnsService = FlutterAliHttpDns();
}
/// 用户同意隐私协议后初始化插件
Future<void> _initializeAfterPrivacyAccept() async {
if (_isPrivacyAccepted) {
await _dnsService.initialize(DnsConfig(
accountId: 'your_account_id',
accessKeyId: 'your_access_key_id',
accessKeySecret: 'your_access_key_secret',
));
print('DNS服务初始化完成');
}
}
@override
void dispose() {
// 应用退出时清理资源
_dnsService.dispose();
super.dispose();
}
}如果只需要域名解析功能,不需要代理服务器:
import 'package:flutter_ali_http_dns/flutter_ali_http_dns.dart';
// 1. 初始化DNS服务
final dnsService = FlutterAliHttpDns();
await dnsService.initialize(DnsConfig(
accountId: 'your_account_id',
accessKeyId: 'your_access_key_id',
accessKeySecret: 'your_access_key_secret',
));
// 2. 解析域名(无需启动代理)
final ip = await dnsService.resolveDomain('www.example.com');
print('解析结果: $ip');
// 3. 应用退出时清理资源
@override
void dispose() {
dnsService.dispose();
super.dispose();
}// 1. 启动代理服务器(仅在需要代理时启动)
final success = await dnsService.startProxy();
if (success) {
print('代理服务器启动成功');
} else {
print('代理服务器启动失败');
}
// 2. 配置HttpClient使用代理
final client = HttpClient();
final success = await dnsService.configureHttpClient(client);
if (success) {
print('HttpClient配置成功');
} else {
print('HttpClient配置失败');
}
// 3. 发起请求
final request = await client.getUrl(Uri.parse('https://www.example.com'));
final response = await request.close();
// 4. 如果不再需要代理,可以停止代理服务器
// await dnsService.stopProxy();import 'package:dio/dio.dart';
import 'package:dio/io.dart';
// 启动代理
final success = await dnsService.startProxy();
if (success) {
print('代理服务器启动成功');
} else {
print('代理服务器启动失败');
}
// 配置Dio使用代理
final dio = Dio();
final proxyConfig = await dnsService.getDioProxyConfig();
if (proxyConfig != null) {
dio.httpClientAdapter = IOHttpClientAdapter(
createHttpClient: () {
final client = HttpClient();
client.findProxy = (uri) => 'PROXY ${proxyConfig['host']}:${proxyConfig['port']}';
return client;
},
);
}
// 发起请求
final response = await dio.get('https://www.example.com');SDK 会在本地启动一个代理服务器,转发网络请求,你可以配置这个代理服务器的端口范围,SDK 会优先遵循,但是如果指定限制下无法找到可用端口,也可能突破这个限制。
// 自定义端口池和端口范围
final proxyConfig = ProxyConfig(
portPool: [4041, 4042, 4043], // 优先使用的端口
startPort: 4044, // 自动分配起始端口
endPort: 4050, // 自动分配结束端口
enabled: true,
host: 'localhost',
);
final success = await dnsService.startProxy(proxyConfig);
if (success) {
print('代理服务器启动成功');
} else {
print('代理服务器启动失败');
}配置属性参考云解析 DNS/解析服务/移动解析 HTTPDNS/开发参考
final dnsConfig = DnsConfig(
accountId: 'your_account_id',
accessKeyId: 'your_access_key_id',
accessKeySecret: 'your_access_key_secret',
enableCache: true, // 启用缓存
maxCacheSize: 1000, // 最大缓存条目数
maxNegativeCache: 30, // 最大否定缓存时间(秒)
enableSpeedTest: true, // 启用测速
timeout: 5, // 超时时间(秒)
enableIPv6: false, // 是否启用IPv6
enableShort: false, // 是否启用短连接
preloadDomains: [ // 预加载域名
'www.example.com',
'api.example.com',
],
keepAliveDomains: [ // 缓存保持域名
'www.example.com',
],
maxCacheTTL: 3600, // 最大缓存TTL时间(秒)
ispEnable: true, // 启用ISP网络区分
speedPort: 80, // 测速端口
);
await dnsService.initialize(dnsConfig);插件提供了独立的缓存管理器,支持动态缓存控制:
// 检查缓存状态
bool isEnabled = dnsService.isCacheEnabled;
int currentSize = dnsService.cacheSize;
int maxSize = dnsService.maxCacheSize;
// 动态切换缓存状态
await dnsService.setEnableCache(false); // 禁用缓存
await dnsService.setEnableCache(true); // 启用缓存
// 清除缓存
await dnsService.clearCache(); // 清除所有缓存
await dnsService.clearCache(['example.com']); // 清除指定域名缓存
// 缓存状态监控
print('缓存状态: ${dnsService.isCacheEnabled ? "启用" : "禁用"}');
print('缓存使用: ${dnsService.cacheSize}/${dnsService.maxCacheSize}');// 获取当前使用的端口
final mainPort = await dnsService.getMainPort();
final allPorts = await dnsService.getActualPorts();
// 检查端口可用性
final isAvailable = await dnsService.isPortAvailable(4041);
// 获取代理地址
final proxyAddress = await dnsService.getProxyAddress();
final http2Address = await dnsService.getHttp2ProxyAddress();// 设置日志级别
FlutterAliHttpDns.setLogLevel(LogLevel.info);
// 启用/禁用日志
FlutterAliHttpDns.setLogEnabled(true);// 检查代理状态
final isRunning = dnsService.isProxyRunning;
final isInitialized = dnsService.isInitialized;
// 停止代理
final success = await dnsService.stopProxy();
if (success) {
print('代理服务器已停止');
} else {
print('停止代理服务器失败');
}
// 检查代理状态
final status = await dnsService.checkProxyStatus();class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late final FlutterAliHttpDns _dnsService;
bool _isInitialized = false;
bool _isProxyRunning = false;
@override
void initState() {
super.initState();
_dnsService = FlutterAliHttpDns();
}
/// 初始化DNS服务(在用户同意隐私协议后调用)
Future<void> _initializeDns() async {
try {
final success = await _dnsService.initialize(DnsConfig(
accountId: 'your_account_id',
accessKeyId: 'your_access_key_id',
accessKeySecret: 'your_access_key_secret',
));
if (success) {
setState(() => _isInitialized = true);
print('DNS服务初始化成功');
}
} catch (e) {
print('DNS服务初始化失败: $e');
}
}
/// 启动代理服务器(仅在需要代理时调用)
Future<void> _startProxy() async {
if (!_isInitialized) {
print('请先初始化DNS服务');
return;
}
try {
final success = await _dnsService.startProxy();
if (success) {
setState(() => _isProxyRunning = true);
print('代理服务器启动成功');
}
} catch (e) {
print('代理服务器启动失败: $e');
}
}
/// 停止代理服务器
Future<void> _stopProxy() async {
try {
final success = await _dnsService.stopProxy();
if (success) {
setState(() => _isProxyRunning = false);
print('代理服务器已停止');
} else {
print('停止代理服务器失败');
}
} catch (e) {
print('停止代理服务器失败: $e');
}
}
@override
void dispose() {
// 应用退出时清理所有资源
_dnsService.dispose();
super.dispose();
}
}class NetworkService {
final FlutterAliHttpDns _dnsService = FlutterAliHttpDns();
bool _isProxyStarted = false;
/// 初始化DNS服务
Future<void> initialize() async {
await _dnsService.initialize(DnsConfig(
accountId: 'your_account_id',
accessKeyId: 'your_access_key_id',
accessKeySecret: 'your_access_key_secret',
));
}
/// 按需启动代理(仅在需要代理时调用)
Future<void> _ensureProxyStarted() async {
if (!_isProxyStarted) {
final success = await _dnsService.startProxy();
if (success) {
_isProxyStarted = true;
}
}
}
/// 使用代理发起HTTP请求
Future<void> makeHttpRequest() async {
await _ensureProxyStarted(); // 按需启动代理
final client = HttpClient();
final success = await _dnsService.configureHttpClient(client);
if (success) {
// 发起请求...
} else {
print('配置HttpClient失败');
}
}
/// 仅解析域名(无需代理)
Future<String> resolveDomain(String domain) async {
return await _dnsService.resolveDomain(domain);
}
/// 清理资源
void dispose() {
_dnsService.dispose();
}
}// 批量解析域名,无需启动代理服务器
final domains = ['www.taobao.com', 'www.douyin.com', 'www.baidu.com'];
final results = <String, String>{};
for (final domain in domains) {
try {
final ip = await dnsService.resolveDomain(domain);
results[domain] = ip;
print('$domain -> $ip');
} catch (e) {
print('解析失败: $domain - $e');
}
}当需要绕过HTTPDNS直接使用系统DNS解析时,可以使用 resolveWithSystemDns 方法:
// 使用系统DNS解析域名(默认只返回公网IP)
final ip = await dnsService.resolveWithSystemDns('www.example.com');
if (ip != null) {
print('系统DNS解析结果: $ip');
} else {
print('系统DNS解析失败');
}
// 对比HTTPDNS和系统DNS的解析结果
final httpDnsResult = await dnsService.resolveDomain('www.example.com');
final systemDnsResult = await dnsService.resolveWithSystemDns('www.example.com');
print('HTTPDNS结果: $httpDnsResult');
print('系统DNS结果: $systemDnsResult');
// 调试模式:获取所有IP地址(包括私有地址)
final allIps = await dnsService.resolveWithSystemDns('www.example.com', onlyPublicIp: false);
print('所有IP地址: $allIps');参数说明:
domain: 要解析的域名onlyPublicIp: 是否只返回公网IP地址,默认为truetrue: 过滤私有地址、回环地址、多播地址等,只返回可公网访问的IPfalse: 返回所有解析到的IP地址,包括私有地址(用于调试)
使用场景:
- 网络调试和对比测试
- 当HTTPDNS服务不可用时
- 需要验证域名解析结果的准确性
- 特殊网络环境下的备选方案
- 调试时获取完整的IP地址列表
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
accountId |
String |
- | 阿里云账号 ID |
accessKeyId |
String |
- | 访问密钥 ID |
accessKeySecret |
String |
- | 访问密钥 Secret |
enableCache |
bool |
true |
是否启用缓存 |
maxCacheSize |
int |
100 |
最大缓存大小 |
maxNegativeCache |
int |
30 |
最大否定缓存时间(秒) |
enableSpeedTest |
bool |
true |
是否启用速度测试 |
timeout |
int |
3 |
超时时间(秒) |
enableIPv6 |
bool |
false |
是否启用 IPv6 |
enableShort |
bool |
false |
是否启用短连接 |
preloadDomains |
List<String> |
[] |
预加载域名列表 |
keepAliveDomains |
List<String> |
[] |
缓存保持域名列表 |
maxCacheTTL |
int |
3600 |
最大缓存 TTL 时间(秒) |
ispEnable |
bool |
true |
是否启用 ISP 网络区分 |
speedPort |
int |
80 |
测速端口 |
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
portPool |
List<int>? |
null |
端口池(优先使用的端口列表) |
startPort |
int? |
4041 |
自动分配的起始端口 |
endPort |
int? |
startPort + 100 |
自动分配的结束端口 |
enabled |
bool |
true |
是否启用代理 |
host |
String |
'localhost' |
代理主机 |
// 初始化DNS服务
Future<bool> initialize(DnsConfig config)
// 启动代理服务器
Future<bool> startProxy({ProxyConfig config = const ProxyConfig()})
// 停止代理服务器
Future<bool> stopProxy()// 解析域名,失败时返回原域名
Future<String> resolveDomain(String domain, {bool enableSystemDnsFallback = true})
// 解析域名,失败时返回null
Future<String?> resolveDomainNullable(String domain, {bool enableSystemDnsFallback = true})
// 使用系统DNS解析域名(绕过HTTPDNS),默认过滤非公网地址
Future<String?> resolveWithSystemDns(String domain, {bool onlyPublicIp = true})// 动态开启或关闭缓存功能。此设置在服务初始化后依然可以随时修改。
Future<void> setEnableCache(bool enable)
// 清除缓存。不带参数时,清除所有域名的缓存;带参数时,清除指定域名列表的缓存。
Future<bool> clearCache([List<String>? hostNames])
// 检查缓存是否启用
bool get isCacheEnabled
// 获取当前缓存大小
int get cacheSize
// 获取最大缓存大小
int get maxCacheSize// 获取默认代理地址 (HTTP/1.1, HTTPS)
Future<String?> getProxyAddress()
// 获取HTTP/2代理地址
Future<String?> getHttp2ProxyAddress()
// 获取所有代理地址列表
Future<List<String>> getAllProxyAddresses()
// 获取代理配置字符串
Future<String?> getProxyConfigString()
// 获取Dio客户端所需的代理配置
Future<Map<String, dynamic>?> getDioProxyConfig()
// 检查代理服务器的运行状态
Future<bool> checkProxyStatus()// 获取主要端口(第一个端口)
Future<int?> getMainPort()
// 获取实际使用的端口列表
Future<List<int>> getActualPorts()
// 检查端口是否可用
Future<bool> isPortAvailable(int port)
// 为 HttpClient 配置代理
Future<bool> configureHttpClient(HttpClient client)// 检查是否已初始化
bool get isInitialized
// 检查代理是否正在运行
bool get isProxyRunning
// 获取当前代理服务器实例
ProxyServer? get currentProxyServer// 设置日志级别
static void setLogLevel(LogLevel level)
// 启用或禁用日志
static void setLogEnabled(bool enabled)// 清理资源(停止代理服务器并清理相关资源)
Future<void> dispose()- 初始化阶段: 调用
initialize()方法,配置 DNS 服务 - 域名解析: 可以直接使用
resolveDomain()进行域名解析,无需代理 - 代理启动: 按需调用
startProxy()启动代理服务器 - 资源清理: 调用
dispose()清理所有资源
- HTTPDNS 解析: 优先使用阿里云 HTTPDNS 服务解析域名
- 系统 DNS 回退: 如果 HTTPDNS 解析失败,自动回退到系统 DNS
- 缓存机制: 解析结果会被缓存,提升后续请求速度
- 按需启动: 仅在需要代理 HTTP 请求时才启动代理服务器
- 端口分配: 智能分配可用端口,自动处理端口冲突
- 协议支持: 支持 HTTP/1.1 和 HTTP/2 协议
- 请求转发: 将客户端请求转发到目标服务器
- 优先级 1: 使用配置的端口池中的端口
- 优先级 2: 使用配置的端口范围(startPort 到 endPort)
- 优先级 3: 自动寻找可用端口(突破配置范围)
- 冲突处理: 自动处理端口冲突
- 提升域名解析速度
- 减少网络延迟
- 提高应用响应速度
- 统一 Android 和 iOS 的网络处理逻辑
- 简化网络配置管理
- 本地代理便于网络调试
- 支持多种 HTTP 客户端
查看 example/ 目录获取完整的使用示例:
- 基础测试 (
basic_tests.dart): 域名解析、HttpClient、Dio 测试 - HTTP/2 测试 (
http2_tests.dart): HTTP/2 协议支持测试 - 高级功能测试 (
advanced_tests.dart): 端口管理、代理配置、性能测试、错误处理测试
cd example
flutter pub get
flutter run示例项目使用 path: ../ 依赖,因为它与插件在同一仓库中。如果您在自己的项目中使用,请参考上面的"添加依赖"部分,使用 GitHub 依赖方式。
A: 使用getMainPort()获取默认代理端口,或使用getProxyAddress()获取代理地址。
A: SDK 会自动处理端口冲突,如果指定端口被占用,会自动寻找下一个可用端口。
A: 是的,当调用stopProxy()时,所有端口都会被自动清理。
A: 插件提供基础的连接管理功能,包括连接复用和错误重试,确保网络请求的稳定性和性能。连接管理采用简化的实现方式,专注于核心的网络代理功能。
A: 使用isPortAvailable(int port)方法检查端口是否可用。
A: 插件利用阿里云 HTTPDNS 的基础负载均衡能力,通过智能的 IP 选择来提升网络请求的性能和稳定性。负载均衡功能主要依赖阿里云 HTTPDNS 服务的原生能力。
A: 支持 Dio、HttpClient 等主流 HTTP 客户端,支持 HTTP/1.1 和 HTTP/2 协议,也可以自定义配置其他客户端。目前主要专注于 HTTP/HTTPS 协议的支持。
A: 建议在用户同意隐私协议之后,第一次请求网络之前进行初始化。这样可以确保在用户同意的情况下才开始网络活动。初始化后可以通过检查返回值来判断是否成功。
A: 代理服务器应该按需启动。如果只需要域名解析功能,无需启动代理;只有在需要代理 HTTP 请求时才启动代理服务器。启动后可以通过检查返回值来判断是否成功。
A: 在应用退出或不再使用插件时调用 dispose()方法,它会自动停止代理服务器并清理所有资源。
A: 不需要。域名解析功能不需要代理服务器,只有 HTTP 请求代理才需要启动代理服务器。域名解析方法会返回解析结果或原域名,不会抛出异常。
A: 使用 resolveWithSystemDns(String domain, {bool onlyPublicIp = true}) 方法可以直接使用系统DNS解析域名,绕过HTTPDNS服务。默认情况下只返回公网IP地址,过滤掉私有地址、回环地址等无效地址。设置 onlyPublicIp: false 可以获取所有IP地址用于调试。这个方法适用于网络调试、对比测试或当HTTPDNS服务不可用时的备选方案。解析成功返回IP地址,失败返回null。
项目包含自动版本号同步的 Git hooks,确保 pubspec.yaml 和 README.md 中的版本号始终保持一致。
# 安装 Git hooks(首次克隆项目后运行)
./scripts/install-hooks.sh- 自动检测:当修改
pubspec.yaml中的版本号时,会自动检查README.md中的版本号 - 自动同步:如果版本号不一致,会自动同步并添加到当前提交中
- 提交保护:如果同步失败,提交会被阻止
# 手动检查版本号同步
./scripts/sync-version.shMIT License