前言
Flutter 是一个单线程框架,使用 Future + Dio 的方式实现网络封装,仍然会占用UI线程,当调用频繁时,页面操作会出现卡顿现象。
处理方案
使用 BasicMessageChannel + Retrofit,封装原生层网络插件,开启子线程请求网络,不占用UI线程。
使用方式
- 添加引用
retrofit_plugin:
git:
url: https://github.com/liyufengrex/retrofit_plugin.git
ref: 0.0.1
- 网络配置初始化:
void main() {
NetWorkConfigBuild.init(); ///初始化网络配置
runApp(...省略...);
}
import 'package:retrofit_plugin/retrofit_plugin.dart';
abstract class NetWorkConfigBuild {
static void init() {
NetWorkConfigManager.addDefault(NetWorkConfig(
baseUrl: "http://api.wpbom.com/",
//公共头部
headers: {
"Authorization": "Bearer",
},
intercept: _Intercept(),
));
NetWorkConfigManager.addConfig(
"second",
NetWorkConfig(
baseUrl: "https://www.apiopen.top/",
headers: {},
intercept: _Intercept(),
));
}
}
///过滤器,主要用于log打印
class _Intercept extends NetworkIntercept {
@override
onResponse(Request request, Response response) {
if (response.success) {
print("log: ${request.toString()} - ${response.toString()}");
} else {
///系统错误
print("log-error: ${request.toString()} - ${response.toString()}");
}
}
}
每个 domainKey 对应独立的 netWorkConfig 配置,每个配置会生成独立的 retrofit 请求实例。如上样例中添加了两个网络配置,对应的 domainKey 分别是 default 和 second。
- 创建网络请求
继承 RequestBaseLoader ,泛型为返回值类型
import 'package:retrofit_plugin/retrofit_plugin.dart';
class PostRequest extends RequestBaseLoader<String> {
//重写 domainKey 匹配对应的网络层配置,不重写时,默认读取的是 domainKey 为 default 的网络配置
@override
String get domainKey => "second";
@override
Method get method => Method.Post;
@override
String get path => "likePoetry";
@override
Map<String, dynamic> get data => {
"name": "李白",
};
@override
Map<String, String> get header => {};
@override
Future<String> execute() {
return request().then((value) {
return value.toString();
});
}
}
- 使用网络请求
_post() async {
//PostRequest 为自定义类(需要继承 RequestBaseLoader)
PostRequest().execute().then((value) {
setState(() {
_data = value;
});
});
}
- 注意
android 清单文件,application标签需添加属性:
android:networkSecurityConfig="@xml/network_security_config"
demo已上传,可参考 https://github.com/liyufengrex/retrofit_plugin 内 example。
如下为插件内关键代码分析:
- 原生层不转换数据类型,直接返回字符串
mRetrofitBuilder.addConverterFactory(ScalarsConverterFactory.create());
mRetrofitBuilder.addConverterFactory(GsonConverterFactory.create());
- Retrofit 动态填充参数
@POST
fun doPost(
@Url url: String,
@HeaderMap headers: Map<String, String>,
@Body params: Map<String, @JvmSuppressWildcards Any>
): Observable<String>
@GET
fun doGet(
@Url url: String,
@HeaderMap headers: Map<String, String>,
@QueryMap(encoded=false) params: Map<String, @JvmSuppressWildcards Any>
): Observable<String>
- 使用 BasicMessageChannel 进行这种碎片化的连续交互
class RetrofitPlugin : FlutterPlugin, BasicMessageChannel.MessageHandler<Any> {
...省略
override fun onMessage(
message: Any?,
reply: BasicMessageChannel.Reply<Any>
) {
message?.let {
val mapParams = it as Map<String, Any>
val method = mapParams["method"] as String //区分 post get
val baseUrl = mapParams["baseUrl"] as String //根据 baseUrl 获取 Retrofit 实例
val pathUrl = mapParams["pathUrl"] as String
val headers = mapParams["headers"]
val params = mapParams["params"]
doRequest(
method = method,
baseUrl = baseUrl,
pathUrl = pathUrl,
headers = headers as Map<String, String>,
params = params as Map<String, Any>,
reply = reply
)
}
}
private fun doRequest(
method: String,
baseUrl: String,
pathUrl: String,
headers: Map<String, String>,
params: Map<String, Any>,
reply: BasicMessageChannel.Reply<Any>
) {
mRequestLoader.doRequest(
method,
baseUrl,
pathUrl,
headers,
params,
object : IRequestResult {
override fun onSuccess(data: String) {
reply.reply(createResult(true, data))
}
override fun onError(msg: String) {
reply.reply(createResult(false, msg))
}
})
}
// 统一返回格式
private fun createResult(success: Boolean, data: String): HashMap<String, Any> {
var result = HashMap<String, Any>()
result["success"] = success
result["data"] = data
return result
}
}
- dart 层 channel 封装
class NativeRequestTool {
// BasicMessageChannel 的名称要与 native 端相对应
static const BasicMessageChannel _messageChannel =
const BasicMessageChannel('retrofitRequest', StandardMessageCodec());
static Future<Response> doRequest(Request param) async {
//我们规范参数类型为 map
return _messageChannel
.send(param.toJson())
.then((value) => Response.fromNative(value));
}
}
// 我们规定的传参模型范式
class Request {
final String method;
final String baseUrl;
final String pathUrl;
final Map<String, String> headers;
final Map<String, dynamic> params;
Request({
this.method,
this.baseUrl,
this.pathUrl,
this.headers,
this.params,
}) : assert(
baseUrl != null,
pathUrl != null,
);
HashMap<String, dynamic> toJson() {
return new HashMap<String, dynamic>()
..['method'] = method
..['baseUrl'] = baseUrl
..['pathUrl'] = pathUrl
..['headers'] = headers ?? {}
..['params'] = params ?? {};
}
}
// 我们规定的回参模型范式
class Response {
final bool success;
final String data; //data为请求返回结果
Response({this.success, this.data});
factory Response.fromNative(dynamic result) {
return Response(
success: result['success'],
data: result['data'],
);
}
}
- dart 层网络调用层封装
import 'dart:collection';
import 'dart:convert' as JSON;
import 'channel/message_channel_tool.dart';
import 'retrofit_plugin.dart';
///调用原生网络库
abstract class RequestBaseLoader<T> {
///请求path
String get path;
/// Post 或者 Get
Method get method;
///netConfig对应的key
String get domainKey => null;
///post传参
Map<String, dynamic> get data => HashMap();
///header
Map<String, String> get header => HashMap();
NetWorkConfig _getConfig() {
return NetWorkConfigManager.getConfig(domainKey: domainKey);
}
///获取域名地址
String _getBaseUrl() {
return _getConfig().baseUrl;
}
///拦截器
NetworkIntercept get intercept => _getConfig().intercept;
///获取请求头
Map<String, String> _getHeaders() {
Map<String, String> headers = _getConfig().headers;
if (headers == null) {
headers = Map();
}
if (header != null && header.length > 0) {
header.forEach((key, value) {
headers.putIfAbsent(key, () => value);
});
}
return headers;
}
///获取请求传参
Map<String, dynamic> _getParams() {
return data ?? {};
}
Request _createReq() {
return Request(
method: method == Method.Post ? "Post" : "Get",
baseUrl: _getBaseUrl(),
pathUrl: path,
headers: _getHeaders(),
params: _getParams(),
);
}
///请求结果为 map
Future<Map<String, dynamic>> request() async {
Request _request = _createReq();
Response response = await NativeRequestTool.doRequest(_request);
//添加 response 拦截器
if (intercept != null) {
intercept.onResponse(_request, response);
}
if (response.success) {
Map<String, dynamic> result;
try {
result = JSON.jsonDecode(response.data);
} catch (_) {
result = {"response": response.data};
}
return result;
} else {
throw Exception(response.data);
}
}
Future<T> execute();
}
enum Method {
Post,
Get,
}
abstract class NetWorkConfigManager {
static HashMap<String, NetWorkConfig> _configs = HashMap();
static void addDefault(NetWorkConfig config) {
addConfig("default", config);
}
static void addConfig(String key, NetWorkConfig config) {
_configs.putIfAbsent(key, () => config);
}
static NetWorkConfig getConfig({String domainKey}) {
var config = _configs[domainKey];
return config == null ? _configs["default"] : config;
}
}
class NetWorkConfig {
final String baseUrl;
final Map<String, String> headers;
final NetworkIntercept intercept;
NetWorkConfig({
this.baseUrl,
this.headers,
this.intercept,
});
}
///网络请求拦截器
abstract class NetworkIntercept {
onResponse(Request request, Response response);
}
插件代码已上传:retrofit_plugin
网友评论