当前位置: 首页>移动开发>正文

testerhome flutter 单测接入sonar flutter 单例

testerhome flutter 单测接入sonar flutter 单例,testerhome flutter 单测接入sonar flutter 单例_flutter,第1张

 

 

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

testerhome flutter 单测接入sonar flutter 单例,testerhome flutter 单测接入sonar flutter 单例_json_02,第2张

ClassNotFound

程序员

25 人赞同了该文章

 

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转Model
  • 一个在线的,类似As中的GsonFormat的工具

由于项目中的数据,结构固定,我采用范型+在线工具的的方式来实现我项目中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。

Dart常用工具类库 common_utils

1、TimelineUtil : 时间轴.(新)2、TimerUtil : 倒计时,定时任务.(新)3、MoneyUtil : 精确转换,元转分,分转元,支持格式输出.(新)4、LogUtil : 简单封装打印日志.(新)5、DateUtil : 日期转换格式化输出.6、RegexUtil : 正则验证手机号,身份证,邮箱等等.7、NumUtil : 保留x位小数, 精确加、减、乘、除, 防止精度丢失.8、ObjectUtil : 判断对象是否为空(String List Map),判断两个List是否相等.

Flutter工具类库 flustars

1、DioUtil : Dio 工具类.2、SpUtil : 单例"同步" SharedPreferences 工具类.3、ScreenUtil : 屏幕适配,获取屏幕宽、高、密度,AppBar高,状态栏高度,屏幕方向.4、WidgetUtil : Widget渲染监听,获取Widget宽高,在屏幕上的坐标.

Add dependency #

 x.x.x #latest version
  1.   

APIs #

TimelineUtil -> Example

1. 2.  enum DayFormat {
3.  
///(小于10s->刚刚)、x分钟、x小时、(昨天)、x天.
4.  
  Simple,
5.  
///(小于10s->刚刚)、x分钟、x小时、[今年: (昨天/1天前)、(2天前)、MM-dd],[往年: yyyy-MM-dd].
6.  
  Common,
7.  
///小于10s->刚刚)、x分钟、x小时、[今年: (昨天 HH:mm/1天前)、(2天前)、MM-dd HH:mm],[往年: yyyy-MM-dd HH:mm].
8.  
  Full,
9.  
}
10.  
///Timeline信息配置.
11.  
abstract class TimelineInfo {
12.  
String suffixAgo(); //suffix ago(后缀 后).
13.  
String suffixAfter(); //suffix after(后缀 前).
14.  
String lessThanTenSecond() => ''; //just now(刚刚).
15.  
String customYesterday() => ''; //Yesterday(昨天).优先级高于keepOneDay
16.  
bool keepOneDay(); //保持1天,example: true -> 1天前, false -> MM-dd.
17.  
bool keepTwoDays(); //保持2天,example: true -> 2天前, false -> MM-dd.
18.  
String oneMinute(int minutes); //a minute(1分钟).
19.  
String minutes(int minutes); //x minutes(x分钟).
20.  
String anHour(int hours); //an hour(1小时).
21.  
String hours(int hours); //x hours(x小时).
22.  
String oneDay(int days); //a day(1天).
23.  
String days(int days); //x days(x天).
24.  
DayFormat dayFormat(); //format.
25.  
}
26.  
setLocaleInfo               : 自定义设置配置信息.
27.  
formatByDateTime            : 格式输出时间轴信息 by DateTime .
28.  
format                      : 格式输出时间轴信息.
  1.   

TimerUtil -> Example

  1.  
    setInterval : 设置Timer间隔.
  2.  
    setTotalTime : 设置倒计时总时间.
  3.  
    startTimer() : 启动定时Timer.
  4.  
    startCountDown : 启动倒计时Timer.
  5.  
    updateTotalTime : 重设倒计时总时间.
  6.  
    cancel : 取消计时器.
  7.  
    setOnTimerTickCallback : 计时器回调.
  8.  
    isActive : Timer是否启动.
MoneyUtil 精确转换,防止精度丢失 -> Example

 changeF2Y : 分 转 元, format格式输出.

 changeFStr2YWithUnit : 分字符串 转 元, format 与 unit 格式 输出.

 changeF2YWithUnit : 分 转 元, format 与 unit 格式 输出.

 changeYWithUnit : 元, format 与 unit 格式 输出.

 changeY2F : 元 转 分.

LogUtil #

  1.  init(isDebug, tag) : isDebug: 模式, tag 标签.
  2.  e(object, tag) : 日志e
  3.  v(object, tag) : 日志v,只在debug模式输出.
  4.   

NumUtil -> Example

  1.  getIntByValueStr : 数字字符串转int.
  2.  getDoubleByValueStr : 数字字符串转double.
  3.  getNumByValueStr : 保留x位小数 by 数字字符串.
  4.  getNumByValueDouble : 保留x位小数 by double.
  5.  add : 加(精确相加,防止精度丢失).
  6.  subtract : 减(精确相减,防止精度丢失).
  7.  multiply : 乘(精确相乘,防止精度丢失).
  8.  divide : 除(精确相除,防止精度丢失).
  9.  remainder : 余.
  10.  lessThan : < .
  11.  thanOrEqual : <= .
  12.  greaterThan : > .
  13.  greaterOrEqual : >= .
  14.   

DateUtil -> Example

  1.  enum DateFormat {
  2.  DEFAULT, //yyyy-MM-dd HH:mm:ss.SSS
  3.  NORMAL, //yyyy-MM-dd HH:mm:ss
  4.  YEAR_MONTH_DAY_HOUR_MINUTE, //yyyy-MM-dd HH:mm
  5.  YEAR_MONTH_DAY, //yyyy-MM-dd
  6.  YEAR_MONTH, //yyyy-MM
  7.  MONTH_DAY, //MM-dd
  8.  MONTH_DAY_HOUR_MINUTE, //MM-dd HH:mm
  9.  HOUR_MINUTE_SECOND, //HH:mm:ss
  10.  HOUR_MINUTE, //HH:mm
  11.   
  12.  ZH_DEFAULT, //yyyy年MM月dd日 HH时mm分ss秒SSS毫秒
  13.  ZH_NORMAL, //yyyy年MM月dd日 HH时mm分ss秒 / timeSeparate: ":" --> yyyy年MM月dd日 HH:mm:ss
  14.  ZH_YEAR_MONTH_DAY_HOUR_MINUTE, //yyyy年MM月dd日 HH时mm分 / timeSeparate: ":" --> yyyy年MM月dd日 HH:mm
  15.  ZH_YEAR_MONTH_DAY, //yyyy年MM月dd日
  16.  ZH_YEAR_MONTH, //yyyy年MM月
  17.  ZH_MONTH_DAY, //MM月dd日
  18.  ZH_MONTH_DAY_HOUR_MINUTE, //MM月
  19.  HH时mm分 / timeSeparate: ":" --> MM月dd日 HH:mm
  20.  ZH_HOUR_MINUTE_SECOND, //HH时mm分ss秒
  21.  ZH_HOUR_MINUTE, //HH时mm分
  22.  }
  23.  getNowDateMs : 获取现在 毫秒.
  24.  getNowDateStr : 获取现在 日期字符串.(yyyy-MM-dd HH:mm:ss)
  25.  getDateMsByTimeStr : 获取毫秒 By 日期字符串(Format格式输出).
  26.  getDateStrByTimeStr : 获取日期字符串 By 日期字符串(Format格式输出).
  27.  getDateStrByMs : 获取日期字符串 By 毫秒(Format格式输出).
  28.  getDateStrByDateTime : 获取日期字符串 By DateTime(Format格式输出).
  29.  getWeekDay : 获取WeekDay By DateTime.
  30.  getZHWeekDay : 获取星期 By DateTime.
  31.  getWeekDayByMs : 获取WeekDay By 毫秒.
  32.  getZHWeekDayByMs : 获取星期 By 毫秒.
  33.  isLeapYearByYear : 是否是闰年.
  34.  yearIsEqual : 是否同年.
  35.  getDayOfYear : 在今年的第几天.
  36.  isYesterday : 是否是昨天.
  37.  isToday : 是否是今天.
  38.   

RegexUtil -> Example

  1.  isMobileSimple : 简单验证手机号
  2.  isMobileExact : 精确验证手机号
  3.  isTel : 验证电话号码
  4.  isIDCard : 验证身份证号码
  5.  isIDCard15 : 验证身份证号码 15 位
  6.  isIDCard18 : 简单验证身份证号码 18 位
  7.  isIDCard18Exact : 精确验证身份证号码 18 位
  8.  isEmail : 验证邮箱
  9.  isURL : 验证 URL
  10.  isZh : 验证汉字
  11.  isDate : 验证 yyyy-MM-dd 格式的日期校验,已考虑平闰年
  12.  isIP : 验证 IP 地址

ObjectUtil -> Example

  1.  isEmptyString : 判断String是否为空.
  2.  isEmptyList : 判断List是否为空.
  3.  isEmptyMap : 判断Map是否为空.
  4.  isEmpty : 判断对象是否为空.(String List Map).
  5.  isNotEmpty : 判断对象是否非空.(String List Map).
  6.  twoListIsEqual : 判断两个List是否相等.
  7.   

Example #

    1.   
    2.  // Import package
    3.  import 'package:common_utils/common_utils.dart';
    4.   
    5.  //TimelineUtil
    6.  DateTime xxxDateTime = DateTime(2018, 6, 16, 16, 16, 16);
    7.  LogUtil.e("Timeline: " + TimelineUtil.formatByDateTime(xxxDateTime, locale: 'zh').toString());
    8.   
    9.  //MoneyUtil example
    10.  String moneyTxt = MoneyUtil.changeFStr2YWithUnit("1160", format: MoneyFormat.NORMAL, unit: MoneyUnit.YUAN_ZH);
    11.  String moneyTxt = MoneyUtil.changeYWithUnit("1.66", unit: MoneyUnit.YUAN_ZH);
    12.   
    13.  //TimerUtil example
    14.  TimerUtil timerUtil;
    15.  //定时任务test
    16.  new TimerUtil(mInterval: 1000);
    17.  //timerUtil.setInterval(1000);
    18.  int value) {
    19.  "TimerTick: " + value.toString());
    20.    });
    21.    timerUtil.startTimer();
    22.  //timerUtil.cancel();
    23.   
    24.  TimerUtil timerCountDown;
    25.  //倒计时test
    26.  new TimerUtil(mInterval: 1000, mTotalTime: 3 * 1000);
    27.  //    timerCountDown.setInterval(1000);
    28.  //    timerCountDown.setTotalTime(3 * 1000);
    29.  int value) {
    30.  double tick = (value / 1000);
    31.  "CountDown: " + tick.toInt().toString());
    32.      });
    33.      timerCountDown.startCountDown();
    34.  //timerUtil.cancel();
    35.   
    36.  //LogUtil example
    37.  LogUtil.init(isDebug: true, tag: "test");
    38.  LogUtil.e("...log...", tag: "test");
    39.  LogUtil.v("...log...", tag: "test");
    40.   
    41.  //DateUtil example
    42.  String timeNow = DateUtil.getDateStrByDateTime(DateTime.now());//2018-09-16 23:14:56
    43.  String timeNow = DateUtil.getDateStrByDateTime(DateTime.now(),format: DateFormat.ZH_NORMAL);//2018年09月16日 23时16分15秒
    44.  String weekday = DateUtil.getWeekDay(DateTime.parse("2018-09-16"));//Sunday
    45.  String weekdayZh = DateUtil.getZHWeekDay(DateTime.parse("2018-09-16"));//星期日
    46.   
    47.  //First Page init. Notice!!!
    48.  ScreenUtil.getInstance().init(context);
    49.   
    50.  ScreenUtil.screenWidth
    51.  ScreenUtil.screenHeight
    52.  ScreenUtil.statusBarHeight
    53.  ScreenUtil.screenDensity
    54.   
    55.  List listA = ["A", "B", "C"];
    56.  List listB = ["A", "B", "C"];
    57.  print("Two List Is Equal: " + ObjectUtil.twoListIsEqual(listA, listB).toString());
    58.   
    59.  // Global variable,Reference example
    60.  WidgetUtil widgetUtil = new WidgetUtil();
    61.   
    62.  @override
    63.  Widget build(BuildContext context) {
    64.  false, (Rect rect) {
    65.  double width = rect.width;
    66.  double height = rect.height;
    67.    });
    68.  return ;
    69.   }
    70.   
    71.  //Widgets must be rendered completely. Otherwise return Rect.zero.
    72.  Rect rect = WidgetUtil.getWidgetBounds(context);
    73.  double width = rect.width;
    74.  double height = rect.height;
    75.   
    76.  //Widgets must be rendered completely. Otherwise return Offset.zero.
    77.  Offset offset = WidgetUtil.getWidgetLocalToGlobal(context);
    78.  double dx = offset.dx  
    79.  double dx = offset.dy
    80.



    https://www.xamrdz.com/mobile/4v31961087.html

    相关文章: