MST

星途 面试题库

面试题:Flutter Dio插件在复杂网络场景下的优化与定制

假设你面临一个Flutter项目,需要处理高并发网络请求、动态证书验证以及自定义缓存策略,基于Dio插件,详细说明你会采取哪些策略与技术手段进行优化与定制,要求从原理到代码实现全面阐述。
15.9万 热度难度
前端开发Flutter

知识考点

AI 面试

面试题答案

一键面试

处理高并发网络请求

  1. 原理
    • 使用Dio的Future.wait方法来并发执行多个网络请求。Dio是一个强大的HTTP客户端库,Future.wait可以将多个Future对象组成一个列表,当列表中的所有Future都完成时,返回一个包含所有结果的新Future
    • 控制并发请求数量,避免过多请求占用过多资源,导致性能问题或请求失败。可以使用StreamStreamController来管理请求队列,按照一定的并发数依次执行请求。
  2. 代码实现
import 'package:dio/dio.dart';

void main() async {
  Dio dio = Dio();
  List<Future<Response>> futures = [
    dio.get('https://example.com/api1'),
    dio.get('https://example.com/api2'),
    dio.get('https://example.com/api3')
  ];
  List<Response> responses = await Future.wait(futures);
  responses.forEach((response) {
    print(response.data);
  });
}

控制并发数的示例:

import 'package:dio/dio.dart';
import 'dart:async';

void main() async {
  Dio dio = Dio();
  List<String> urls = [
    'https://example.com/api1',
    'https://example.com/api2',
    'https://example.com/api3',
    'https://example.com/api4'
  ];
  int concurrency = 2;
  StreamController<List<Response>> controller = StreamController<List<Response>>();
  Stream<List<Response>> stream = controller.stream;
  List<Future<Response>> currentRequests = [];
  int index = 0;

  void _executeRequests() {
    while (currentRequests.length < concurrency && index < urls.length) {
      currentRequests.add(dio.get(urls[index]));
      index++;
    }
    Future.wait(currentRequests).then((responses) {
      controller.add(responses);
      currentRequests = [];
      if (index < urls.length) {
        _executeRequests();
      } else {
        controller.close();
      }
    });
  }

  _executeRequests();

  stream.listen((responses) {
    responses.forEach((response) {
      print(response.data);
    });
  });
}

动态证书验证

  1. 原理
    • 在Flutter中,使用Dio进行网络请求时,可以通过自定义HttpClientAdapter来实现动态证书验证。Dio底层依赖HttpClient,通过创建自定义的HttpClientAdapter,我们可以在HttpClient建立连接时,对服务器证书进行验证。
    • 可以从本地存储或远程服务器获取证书信息,在验证时与服务器提供的证书进行对比。
  2. 代码实现
import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
import 'dart:io';

class CustomHttpClientAdapter extends HttpClientAdapter {
  final String certificatePath;

  CustomHttpClientAdapter(this.certificatePath);

  @override
  Future<http.StreamedResponse> send(BaseOptions options, RequestOptions requestOptions) {
    return createHttpClient(requestOptions).then((client) {
      client.badCertificateCallback = (X509Certificate cert, String host, int port) {
        // 从本地文件读取证书
        SecurityContext context = SecurityContext.defaultContext;
        context.setTrustedCertificates(certificatePath);
        return context.verifyCertificateChain(cert, host, port);
      };
      return client.send(requestOptions.toHttpClientRequest());
    });
  }
}

void main() async {
  Dio dio = Dio();
  dio.httpClientAdapter = CustomHttpClientAdapter('path/to/your/certificate.pem');
  try {
    Response response = await dio.get('https://example.com');
    print(response.data);
  } catch (e) {
    print(e);
  }
}

自定义缓存策略

  1. 原理
    • 可以利用Dio的拦截器机制,在请求发出前和响应返回后进行处理。在请求发出前,检查缓存中是否有对应的请求结果,如果有且缓存未过期,则直接返回缓存数据。在响应返回后,根据自定义的缓存策略,将响应数据存入缓存,并记录缓存的过期时间。
    • 可以使用SharedPreferences或文件系统来存储缓存数据。
  2. 代码实现
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';

class CustomCacheInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String cacheKey = options.uri.toString();
    String cachedData = prefs.getString(cacheKey);
    if (cachedData != null) {
      // 假设缓存数据格式为 {data: '...', timestamp: '...'}
      Map<String, dynamic> cacheMap = json.decode(cachedData);
      int currentTime = DateTime.now().millisecondsSinceEpoch;
      // 假设缓存有效期为1小时(3600000毫秒)
      if (currentTime - cacheMap['timestamp'] < 3600000) {
        handler.resolve(Response(
          requestOptions: options,
          data: cacheMap['data'],
          statusCode: 200,
        ));
        return;
      }
    }
    handler.next(options);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String cacheKey = response.requestOptions.uri.toString();
    int currentTime = DateTime.now().millisecondsSinceEpoch;
    Map<String, dynamic> cacheMap = {
      'data': response.data,
      'timestamp': currentTime
    };
    prefs.setString(cacheKey, json.encode(cacheMap));
    handler.next(response);
  }
}

void main() async {
  Dio dio = Dio();
  dio.interceptors.add(CustomCacheInterceptor());
  try {
    Response response = await dio.get('https://example.com');
    print(response.data);
  } catch (e) {
    print(e);
  }
}