flutter SDK

更新时间:
复制 MD 格式

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.4'

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

    • pod 'UMEFS_P', '2.2.0'

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

    • qt_common_sdk: ^2.1.2

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

    • qt_apm_sdk: ^2.6.1

版本依赖关系

qt_common_sdk: ^2.1.2:

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

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

qt_apm_sdk: ^2.6.0:

|---依赖原生 iOS

|-------UMAPM_P: 2.2.0

| -------UMEFS_P: 2.2.0

|---依赖原生 Android

|-------apm-crash:2.0.0

| -------apm-efs:2.0.4

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.1.2 //统计分析
  qt_apm_sdk: ^2.6.1 //性能体验
说明

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

导入

import 'package:qt_apm_sdk/qt_apm_sdk.dart';

混淆配置

Flutter Build APK默认会开启R8如果您的应用使用了代码混淆,请添加如下配置,以避免Quick Tracking SDK被错误混淆导致SDK不可用。

# 保留 QuickTrackingSDK 的类(替换包名)
-keep class com.quick.qt.** { *; }
-keep class com.umeng.** { *; }
-keep class com.uc.** { *; }
-keep class com.efs.** { *; }
-dontwarn com.quick.qt.analytics.middle.DevLog

# 保留所有 Okio 相关的类(OkHttp 的依赖库)
-dontwarn okio.**
-dontwarn javax.annotation.**
-dontwarn okhttp3.**

-keepclassmembers class *{
     public<init>(org.json.JSONObject);
}
-keepclassmembers enum *{
      publicstatic**[] values();
      publicstatic** valueOf(java.lang.String);
}

SDK需要引用导入工程的资源文件,通过了反射机制得到资源引用文件R.java,但是在开发者通过proguard等混淆/优化工具处理apk时,proguard可能会将R.java删除,如果遇到这个问题,请添加如下配置:

-keep public class [您的应用包名].R$*{
public static final int *;
} 

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