improve network request

This commit is contained in:
2024-11-11 17:18:56 +08:00
parent 7cf55fcb8e
commit 6ae3e50a5b
3 changed files with 64 additions and 50 deletions

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
@@ -176,6 +177,8 @@ class AppDio with DioMixin {
return res; return res;
} }
static final Map<String, bool> _requests = {};
@override @override
Future<Response<T>> request<T>( Future<Response<T>> request<T>(
String path, { String path, {
@@ -186,6 +189,13 @@ class AppDio with DioMixin {
ProgressCallback? onSendProgress, ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress, ProgressCallback? onReceiveProgress,
}) async { }) async {
if(options?.headers?['prevent-parallel'] == 'true') {
while(_requests.containsKey(path)) {
await Future.delayed(const Duration(milliseconds: 20));
}
_requests[path] = true;
options!.headers!.remove('prevent-parallel');
}
proxy = await getProxy(); proxy = await getProxy();
if (_proxy != proxy) { if (_proxy != proxy) {
Log.info("Network", "Proxy changed to $proxy"); Log.info("Network", "Proxy changed to $proxy");
@@ -196,7 +206,7 @@ class AppDio with DioMixin {
: rhttp.ProxySettings.proxy(proxy!), : rhttp.ProxySettings.proxy(proxy!),
)); ));
} }
return super.request( var res = super.request<T>(
path, path,
data: data, data: data,
queryParameters: queryParameters, queryParameters: queryParameters,
@@ -205,6 +215,10 @@ class AppDio with DioMixin {
onSendProgress: onSendProgress, onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress, onReceiveProgress: onReceiveProgress,
); );
if(_requests.containsKey(path)) {
_requests.remove(path);
}
return res;
} }
} }

View File

@@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:typed_data';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
class NetworkCache { class NetworkCache {
@@ -43,7 +42,7 @@ class NetworkCacheManager implements Interceptor {
static const _maxCacheSize = 10 * 1024 * 1024; static const _maxCacheSize = 10 * 1024 * 1024;
void setCache(NetworkCache cache) { void setCache(NetworkCache cache) {
while(size > _maxCacheSize){ while (size > _maxCacheSize) {
size -= _cache.values.first.size; size -= _cache.values.first.size;
_cache.remove(_cache.keys.first); _cache.remove(_cache.keys.first);
} }
@@ -53,7 +52,7 @@ class NetworkCacheManager implements Interceptor {
void removeCache(Uri uri) { void removeCache(Uri uri) {
var cache = _cache[uri]; var cache = _cache[uri];
if(cache != null){ if (cache != null) {
size -= cache.size; size -= cache.size;
} }
_cache.remove(uri); _cache.remove(uri);
@@ -64,41 +63,29 @@ class NetworkCacheManager implements Interceptor {
size = 0; size = 0;
} }
var preventParallel = <Uri, Completer>{};
@override @override
void onError(DioException err, ErrorInterceptorHandler handler) { void onError(DioException err, ErrorInterceptorHandler handler) {
if(err.requestOptions.method != "GET"){ if (err.requestOptions.method != "GET") {
return handler.next(err); return handler.next(err);
} }
if(preventParallel[err.requestOptions.uri] != null){
preventParallel[err.requestOptions.uri]!.complete();
preventParallel.remove(err.requestOptions.uri);
}
return handler.next(err); return handler.next(err);
} }
@override @override
void onRequest( void onRequest(
RequestOptions options, RequestInterceptorHandler handler) async { RequestOptions options, RequestInterceptorHandler handler) async {
if(options.method != "GET"){ if (options.method != "GET") {
return handler.next(options); return handler.next(options);
} }
if(preventParallel[options.uri] != null){
await preventParallel[options.uri]!.future;
}
var cache = getCache(options.uri); var cache = getCache(options.uri);
if (cache == null || !compareHeaders(options.headers, cache.requestHeaders)) { if (cache == null ||
if(options.headers['cache-time'] != null){ !compareHeaders(options.headers, cache.requestHeaders)) {
if (options.headers['cache-time'] != null) {
options.headers.remove('cache-time'); options.headers.remove('cache-time');
} }
if(options.headers['prevent-parallel'] != null){
options.headers.remove('prevent-parallel');
preventParallel[options.uri] = Completer();
}
return handler.next(options); return handler.next(options);
} else { } else {
if(options.headers['cache-time'] == 'no'){ if (options.headers['cache-time'] == 'no') {
options.headers.remove('cache-time'); options.headers.remove('cache-time');
removeCache(options.uri); removeCache(options.uri);
return handler.next(options); return handler.next(options);
@@ -106,20 +93,21 @@ class NetworkCacheManager implements Interceptor {
} }
var time = DateTime.now(); var time = DateTime.now();
var diff = time.difference(cache.time); var diff = time.difference(cache.time);
if (options.headers['cache-time'] == 'long' if (options.headers['cache-time'] == 'long' &&
&& diff < const Duration(hours: 2)) { diff < const Duration(hours: 2)) {
return handler.resolve(Response( return handler.resolve(Response(
requestOptions: options, requestOptions: options,
data: cache.data, data: cache.data,
headers: Headers.fromMap(cache.responseHeaders), headers: Headers.fromMap(cache.responseHeaders)
..set('venera-cache', 'true'),
statusCode: 200, statusCode: 200,
)); ));
} } else if (diff < const Duration(seconds: 5)) {
else if (diff < const Duration(seconds: 5)) {
return handler.resolve(Response( return handler.resolve(Response(
requestOptions: options, requestOptions: options,
data: cache.data, data: cache.data,
headers: Headers.fromMap(cache.responseHeaders), headers: Headers.fromMap(cache.responseHeaders)
..set('venera-cache', 'true'),
statusCode: 200, statusCode: 200,
)); ));
} else if (diff < const Duration(hours: 1)) { } else if (diff < const Duration(hours: 1)) {
@@ -133,7 +121,8 @@ class NetworkCacheManager implements Interceptor {
return handler.resolve(Response( return handler.resolve(Response(
requestOptions: options, requestOptions: options,
data: cache.data, data: cache.data,
headers: Headers.fromMap(cache.responseHeaders), headers: Headers.fromMap(cache.responseHeaders)
..set('venera-cache', 'true'),
statusCode: 200, statusCode: 200,
)); ));
} }
@@ -143,6 +132,10 @@ class NetworkCacheManager implements Interceptor {
} }
static bool compareHeaders(Map<String, dynamic> a, Map<String, dynamic> b) { static bool compareHeaders(Map<String, dynamic> a, Map<String, dynamic> b) {
a.remove('cache-time');
a.remove('prevent-parallel');
b.remove('cache-time');
b.remove('prevent-parallel');
if (a.length != b.length) { if (a.length != b.length) {
return false; return false;
} }
@@ -160,11 +153,11 @@ class NetworkCacheManager implements Interceptor {
if (response.requestOptions.method != "GET") { if (response.requestOptions.method != "GET") {
return handler.next(response); return handler.next(response);
} }
if(response.statusCode != null && response.statusCode! >= 400){ if (response.statusCode != null && response.statusCode! >= 400) {
return handler.next(response); return handler.next(response);
} }
var size = _calculateSize(response.data); var size = _calculateSize(response.data);
if(size != null && size < 1024 * 1024 && size > 0) { if (size != null && size < 1024 * 1024 && size > 0) {
var cache = NetworkCache( var cache = NetworkCache(
uri: response.requestOptions.uri, uri: response.requestOptions.uri,
requestHeaders: response.requestOptions.headers, requestHeaders: response.requestOptions.headers,
@@ -175,30 +168,29 @@ class NetworkCacheManager implements Interceptor {
); );
setCache(cache); setCache(cache);
} }
if(preventParallel[response.requestOptions.uri] != null){
preventParallel[response.requestOptions.uri]!.complete();
preventParallel.remove(response.requestOptions.uri);
}
handler.next(response); handler.next(response);
} }
static int? _calculateSize(Object? data){ static int? _calculateSize(Object? data) {
if(data == null){ if (data == null) {
return 0; return 0;
} }
if(data is List<int>) { if (data is List<int>) {
return data.length; return data.length;
} }
if(data is String) { if (data is Uint8List) {
if(data.trim().isEmpty){ return data.length;
}
if (data is String) {
if (data.trim().isEmpty) {
return 0; return 0;
} }
if(data.length < 512 && data.contains("IP address")){ if (data.length < 512 && data.contains("IP address")) {
return 0; return 0;
} }
return data.length * 4; return data.length * 4;
} }
if(data is Map) { if (data is Map) {
return data.toString().length * 4; return data.toString().length * 4;
} }
return null; return null;

View File

@@ -58,8 +58,9 @@ class ImageDownloader {
} }
} }
if (configs['onResponse'] != null) { if (configs['onResponse'] is JSInvokable) {
buffer = configs['onResponse'](buffer); buffer = (configs['onResponse'] as JSInvokable)([buffer]);
(configs['onResponse'] as JSInvokable).free();
} }
await CacheManager().writeCache(cacheKey, buffer); await CacheManager().writeCache(cacheKey, buffer);
@@ -102,7 +103,7 @@ class ImageDownloader {
if (configs['onLoadFailed'] is JSInvokable) { if (configs['onLoadFailed'] is JSInvokable) {
onLoadFailed = () async { onLoadFailed = () async {
dynamic result = configs['onLoadFailed'](); dynamic result = (configs['onLoadFailed'] as JSInvokable)([]);
if (result is Future) { if (result is Future) {
result = await result; result = await result;
} }
@@ -135,8 +136,9 @@ class ImageDownloader {
} }
} }
if (configs['onResponse'] != null) { if (configs['onResponse'] is JSInvokable) {
buffer = configs['onResponse'](buffer); buffer = (configs['onResponse'] as JSInvokable)([buffer]);
(configs['onResponse'] as JSInvokable).free();
} }
var data = Uint8List.fromList(buffer); var data = Uint8List.fromList(buffer);
@@ -158,17 +160,23 @@ class ImageDownloader {
); );
return; return;
} catch (e) { } catch (e) {
if(retryLimit < 0 || onLoadFailed == null) { if (retryLimit < 0 || onLoadFailed == null) {
rethrow; rethrow;
} }
var newConfig = await onLoadFailed(); var newConfig = await onLoadFailed();
(configs['onLoadFailed'] as JSInvokable).free();
onLoadFailed = null; onLoadFailed = null;
if(newConfig == null) { if (newConfig == null) {
rethrow; rethrow;
} }
configs = newConfig; configs = newConfig;
retryLimit--; retryLimit--;
} }
finally {
if(onLoadFailed != null) {
(configs['onLoadFailed'] as JSInvokable).free();
}
}
} }
} }
} }