美文网首页
Flutter:使用原生层网络 retrofit

Flutter:使用原生层网络 retrofit

作者: 李小轰 | 来源:发表于2021-08-16 11:04 被阅读0次
前言

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

相关文章

网友评论

      本文标题:Flutter:使用原生层网络 retrofit

      本文链接:https://www.haomeiwen.com/subject/qmrmbltx.html