likes
comments
collection
share

Flutter网络和数据存储框架搭建

作者站长头像
站长
· 阅读数 66

概述

无论是Flutter开发还是react-native开发都离不了和服务端进行数据交互。

数据的交互避免不了的需要进行网络请求,在大部分场景中数据交互是通过http请求完成的.对于繁杂的业务网络层所面临的挑,需要通过网络层架构设计来应对这些挑战。

对于繁杂的业务网络层所面临的挑战

  • 切换成本高:网络操作使用的三方库存在不维护切换成本高的风险;
  • 接口管理不便:对于大中型APP接口众多,不方便管理;
  • 重复代码多:APP中进行数据交互的场景很多,网络请求存在大量的重复代码;
  • 扩展性差:网络操作和业务代码耦合严重,不利于扩展;
  • 开发效率低:不同三方库使用方式不统一,步骤繁琐开发效率低;

HitNet架构设计

设计要求:

  1. 支持网络库插拔设计,且不干扰业务层
  2. 简洁易用,支持配置来进行请求
  3. Adapter设计,扩展性强
  4. 统一异常和返回处理

Flutter网络和数据存储框架搭建

目录结构

Flutter网络和数据存储框架搭建

封装基础请求

//  http/request/base_request.dart 

enum HttpMethod { GET, POST, DELETE }

///基础请求
abstract class BaseRequest {
  // curl -X GET "http://api.devio.org/uapi/test/test?requestPrams=11" -H "accept: */*"
  // curl -X GET "https://api.devio.org/uapi/test/test/1
  var pathParams;
  var useHttps = true;

  String authority() {
    return "api.devio.org";
  }

  HttpMethod httpMethod();

  String path();

  String url() {
    Uri uri;
    var pathStr = path();
    //拼接path参数
    if (pathParams != null) {
      if (path().endsWith("/")) {
        pathStr = "${path()}$pathParams";
      } else {
        pathStr = "${path()}/$pathParams";
      }
    }
    //http和https切换
    if (useHttps) {
      uri = Uri.https(authority(), pathStr, params);
    } else {
      uri = Uri.http(authority(), pathStr, params);
    }
    print('url:${uri.toString()}');
    return uri.toString();
  }

  bool needLogin();

  Map<String, String> params = Map();

  ///添加参数
  BaseRequest add(String k, Object v) {
    params[k] = v.toString();
    return this;
  }

  Map<String, dynamic> header = {};

  ///添加header
  BaseRequest addHeader(String k, Object v) {
    header[k] = v.toString();
    return this;
  }
}

统一异常和返回处理

// http/core/hi_error.dart
///需要登录的异常
class NeedLogin extends HiNetError {
  NeedLogin({int code = 401, String message = '请先登录'}) : super(code, message);
}

///需要授权的异常
class NeedAuth extends HiNetError {
  NeedAuth(String message, {int code = 403, dynamic data})
      : super(code, message, data: data);
}

///网络异常统一格式类
class HiNetError implements Exception {
  final int code;
  final String message;
  final dynamic data;

  HiNetError(this.code, this.message, {this.data});
}

封装请求抽象类

// http/core/hi_net_adapter.dart

import 'dart:convert';

import 'package:flutter_bili_app/http/request/base_request.dart';

///网络请求抽象类
abstract class HiNetAdapter {
  Future<HiNetResponse<T>> send<T>(BaseRequest request);
}

/// 统一网络层返回格式
class HiNetResponse<T> {
  HiNetResponse({
    this.data,
    required this.request,
    this.statusCode,
    this.statusMessage,
    this.extra,
  });

  /// Response body. may have been transformed, please refer to [ResponseType].
  T? data;

  /// The corresponding request info.
  BaseRequest request;

  /// Http status code.
  int? statusCode;

  /// Returns the reason phrase associated with the status code.
  /// The reason phrase must be set before the body is written
  /// to. Setting the reason phrase after writing to the body.
  String? statusMessage;

  /// Custom field that you can retrieve it later in `then`.
  late dynamic extra;

  /// We are more concerned about `data` field.
  @override
  String toString() {
    if (data is Map) {
      return json.encode(data);
    }
    return data.toString();
  }
}

安装并使用dio

插件地址: pub.dev/packages/di…

修改pubspec.yaml

dependencies:
    dio: ^5.1.2

然后 flutter pub get

// http/core/dio_adapter.dart

import 'package:dio/dio.dart';
import 'package:flutter_bili_app/http/core/hi_error.dart';
import 'package:flutter_bili_app/http/core/hi_net_adapter.dart';
import 'package:flutter_bili_app/http/request/base_request.dart';

///Dio适配器
class DioAdapter extends HiNetAdapter {
  @override
  Future<HiNetResponse<T>> send<T>(BaseRequest request) async {
    var response, options = Options(headers: request.header);
    var error;
    try {
      if (request.httpMethod() == HttpMethod.GET) {
        response = await Dio().get(request.url(), options: options);
      } else if (request.httpMethod() == HttpMethod.POST) {
        response = await Dio()
            .post(request.url(), data: request.params, options: options);
      } else if (request.httpMethod() == HttpMethod.DELETE) {
        response = await Dio()
            .delete(request.url(), data: request.params, options: options);
      }
    } on DioError catch (e) {
      error = e;
      response = e.response;
    }
    if (error != null) {
      ///抛出HiNetError
      throw HiNetError(response?.statusCode ?? -1, error.toString(),
          data: await buildRes(response, request));
    }
    return buildRes(response, request);
  }

  ///构建HiNetResponse
  Future<HiNetResponse<T>> buildRes<T>(
      Response? response, BaseRequest request) {
    return Future.value(HiNetResponse(
      //?.防止response为空
        data: response?.data,
        request: request,
        statusCode: response?.statusCode,
        statusMessage: response?.statusMessage,
        extra: response));
  }
}

封装统一网络请求

// http/core/hi_net.dart
import 'package:flutter_bili_app/http/core/dio_adapter.dart';

import '../request/base_request.dart';
import 'hi_error.dart';
import 'hi_net_adapter.dart';

///1.支持网络库插拔设计,且不干扰业务层
///2.基于配置请求请求,简洁易用
///3.Adapter设计,扩展性强
///4.统一异常和返回处理
class HiNet {
  HiNet._();
  static HiNet? _instance;

  static HiNet getInstance() {
    if (_instance == null) {
      _instance = HiNet._();
    }
    return _instance!;
  }

  Future fire(BaseRequest request) async {
    HiNetResponse? response;
    var error;
    try {
      response = await send(request);
    } on HiNetError catch (e) {
      error = e;
      response = e.data;
      printLog(e.message);
    } catch (e) {
      //其它异常
      error = e;
      printLog(e);
    }
    if (response == null) {
      printLog(error);
    }
    var result = response?.data;
    printLog(result);
    var status = response?.statusCode;
    switch (status) {
      case 200:
        return result;
      case 401:
        throw NeedLogin();
      case 403:
        throw NeedAuth(result.toString(), data: result);
      default:
        throw HiNetError(status ?? -1, result.toString(), data: result);
    }
  }

  Future<HiNetResponse<T>> send<T>(BaseRequest request) async {
    ///使用Dio发送请求
    HiNetAdapter adapter = DioAdapter();
    return adapter.send(request);
  }

  void printLog(log) {
    print('hi_net:' + log.toString());
  }
}

封装测试接口

// http/request/test_request.dart
import 'package:flutter_bili_app/http/request/base_request.dart';

class TestRequest extends BaseRequest {
  @override
  HttpMethod httpMethod() {
    return HttpMethod.GET;
  }

  @override
  bool needLogin() {
    return false;
  }

  @override
  String path() {
    return 'uapi/test/test';
  }
}

页面中使用

import 'package:flutter_bili_app/http/core/hi_error.dart';
import 'package:flutter_bili_app/http/request/test_request.dart';
import 'dart:convert';
import 'http/core/hi_net.dart';

void _incrementCounter() async {
    TestRequest request = TestRequest();
    request.add("aa", "ddd").add("bb", "333").add("requestPrams", "kkkk");
    try {
      var result = await HiNet.getInstance().fire(request);
      print(result);
    } on NeedAuth catch (e) {
      print(e);
    } on NeedLogin catch (e) {
      print(e);
    } on HiNetError catch (e) {
      print(e);
    }
  }

Dart JSON编码器和解码器剖析

概述

JSON(JavaScript对象表示法)是一种轻量级的数据交换格式。因为易于阅读和书写并且机器也很容易解析和生成,被广泛应用到客户端和服务器的数据交换中。

目前各大公司的服务端接口基本上都支持json格式的数据交换,支持json交换格式的接口会在http的响应头中包含一下信息:

Content-Type: application/json;charset=UTF-8

使用dart:convert进行json解析

Flutter采用Dart语言进行开发,那dart是如何进行json解析的呢?

在dart中有个内置的json解析器:dart:convert,对于较小项目可以借助它来进行手动JSON序列化。

json转map

import 'dart:convert';
const jsonString =
    "{ \"name\": \"flutter\", \"url\": \"https://coding.imooc.com/class/487.html\" }";
//json转map
Map<String, dynamic> jsonMap = jsonDecode(jsonString);
print('name:${jsonMap['name']}');
print('url:${jsonMap['url']}');

使用jsonDecode方法,会将json字符串解析成Map<String, dynamic>,然后根据需要从map中查找所需的值就可以了,它没有外部依赖或其它的设置,对于小项目很方便。

map转json

String jsonStr = jsonEncode(jsonMap);
print('json:$jsonStr');

jsonEncode可以很轻松的将object转成json字符串。

当您的项目变大时,手动编写序列化逻辑可能变得难以管理且容易出错。如果您在访问未提供的JSON字段时输入了一个错误的字段,则您的代码将会在运行时会引发错误。

如果您的项目中JSON model并不多,并且希望快速测试一下,那么手动序列化可能会很方便。

实体类

安装json_serializable 与 json_annotation 与 build_runner

插件:

pub.dev/packages/js… pub.dev/packages/js… pub.dev/packages/bu…

修改pubspec.yaml

dependencies:
    json_serializable: ^6.7.0
    json_annotation: ^4.8.1
dev_dependencies:
  build_runner: ^2.4.5

配置实体类

// model/result.dart

// result.g.dart 将在我们运行生成命令后自动生成
import 'package:json_annotation/json_annotation.dart';

part 'result.g.dart';

@JsonSerializable()
class Result {
  //定义字段
  int code;
  String method;
  String requestPrams;

  Result(this.code, this.method, this.requestPrams);

  //固定格式,不同的类使用不同的mixin即可
  factory Result.fromJson(Map<String, dynamic> json) => _$ResultFromJson(json);

  //固定格式,
  Map<String, dynamic> toJson() => _$ResultToJson(this);
}

因为实体类的生成代码还不存在,所以上代码会提示一些错误是正常现象 执行build生成实体类, 会生成result.g.dart

flutter packages pub run build_runner build

使用

import 'package:flutter_bili_app/model/result.dart';

const jsonString = {'code': 100, 'method': 'get', 'requestPrams': 'aabb'};
print(Result.fromJson(jsonString).toJson());

统一缓存管理框架hi_cache设计

安装shared_preferences

插件: pub.dev/packages/sh…

修改pubspec.yaml

shared_preferences: ^2.1.2
// db/hi_cache.dart
import 'package:shared_preferences/shared_preferences.dart';

///缓存管理类
class HiCache {
  SharedPreferences? prefs;

  HiCache._() {
    init();
  }

  static HiCache? _instance;

  HiCache._pre(SharedPreferences prefs) {
    this.prefs = prefs;
  }

  ///预初始化,防止在使用get时,prefs还未完成初始化
  static Future<HiCache> preInit() async {
    if (_instance == null) {
      var prefs = await SharedPreferences.getInstance();
      _instance = HiCache._pre(prefs);
    }
    return _instance!;
  }

  static HiCache getInstance() {
    if (_instance == null) {
      _instance = HiCache._();
    }
    return _instance!;
  }

  void init() async {
    if (prefs == null) {
      prefs = await SharedPreferences.getInstance();
    }
  }

  setString(String key, String value) {
    prefs?.setString(key, value);
  }

  setDouble(String key, double value) {
    prefs?.setDouble(key, value);
  }

  setInt(String key, int value) {
    prefs?.setInt(key, value);
  }

  setBool(String key, bool value) {
    prefs?.setBool(key, value);
  }

  setStringList(String key, List<String> value) {
    prefs?.setStringList(key, value);
  }

  remove(String key) {
    prefs?.remove(key);
  }

  T? get<T>(String key) {
    var result = prefs?.get(key);
    if (result != null) {
      return result as T;
    }
    return null;
  }
}

使用

import 'package:flutter_bili_app/db/hi_cache.dart';

HiCache.getInstance().setString("aa", "1234");
var value = HiCache.getInstance().get("aa");
print('value:$value');
转载自:https://juejin.cn/post/7248073216513605690
评论
请登录