flutter SDK

Quick Tracking 性能体验SDK Flutter插件集成说明

应用性能稳定是良好用户体验中非常关键的一环,Quick Tracking 性能体验SDK Flutter插件通过轻量级的集成接入即可拥有实时、可靠、全面的应用页面性能、页面帧率、dart异常、自定义异常等监控能力,帮助开发者高效还原异常、卡顿用户的访问路径和业务现场。

1. 环境准备

  • Android:APM性能SDK,v2.0.0版本及以上,在工程App对应build.gradle配置脚本dependencies段中添加组件库依赖:

    • api 'com.lydaas.qtsdk:apm-efs:2.0.0'

  • iOS:APM性能SDK v2.0.0版本及以上,在项目根目录的Podfile中添加:

    • pod 'UMEFS_P', '2.0.0'

  • Flutter Common 版本,在工程pubspec.yaml中加入 dependencies:

    • qt_common_sdk: ^2.0.0

  • Flutter APM 版本,在工程pubspec.yaml中加入 dependencies:

    • qt_apm_sdk: ^2.3.0

版本依赖关系

qt_common_sdk: ^2.0.3:

|---依赖原生iOS 1.5.2.PX 版本

|---依赖原生 Android 1.6.1.PX 版本

qt_apm_sdk: ^2.3.0:

|---依赖原生 iOS

|-------UMAPM_P: 1.5.7.guomi

| -------UMEFS_P: 2.0.0

|---依赖原生 Android

|-------apm-crash:1.5.2.1.0.0.3_guomi

| -------apm-efs:2.0.0

Appkey获取

在初始化SDK时,需要填写参数Appkey。Appkey是在Quick Tracking中代表应用的唯一ID,在创建应用时生成,其获取或查看方法详见文档:应用管理

收数域名获取

在“管理控制台-采集信息”模块中获取

2. 集成 Flutter APM 插件

性能体验SDK依赖统计分析SDK,您可以集成 flutter版本的统计分析,也可以集成Native版本的统计分析SDK。Flutter APM 和 Flutter Common SDK内部已集成了Android和iOS SDK,如果工程类型是Flutter App可以只集成Flutter SDK

  • flutter统计SDK集成方式请参考文档:qt_common_sdk

  • iOS统计分析SDK集成方式请参考文档:iOS SDK

  • Android统计分析SDK集成方式请参考文档:Android SDK

请注意:

集成Flutter SDK(已内置原生APM和Common SDK依赖)不需要在原生项目中引入原生SDK,如果您的原生项目中的Cocoapods或手动集成依赖库内存在Quick Tracking SDK依赖(原生QTCommon、原生APM),需要删掉,否则可能会导致SDK冲突。

集成步骤

在工程pubspec.yaml中加入 dependencies

# 线上依赖
dependencies:
  qt_common_sdk: ^2.0.0 //统计分析
  qt_apm_sdk: ^2.3.0 //性能体验
说明

注意: 如果需要兼容flutter2.8.1的版本,可以集成qt_apm_sdk: ^2.3.0-flutter-2.8.1

导入

import 'package:qt_apm_sdk/qt_apm_sdk.dart';

2.1 实例化设置

final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
    name: '',
    bver: '',
    flutterVersion: '您使用的flutter版本',
    engineVersion: '您使用的flutter引擎版本',
    enableLog: true,
    enableTrackingPageFps: true,
    enableTrackingPagePerf: true,
    errorFilter: {
      "mode": "ignore",
      "rules": [],
    },
    trackDomain: "您的收数服务域名",
    initFlutterBinding: MyApmWidgetsFlutterBinding.ensureInitialized,
    // onError: (exception, stack) {},
  );

参数说明

字段

含义

是否必填

类型

name

应用或模块名称

string

bver

应用或模块版本+构建号

string

flutterVersion

Flutter SDK 版本(默认为空)

string

engineVersion

flutter引擎版本

string

enableLog

是否开启SDK日志打印 (默认关闭)

boolean

enableTrackingPageFps

开启监测页面帧率(默认关闭)

boolean

enableTrackingPagePerf

开启监测页面性能(默认关闭)

boolean

errorFilter

设置采集的异常黑白名单

map

trackDomain

收数域名

string

initFlutterBinding

ApmWidgetsFlutterBinding的覆写和初始化方法

function

onError

抛出异常回调

function

2.2 初始化SDK

  • 确保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出现重复初始化绑定的异常造成无法正常初始化

  • SDK内部已通过initFlutterBinding入参带入继承的WidgetsFlutterBinding实现初始化操作

  • 依赖ensureInitialized()初始化的代码可在此调用

  • 需要异步获取设置应用名称和版本号可在此回调中操作

  • SDK实例化的设置可先将name和bver 为 "",然后通过以下方式进行设置

初始化完整示例

import 'package:qt_apm_sdk/qt_apm_sdk.dart';

void main() {
  final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
    name: '',
    bver: '',
    flutterVersion: '3.10.0',
    engineVersion: 'd44b5a94c9',
    enableLog: true,
    enableTrackingPageFps: true,
    enableTrackingPagePerf: true,
    errorFilter: {
      "mode": "ignore",
      // "rules": [RegExp('RangeError')],
      "rules": [],
    },
    trackDomain: "配置收数域名",
    initFlutterBinding: MyApmWidgetsFlutterBinding.ensureInitialized,
    // onError: (exception, stack) {},
  );

  qtApmSdk.init(appRunner: (observer) async {
    // 确保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出现重复初始化绑定的异常造成无法正常初始化,
    // SDK内部已通过initFlutterBinding入参带入继承的WidgetsFlutterBinding实现初始化操作
    // 依赖ensureInitialized()初始化的代码可在此调用
    // 需要异步获取设置应用名称和版本号可在此回调中操作
    // SDK实例化的设置可先将name和bver 为 "",然后通过以下方式进行设置
    // 如:qtApmSdk.name = 'app_demo';
    qtApmSdk.name = 'app_demo';
    qtApmSdk.bver = '1.0.0+9';
    return MyApp(observer);
  });
}

class MyApmWidgetsFlutterBinding extends ApmWidgetsFlutterBinding {
  @override
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
    // 添加自己的实现逻辑
    // print('AppLifecycleState changed to $state');
    super.handleAppLifecycleStateChanged(state);
  }

  static WidgetsBinding ensureInitialized() {
    // 等到初始化完成了再去Binding
    if (WidgetsBinding.instance == null) {
      MyApmWidgetsFlutterBinding();
    }
    return WidgetsBinding.instance;
  }
}

2.3 注册监听

在MyApp(StatelessWidget或者StatefulWidget)中注册NavigatorObserver,实现MyApp(this._navigatorObserver)的构造方法,在navigatorObservers中添加ApmNavigatorObserver.singleInstance

class MyApp extends StatelessWidget {
  MyApp([this._navigatorObserver]);
  NavigatorObserver? _navigatorObserver;
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      routes: routes,
      initialRoute: "/",
      navigatorObservers: <NavigatorObserver>[
        _navigatorObserver ?? ApmNavigatorObserver.singleInstance
      ],
    );
  }
}

3. SDK API

3.1 页面监控

页面性能监测

页面性能检测功能默认关闭,如需开始需要设置enableTrackingPagePerf为true

示例:

final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
  ...
  enableTrackingPagePerf: true,
  ...
);

qtApmSdk.init(appRunner: (observer) async {
  qtApmSdk.name = 'qt_demo';
  qtApmSdk.bver = '应用版本+构建号';
  return MyApp(observer);
});

页面帧率分析

页面帧率(FPS)表示每秒传输的图像帧数,用于衡量视频流畅度和画面动态处理能力。需要使用SDK的ApmScrollController实例,注册滚动控制器用于监听滚动事件。

import 'package:flutter/material.dart';
import 'package:qt_apm_sdk/qt_apm_sdk.dart';

class ScrollLazyLoadPage extends StatefulWidget {
  @override
  _ScrollLazyLoadPageState createState() => _ScrollLazyLoadPageState();
}

class _ScrollLazyLoadPageState extends State<ScrollLazyLoadPage> {
  List<String> imageUrls = [];
  int page = 1;

  // 使用APM滚动控制器(ApmScrollController)
  ScrollController _scrollController = ApmScrollController();
  bool isLoading = false;

  @override
  void initState() {
    super.initState();
    fetchData();

    _scrollController.addListener(() {
      if (_scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
        fetchData();
      }
    });
  }

  Future<void> fetchData() async {
    if (!isLoading) {
      setState(() {
        isLoading = true;
      });

      // Simulating a delay of 2 seconds
      await Future.delayed(Duration(seconds: 2));

      final List<String> urls = [
        'https://img_01.jpg',
        'https://img_02.jpg',
        'https://img_03.jpg',
        ...
      ];

      try {
        setState(() {
          imageUrls.addAll(urls);
          isLoading = false;
        });
      } catch (e) {}
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Scroll Lazy Load Demo'),
      ),
      body: GridView.builder(
        controller: _scrollController,
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 2,
          mainAxisSpacing: 10,
          crossAxisSpacing: 10,
        ),
        itemCount: imageUrls.length + 1,
        itemBuilder: (context, index) {
          if (index == imageUrls.length) {
            return Center(
              child:
                  isLoading ? CircularProgressIndicator() : SizedBox.shrink(),
            );
          }
          return Card(
            child: Image.network(
              imageUrls[index],
              fit: BoxFit.cover,
            ),
          );
        },
      ),
    );
  }
}

3.2 异常监控

Dart异常

SDK自动监控Dart异常信息,包含app内的同步、异步异常(runZonedGuarded所管理的所有异常)、framework异常、用户自定义上报的异常,上报的数据可以在平台中查看。

请注意:Dart异常支持设置单设备每天上报Dart异常条数的上限,默认20个,最高支持120条/天

自定义异常

captureException(类型:Function)

入参配置

含义

是否必传

类型

exception

异常摘要

Exception

stack

异常堆栈

String

extra

自定义属性

Map<String, dynamic>

案例一

import 'package:qt_apm_sdk/qt_apm_sdk.dart';

void main() {

  Isolate isolate = await Isolate.spawn(runIsolate, []);

  // 监听isolate异常
  isolate.addErrorListener(RawReceivePort((pair) {
    var error = pair[0];
    var stacktrace = pair[1];
 
    // 主动采集isolate异常
    ExceptionTrace.captureException(
      exception: Exception(error),
      stack: stacktrace.toString());
   }).sendPort);
}

案例二

import 'package:qt_apm_sdk/qt_apm_sdk.dart';

void main() {	
  try {     
   List<String> numList = ['1', '2'];     
   print(numList[5]);
  } catch (e) {
   // 主动捕获上报代码执行异常
   ExceptionTrace.captureException(
   exception: Exception(e), extra: {"user": '123'});
  }
}

3.3 黑白名单设置ErrorFilter

用于设置采集的项的黑白名单,可以在黑名单和白名单中选择其一,如果选择白名单的方式,那么只有符合标准的页面会被采集,如果选择的是黑名单的方式,那么符合标准的页面不会被采集。

此项非必须参数,用于判断是否过滤日志,包含如下属性:

属性

含义

默认

类型

mode

匹配模式

  • 当值为ignore,表示黑名单模式,命中规则的不上报

  • 当值为match,表示白名单模式命中规则的上报

ignore

枚举值 ignore|match

rules

匹配规则集合

  • 当类型为数组时,表示规则集合

  • 规则之间为或的关系,只要任意一个规则命中,则规则集命中

[],该默认值表示黑名单为空,日志全部上报

Array<string | RegExp >

示例:

void main() {
 QuickTrackingFlutterApmSdk(
   name: '应用或者模块名称',
   // 过滤异常筛选
   errorFilter: {
     "mode": "match",
     "rules": [RegExp('RangeError')],
   },
   ....
 );
}

4. Native SDK 集成

5.远程配置

目前我们根据不同的应用权限提供了设备PV采样率和单设备日志最大条数上报的设置功能,采样率设置生效时间:修改采样率配置后,服务端需要15分钟下发最新的配置,SDK侧在服务端配置生效后,再次冷启动时会拉取最新的配置,并将拉取到的配置缓存在本地,下次设备重新冷启动(SDK初始化)时配置生效。

【PV采样率】解释:设备启动后产生的页面访问行为会采集异常、性能、帧率的日志,通过云配采样率控制以设备为维度的采集行为。例: PV采样率 5% 100台,设备通过端上的随机计算是否能命中那5%的概率,命中即可通过PV行为采集各类型日志。

远程配置默认规则:

  • flutter监控:默认开启

  • PV采样率:默认5%,最高可调整到100%;pv采样率也会影响flutter监控,如果pv采样率过低,当采样率未命中时,flutter 监控功能也不生效

  • 页面性能上限:支持设置单设备每天上报页面性能的上限,默认200个,最高支持1000条/天

  • Dart异常上限:支持设置单设备每天上报Dart异常条数的上限,默认20个,最高支持120条/天

6. 验证SDK运行

调整采样率

在验证前需要将Flutter PV采样率调至100%以确保可以命中日志采样,线上可以按需配置

image

查看日志面板

image

如遇数据查询不到的情况,请检查产品【开关与采样配置】中的 【Flutter监控是否是开启的状态(默认开启)

image