天天看點

Flutter 開發 mvp 和 網絡架構 使用及簡單封裝

github位址

背景:flutter 跨平台開發吸引人,想試試

web -> service -> dao

view -> presenter -> model

mvp 此類設計可以把工程易變的和不容易變的分離,是為解耦。關于為什麼要解耦,如何解耦,什麼是解耦 … 我們暫且不聊~

既然要開發 flutter 工程,我們必然要做一些基礎工作。比如 mvp,網絡請求,工具類,基礎UI 等等的封裝和抽取

Flutter 開發 mvp 和 網絡架構 使用及簡單封裝

介紹一下上圖:

1:和業務線無關的 base/core/utils/mvp/network

2:和技術選型無關的 mvp/core 也就是 mvp 和 網絡封裝(core 通過擴充卡可适配各種網絡架構)

今天,我們隻看 flutter 中 mvp 和 網絡架構的封裝及其使用:

1:mvp

基于 google Android Architecture =>https://github.com/googlesamples/android-architecture

mvp 實作: 遵守 contract 面向接口程式設計思想

HomeContract.dart

class View extends ILoadingView{
}
class Presenter extends IPresenter{
  void getBannerList(){}
}
           

HomePresenter.dart 負責業務相關實作

class HomePresenter extends BasePresenter<View> implements Presenter {
  HomePresenter(View view) : super(view);  
  @override
  void start() {}
  @override
  void getBannerList() {
    view.showLoading();
    // 方式一
    HttpProxy.getBannerList((int state, dynamic data) {
      if (state == HState.success) {
        view.closeLoading();
        List<Banner> bannerList = new GetBannerListJsonParser().parse(data);
        view.renderPage(bannerList);
      } else {
        view.closeLoading();
        view.showError(data.toString());
      }
    });
//    方式二
//    HttpProxy.getBannerList().then((Response res) {
//      view.closeLoading();
//      List<Banner> bannerList =
//          new GetBannerListJsonParser().parse(res.data);
//      view.renderPage(bannerList);
//    }).catchError((e) {
//      view.closeLoading();
//      view.showError(e.toString());
//    });
  }
}
           

方式1和方式2的兩種網絡請求我們一會兒再看,接下來我們看 ?

mvp 封裝:

最頂層基類,我們遵守可擴充原則

IPresenter.dart 和 IView.dart

class IPresenter{}
class IView{}
           

對于app常見頁面場景 ILoadingView.dart 和 ILoadingListView.dart

class ILoadingView extends IView{
  void showLoading(){}
  void closeLoading(){}
  void renderPage(Object o){}
  void reload(){}
  void showError(String msg){}
  void showDisconnect(){}
}
class ILoadingListView extends ILoadingView{
  void showEmpty(){}
}
           

關于命名,dart 沒有 interface 我就以 I 開頭了,至于抽象類建議 Abs 開頭

如何把 view 和 presenter 關聯呢,看 BasePresenter.dart

abstract class BasePresenter<T> {
  T view;
  BasePresenter(this.view);
  void start();
  void stop() {
    this.view = null;
  }
}
           

關于這樣寫,也是受 google 的那些 sample 影響,至少有一個 start () 方法,可以用可以不用,個人習慣,不過還是建議,有構造就有析構 有始有終,至于指定泛型,好處呢,編譯前裡可以找到 view 的方法并且省去一些 view 綁定 presenter 代碼的編寫。dartVm沒有泛型插除,帶來自由的同時,也帶來了危險,不過 dynamic 的類型真的很好。

2:網絡請求二次封裝

core 是存粹的,不應該引入任何網絡請求的庫,這樣才能做到通用性,通過 Adapter 來做不同網絡架構的适配

core 裡 HttpUtils 的使用:初始化擴充卡

main.dart 一些個人想法,所有 widget 組成的 page 都應有 state ,stateless 可以做一些自定義基礎控件和無互動的UI展示

void main() {
  ThirdLibsManager.get().setup();
  runApp(MyApp());
}
class MyApp extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    // ...
  }
  @override
  void dispose() {
    super.dispose();
    // 這個回調放這可能不對,感覺要用 channel 建立 dart 和 native(and/ios) 通道
    ThirdLibsManager.get().release();
  }
}
           
class TDelegate{
  void setup(){}
  void release(){}
}
class ThirdLibsManager implements TDelegate{
  static final ThirdLibsManager _instance = new ThirdLibsManager._internal();
  static ThirdLibsManager get() => _instance;
  factory ThirdLibsManager() => _instance;
  ThirdLibsManager._internal();
  @override
  void release() {}
  @override
  void setup() {
     // 設定擴充卡
     HttpUtils.get().setAdapter(DioAdapter());
     Log.setEnable(true);
  }
}
           

設定擴充卡以後的工作就是之前我們看到 HomePresenter.dart 裡的那樣了

封裝:

1:interface 一些接口和協定,可以看出 dart 語言靈活的魅力,一千個讀者一千個哈姆雷特,總有一種寫法适合你;

2:utils 一個入口;

3:ctx 一個抽象的全局的結構體;

HInterface.dart

import 'dart:async';
import 'RequestCtx.dart';
abstract class JsonParser<T> {
  T parse(String jsonStr);
}
abstract class HAdapter {
  Future<dynamic> request(RequestCtx ctx);
}
// a transformer
typedef transformer = String Function(String original);
// a callback
typedef dataCallback = Function(int state, dynamic data);
class HState{
  static final int success = 1;//成功
  static final int fail = 0;//失敗
  static final int intercept = -1;//中斷
}
           

RequestCtx.dart 包含了網絡請求和響應的所有必要的結構組成

import 'dart:io';
import 'HInterface.dart';
class HConstants {
  static final String get = "get";
  static final String post = "post";
  static final int timeout = 15000;
}
class RequestCtx {
  String _url;
  String _method;
  int _timeout;
  ContentType _contentType;
  dynamic _responseType;
  Map<String, dynamic> _paramMap;
  Map<String, dynamic> _headerMap;
  Map<String, dynamic> _bodyMap;
  int _retryCount;
  dynamic _transformer;
  List<dynamic> _interceptors;
  dataCallback _callback;
  // ...  太多了,就不寫了,可以 看github ?article start part
}
           

上面看到有2種網絡請求的書寫方式呢?我們看 HttpUtils 入口和 Adatper 接口的實作類:

HttpUtils.dart 某些人寫的網絡架構做了更深的 future 的封裝,我們就不需要 callback,沒做的,我們要在 adapter 的實作類裡手動回調,這也是 callback 函數的意義。

class HttpUtils{
  HAdapter _adapter;
  // 省略 單列模式 的固定書寫
  setAdapter(HAdapter adapter){
    this._adapter = adapter;
  }
  Future<dynamic> req(String url, {String method,int timeout,
    Map<String, dynamic> header,
    Map<String, dynamic> params,
    Map<String, dynamic> body,
    Transformer transformer,
    List<Interceptor> interceptors,
    dataCallback callback
    }) {
    assert(_adapter!=null);//沒有做adapter的實作是無法去請求的 asset 很強大
    try {
      RequestCtx ctx = new Builder()
          .setUrl(url)
          .setMethod(method)
          .setHeaderMap(header)
          .setTimeout(timeout)
          .setParams(params)
          .setResponseType(ResponseType.plain)
          .setBodyMap(body)
          .setTransformer(transformer)
          .setInterceptors(interceptors)
          .setDataCallback(callback)
          .build();
      return _adapter.request(ctx);
      
    } catch (e) {
      print(e.toString());
      rethrow;
    }
  }

  String wrapUrlByParams(String url,Map<String, dynamic> params){
    String ret = url;
    if (params != null && params is Map && params.isNotEmpty) {
      StringBuffer sb = new StringBuffer("?");
      params.forEach((key, value) {
        sb.write("$key" + "=" + "$value" + "&");
      });
      String paramStr = sb.toString();
      paramStr = paramStr.substring(0, paramStr.length - 1);
      ret += paramStr;
    }
    return ret;
  }
}
           

DioAdapter.dart 這裡先寫一個 dio 的擴充卡,dynamic 的好處大家可以體會到,要實作其他的網絡架構可以自己适配:

class DioAdapter implements HAdapter{

  Dio _dio;

  DioAdapter() {
    _dio = new Dio();
  }

  @override
  Future<Response<dynamic>> request(RequestCtx ctx) async {

    Future<Response<dynamic>> response;

    _dio.options = new BaseOptions(
        connectTimeout: ctx.timeout == null ? HConstants.timeout : ctx.timeout,
        receiveTimeout: ctx.timeout == null ? HConstants.timeout : ctx.timeout,
        headers: ctx.headerMap==null?{HttpHeaders.userAgentHeader: "dio-2.1.0"}:ctx.headerMap,
        contentType: ctx.contentType == null ? ContentType.json : ctx.contentType,
        responseType: ctx.responseType == null ? ResponseType.json : ctx.responseType,
        validateStatus: (status) {
          return status >= 200 && status < 300 || status == 304;
        }
    );

    if (ctx.transformer != null) {
      _dio.transformer = ctx.transformer;
    }

    if (ctx.interceptors != null && ctx.interceptors.isNotEmpty) {
      for (var value in ctx.interceptors) {
        _dio.interceptors.add(value);
      }
    }

    String url = HttpUtils.get().wrapUrlByParams(ctx.url, ctx.paramMap);

    switch (ctx.method) {
      case "get":
        response = _dio.get(url);
        break;
      case "post":
        response = _dio.post(url, data: ctx.bodyMap);
        break;
      default:
        response = _dio.get(url);
    }

    if(ctx.callback!=null){
      response.then((response){
        // can by some response.statusCode to make some regex
        ctx.callback(HState.success,response.data);
      }).catchError((e){
        ctx.callback(HState.fail,e);
      });
    }

    return response;
  }
}
           

感謝?,讀到這裡的大佬,萬分感謝你們:)後期可能會把 Adapter 的形式改成注解@ +動态代理 也就是類似于 Retrofit 那樣的擴充卡。盡管 flutter 不讓用反射,應該也有其他的實作動态代理實作方式吧?