Flutter中的单例以及网络请求库的封装

https://zhuanlan.zhihu.com/p/53498914


Flutter中的单例以及网络请求库的封装

Flutter中的单例以及网络请求库的封装

Why?为什么需要单例

在Android中我们经常使用OkHttp来进行网络请求,但我们并不希望每次都创建一个OkHttpClient;亦或有些资源初始化非常麻烦,消耗性能,我们希望一次创建,处处使用。这时候就需要单例。Dio作为flutter中的OkHttp,我们也可以用单例模式对其进行封装。

How?如何用dart实现单例

单例一般有这几个特征:

  1. 隐藏类的构造函数
  2. 提供一个方法获取该类的实例(Java中常常是一个静态方法,通过DCL或静态内部类等方法,返回一个实例)
  3. 该实例只能被创建一次,内存中独一份,任何地方通过调用特征2中所述方法获取到的实例都应该是同一个

来看看《Dart Cookbook》中时如何实现单例模式的:

/*The singleton example shows how to do this 
(substitute your singleton class name for Immortal).
Use a factory constructor to implement the
 singleton pattern, as shown in the following code:*/
 class Immortal { static final Immortal theOne = new Immortal._internal('Connor MacLeod'); String name; factory Immortal(name) => theOne; // private, named constructor  Immortal._internal(this.name); } main() { var im1 = new Immortal('Juan Ramirez'); var im2 = new Immortal('The Kurgan'); print(im1.name); print(im2.name); print(Immortal.theOne.name); assert(identical(im1, im2)); }

可以看到,他通过私有的具名构造方法_internal()隐藏了构造方法,提供了一个工厂方法来获取该类的实例,并且用static final修饰了theOne,theOne会在编译期被初始化,保证了特征3。至于theOne为什么会在编译期初始化,参考 Static variable initialization opened up to any expression

Singleton In Action!在项目中使用单例

Dio是flutterchina提供的一个网络请求库,可以说是Flutter中的OkHttp,支持拦截器,缓存等特性。具体使用参考Dio github。我在项目中使用单例模式对Dio库进行了一层简单封装:

import "package:dio/dio.dart"; import 'dart:async';  class HttpUtil {  static final HttpUtil _instance = HttpUtil._internal();  Dio _client;   factory HttpUtil() => _instance;   HttpUtil._internal() {  if (null == _client) {  Options options = new Options();  options.baseUrl = "http://www.wanandroid.com";  options.receiveTimeout = 1000 * 10; //10秒  options.connectTimeout = 5000; //5秒  _client = new Dio(options); } } Future<Map<String, dynamic>> get(String path, [Map<String, dynamic> params]) async { Response<Map<String, dynamic>> response; if (null != params) { response = await _client.get(path, data: params); } else { response = await _client.get(path); } return response.data; } //...省略post等方法... }

One More Thing

App后端接口返回的数据格式一般都有固定结构,以wanandroid.com的开发Api为例:

{
  "data": {
    "curPage": 1, "datas": [], "offset": 0, "over": true, "pageCount": 0, "size": 20, "total": 0 }, "errorCode": 0, "errorMsg": "" }

要解析这样的json,Android中可以玩出花。但是flutter禁用反射,也就没有类似Java中Gson这样的库。网上提供了flutter中,几种根据json自动生成model的方式,如下:

由于项目中的数据,结构固定,我采用范型+在线工具的的方式来实现我项目中json的解析,这种方法看起来有些笨拙(希望有同学可以提供更优雅的方式让我学习下):

1.定义一个抽象类,约定datas字段中model的行为:

abstract class JsonData{
  JsonData fromJson(Map<String, dynamic> json,JsonData mySelf); }

2.抽象出data字段对应的model:

import 'package:flutter_app/bean/JsonData.dart'; import 'package:meta/meta.dart'; class PageData<T extends JsonData>{ List<T> datas; int curPage; int pageCount; //...省略size,total等字段  PageData.fromJson({@required Map<String, dynamic> json,@required JsonDataCreator beanCreator}) { // TODO: implement fromJson  curPage = json['curPage']; pageCount = json['pageCount']; print(json); if (json['datas'] != null) { datas = new List<T>(); json['datas'].forEach((v) { JsonData item = beanCreator(); item.fromJson(v,item); datas.add(item); }); } } } typedef JsonDataCreator = JsonData Function();

3.抽象出整个返回结果对应的model:

import 'package:flutter_app/bean/PageData.dart'; import 'package:flutter_app/bean/JsonData.dart'; import 'package:meta/meta.dart'; class BaseResult<T extends JsonData>{ int errorCode; String errorMsg; PageData<T> data; BaseResult.fromJson({@required Map<String, dynamic> json,@required JsonDataCreator beanCreator}) { // TODO: implement fromJson  errorCode = json['errorCode']; errorMsg = json['errorMsg']; data = PageData.fromJson(json:json['data'],beanCreator: beanCreator); } } 

4.在项目中使用时,拿到json,放到在线工具中生成model,copy一下,改成以上约定的格式。以wanandroid文章列表接口为例,其返回结果如下:

{
  "data": {
    "curPage": 2, "datas": [ { "apkLink": "", "author": "鸿洋", "chapterId": 408, "chapterName": "鸿洋", "collect": false, "courseId": 13, "desc": "", "envelopePic": "", ...其他字段 "title": "一篇文本跳动控件,为你打开一扇大门,学会这两点心得,控件你也会写", "type": 0, "userId": -1, "visible": 1, "zan": 0 }, .... ], "errorCode":0, "errorMsg": } } 

封装一个model对应datas中的一个数据:

import 'package:flutter_app/bean/JsonData.dart'; class ProjectBean extends JsonData{ String title; String envelopePic; @override JsonData fromJson(Map<String, dynamic> json, JsonData mySelf) { // TODO: implement fromJson  if(mySelf is ProjectBean){ mySelf.title = json['title']; mySelf.envelopePic = json['envelopePic']; //...省略其他字段  } return mySelf; } } 

请求数据,并解析:

class ApiService{
  static Future<List<ProjectBean>> getProjectList() async{ String url = "/project/list/1/json"; Map<String,dynamic> response = await HttpUtil().get(url); BaseResult result = BaseResult<ProjectBean>.fromJson(json: response,beanCreator: ()=>ProjectBean()); print(result.data.datas.length); return result.data.datas; } } 

因为flutter中不能使用反射,我不知道有什么方法能够获得范型的实际类型,所以PageData的构造方法fromJson()需要传入一个闭包,用来创建model。

Thanks

感谢这些分享知识的作者,让我学习的过程中有资料可参考:

帮你整理一份快速入门Flutter的秘籍​mp.weixin.qq.com图标 8 篇文章,再学不会 Flutter 你来打我!​mp.weixin.qq.com图标 Flutter基础视频免费教程 共25集完成​juejin.im 《Flutter实战》开源电子书 - 掘金​juejin.im

猜你喜欢

转载自www.cnblogs.com/sundaysme/p/12588716.html
今日推荐