为什么我的flutter http网络呼叫很慢?

qyuhtwio  于 2023-03-19  发布在  Flutter
关注(0)|答案(2)|浏览(155)

我正在开发一个带有网络活动的flutter应用程序。为了获取数据,我正在连接到一个REST API,这个API速度应该很快。
有关详细信息,此API使用AWS API GatewayAWS Lambda沿着其他AWS技术。
下面是我的代码,连接到网络.

class RoleService with ChangeNotifier {
  NavLinks _navLinks = NavLinks();
  late List<Role> _roles;


  /// Get all Roles
  Future<void> getAllRoles(String authToken) async {
    try {
      var data = await http.get(
        Uri.parse("https://api2.example.com/userrel/roles/getall"),
        headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"},
      );
      var jsonData =
          convert.json.decode(data.body).cast<Map<String, dynamic>>();
      _roles = jsonData.map<Role>((json) => new Role.fromJson(json)).toList();
      print(_roles);
    } catch (error) {
      print(error);
      throw error;
    }
  }
}

下面可以看到上面API调用的postman性能,Flutter测试我用的是华为p30 Lite安卓手机。

然后,当我在flutter中执行相同的API调用时,得到的结果如下。

观察postman的输出,我可以看到它缓存了DNS查找、TCP握手和SSL握手。postman在第一次调用API基本URI后会这样做。然后从第二次开始,DNS查找等会被缓存,从而节省了以后API调用相同基本URI的大量时间。
但在Flutter中,“连接建立”时间很长,即使检索数据的时间只有几毫秒。
如何避免连接延迟并获得最大性能?如果缓存SSL,DNS查找等是解决方案,我如何在flutter中做到这一点?

lx0bsm1f

lx0bsm1f1#

似乎很多人都有这个问题,所以,让我回答我自己的问题。

flutter能记住网络连接吗?是的,它能。

Flutter只需要对同一个API进行一次网络调用就可以记住连接。从第二次调用同一个API开始,它将使用它的“缓存”内存,给你一个很大的性能提升。
首先要记住,只有在多次调用同一个API的情况下,这种方法才有效。如果调用不同的API,这种方法就无效。但是,在许多应用中,您有一个由API团队构建的API,您将调用相同的应用吞吐量。
解决方案是使用flutter http.Client。然后在对同一个API的调用中共享同一个http.Client。您将看到只有第一个调用需要时间进行“连接”,其余的调用不需要时间。
flutter http pub page中有一个例子,它说:
如果您向同一服务器发出多个请求,则可以使用客户端而不是发出一次性请求来保持持久连接处于打开状态。如果执行此操作,请确保在完成后关闭客户端:
检查下面的例子。它只是供你参考,不是最好的使用方法。

主省道

import 'package:flutter/material.dart';
import 'package:network_test/role_service.dart';
import 'package:network_test/user_role_service.dart';
import 'package:network_test/user_service.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  var startTime = "";
  var endTime = "";

  void _network() async {
    var client = http.Client();

    RoleService _roleService = RoleService();
    UserService _userService = UserService();
    UserRoleService _userRoleService = UserRoleService();

    String authToken = "****";

    String uid = "555555";
    try {
      await _roleService.getAllRoles(authToken, client);
      //await _roleService.getAllRoles(authToken, client);
      await _userService.getUserByUID(authToken, uid, client);
      await _userService.getUserByID(authToken, 27, client);
      await _userRoleService.getUserRoleByUser(authToken, 27, client);
    } finally {
      client.close();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              "Start Time: " + startTime,
              style: Theme.of(context).textTheme.headline4,
            ),
            Text(
              "End Time: " + endTime,
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _network,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

角色服务.dart

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:network_test/role.dart';
import 'dart:convert' as convert;
import 'dart:io';

class RoleService with ChangeNotifier {
  late List<Role> _roles;
  String link2 = "https://api2.somewhere.com/userrel";

  /// Return roles
  List<Role> returnRoles() {
    return _roles;
  }

  /// Get all Roles
  Future<void> getAllRoles(String authToken, Client client) async {
    try {
      var data = await client.get(Uri.parse(link2 + "/role/getall"),
          headers: {HttpHeaders.authorizationHeader: "Bearer $authToken"});

     
      var jsonData =
          convert.json.decode(data.body).cast<Map<String, dynamic>>();
      _roles = jsonData.map<Role>((json) => Role.fromJson(json)).toList();
      print(_roles[0].roleName);
    } catch (error) {
      print(error);
      throw error;
    }
  }
}

现在我告诉你上面的不是最好的实践。2为什么?3因为你将在许多不同的地方创建和销毁http.Client。4让我们关注一个更好的实践。
在几乎每一个应用程序中,我们都使用状态管理。我是Provider的粉丝,它可以是你选择的任何东西。我发现最好的方法是让状态管理记住http.Client的创建。因为我使用的是Provider,所以我创建了下面的类。

import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';

class ConnectionService with ChangeNotifier {
  http.Client _client = http.Client();

  http.Client returnConnection() {
    return _client;
  }
}

这是我的主课

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  runApp(MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (context) => ConnectionService()),
    ],
    child: MyApp(),
  ));
}

现在,当应用程序打开时,我调用ConnectionService类来建立连接,并执行API调用,如检查用户身份验证、用户访问等。只有第一个调用会花时间建立连接,其他调用不会。

bttbmeg0

bttbmeg02#

我想补充一下PeakGen的回答。我使用Dio从我的Flutter应用发出API请求。我注意到我的所有API的连接等待时间〉200 ms,这降低了我的整体API性能。请参见下面的DevTool屏幕截图。

经过深入研究,我发现Dio使用套接字连接来发送/接收数据。我还发现它覆盖了idealTimeout参数,并将其值设置为3s。因此,每当API调用之间的延迟超过3s时,Dio必须打开一个新的套接字连接,因为前一个连接已经关闭。这会增加连接等待时间,并降低API的整体速度。
我将idealTimeout的值改为15 s,之后API性能得到了显著提高。

有一个单独的SO线程,我在其中描述了我是如何发现问题的,并更详细地解释了问题-here

相关问题