flutter SDK
Quick Tracking 性能體驗(yàn)SDK Flutter插件集成說明
應(yīng)用性能穩(wěn)定是良好用戶體驗(yàn)中非常關(guān)鍵的一環(huán),Quick Tracking 性能體驗(yàn)SDK Flutter插件通過輕量級的集成接入即可擁有實(shí)時(shí)、可靠、全面的應(yīng)用頁面性能、頁面幀率、dart異常、自定義異常等監(jiān)控能力,幫助開發(fā)者高效還原異常、卡頓用戶的訪問路徑和業(yè)務(wù)現(xiàn)場。
1. 環(huán)境準(zhǔn)備
Android:APM性能SDK,v2.0.0版本及以上,在工程App對應(yīng)build.gradle配置腳本dependencies段中添加組件庫依賴:
api 'com.lydaas.qtsdk:apm-efs:2.0.0'
iOS:APM性能SDK v2.0.0版本及以上,在項(xiàng)目根目錄的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
版本依賴關(guān)系
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時(shí),需要填寫參數(shù)Appkey。Appkey是在Quick Tracking中代表應(yīng)用的唯一ID,在創(chuàng)建應(yīng)用時(shí)生成,其獲取或查看方法詳見文檔:應(yīng)用管理。
收數(shù)域名獲取
在“管理控制臺-采集信息”模塊中獲取
2. 集成 Flutter APM 插件
性能體驗(yàn)SDK依賴統(tǒng)計(jì)分析SDK,您可以集成 flutter版本的統(tǒng)計(jì)分析,也可以集成Native版本的統(tǒng)計(jì)分析SDK。Flutter APM 和 Flutter Common SDK內(nèi)部已集成了Android和iOS SDK,如果工程類型是Flutter App可以只集成Flutter SDK
flutter統(tǒng)計(jì)SDK集成方式請參考文檔:qt_common_sdk
iOS統(tǒng)計(jì)分析SDK集成方式請參考文檔:iOS SDK
Android統(tǒng)計(jì)分析SDK集成方式請參考文檔:Android SDK
請注意:
集成Flutter SDK(已內(nèi)置原生APM和Common SDK依賴)不需要在原生項(xiàng)目中引入原生SDK,如果您的原生項(xiàng)目中的Cocoapods或手動(dòng)集成依賴庫內(nèi)存在Quick Tracking SDK依賴(原生QTCommon、原生APM),需要?jiǎng)h掉,否則可能會(huì)導(dǎo)致SDK沖突。
集成步驟
在工程pubspec.yaml中加入 dependencies
# 線上依賴
dependencies:
qt_common_sdk: ^2.0.0 //統(tǒng)計(jì)分析
qt_apm_sdk: ^2.3.0 //性能體驗(yàn)
注意: 如果需要兼容flutter2.8.1的版本,可以集成qt_apm_sdk: ^2.3.0-flutter-2.8.1
導(dǎo)入
import 'package:qt_apm_sdk/qt_apm_sdk.dart';
2.1 實(shí)例化設(shè)置
final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
name: '',
bver: '',
flutterVersion: '您使用的flutter版本',
engineVersion: '您使用的flutter引擎版本',
enableLog: true,
enableTrackingPageFps: true,
enableTrackingPagePerf: true,
errorFilter: {
"mode": "ignore",
"rules": [],
},
trackDomain: "您的收數(shù)服務(wù)域名",
initFlutterBinding: MyApmWidgetsFlutterBinding.ensureInitialized,
// onError: (exception, stack) {},
);
參數(shù)說明:
字段 | 含義 | 是否必填 | 類型 |
name | 應(yīng)用或模塊名稱 | 是 | string |
bver | 應(yīng)用或模塊版本+構(gòu)建號 | 是 | string |
flutterVersion | Flutter SDK 版本(默認(rèn)為空) | 否 | string |
engineVersion | flutter引擎版本 | 否 | string |
enableLog | 是否開啟SDK日志打印 (默認(rèn)關(guān)閉) | 否 | boolean |
enableTrackingPageFps | 開啟監(jiān)測頁面幀率(默認(rèn)關(guān)閉) | 否 | boolean |
enableTrackingPagePerf | 開啟監(jiān)測頁面性能(默認(rèn)關(guān)閉) | 否 | boolean |
errorFilter | 設(shè)置采集的異常黑白名單 | 否 | map |
trackDomain | 收數(shù)域名 | 是 | string |
initFlutterBinding | ApmWidgetsFlutterBinding的覆寫和初始化方法 | 否 | function |
onError | 拋出異常回調(diào) | 否 | function |
2.2 初始化SDK
確保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出現(xiàn)重復(fù)初始化綁定的異常造成無法正常初始化
SDK內(nèi)部已通過initFlutterBinding入?yún)肜^承的WidgetsFlutterBinding實(shí)現(xiàn)初始化操作
依賴ensureInitialized()初始化的代碼可在此調(diào)用
需要異步獲取設(shè)置應(yīng)用名稱和版本號可在此回調(diào)中操作
SDK實(shí)例化的設(shè)置可先將name和bver 為 "",然后通過以下方式進(jìn)行設(shè)置
初始化完整示例
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: "配置收數(shù)域名",
initFlutterBinding: MyApmWidgetsFlutterBinding.ensureInitialized,
// onError: (exception, stack) {},
);
qtApmSdk.init(appRunner: (observer) async {
// 確保去掉原有的WidgetsFlutterBinding.ensureInitialized() ,以免出現(xiàn)重復(fù)初始化綁定的異常造成無法正常初始化,
// SDK內(nèi)部已通過initFlutterBinding入?yún)肜^承的WidgetsFlutterBinding實(shí)現(xiàn)初始化操作
// 依賴ensureInitialized()初始化的代碼可在此調(diào)用
// 需要異步獲取設(shè)置應(yīng)用名稱和版本號可在此回調(diào)中操作
// SDK實(shí)例化的設(shè)置可先將name和bver 為 "",然后通過以下方式進(jìn)行設(shè)置
// 如: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) {
// 添加自己的實(shí)現(xiàn)邏輯
// print('AppLifecycleState changed to $state');
super.handleAppLifecycleStateChanged(state);
}
static WidgetsBinding ensureInitialized() {
// 等到初始化完成了再去Binding
if (WidgetsBinding.instance == null) {
MyApmWidgetsFlutterBinding();
}
return WidgetsBinding.instance;
}
}
2.3 注冊監(jiān)聽
在MyApp(StatelessWidget
或者StatefulWidget
)中注冊NavigatorObserver,實(shí)現(xiàn)MyApp(this._navigatorObserver)的構(gòu)造方法,在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 頁面監(jiān)控
頁面性能監(jiān)測
頁面性能檢測功能默認(rèn)關(guān)閉,如需開始需要設(shè)置enableTrackingPagePerf為true
示例:
final QuickTrackingFlutterApmSdk qtApmSdk = QuickTrackingFlutterApmSdk(
...
enableTrackingPagePerf: true,
...
);
qtApmSdk.init(appRunner: (observer) async {
qtApmSdk.name = 'qt_demo';
qtApmSdk.bver = '應(yīng)用版本+構(gòu)建號';
return MyApp(observer);
});
頁面幀率分析
頁面幀率(FPS)表示每秒傳輸?shù)膱D像幀數(shù),用于衡量視頻流暢度和畫面動(dòng)態(tài)處理能力。需要使用SDK的ApmScrollController實(shí)例,注冊滾動(dòng)控制器用于監(jiān)聽滾動(dòng)事件。
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滾動(dòng)控制器(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 異常監(jiān)控
Dart異常
SDK自動(dòng)監(jiān)控Dart異常信息,包含app內(nèi)的同步、異步異常(runZonedGuarded所管理的所有異常)、framework異常、用戶自定義上報(bào)的異常,上報(bào)的數(shù)據(jù)可以在平臺中查看。
請注意:Dart異常支持設(shè)置單設(shè)備每天上報(bào)Dart異常條數(shù)的上限,默認(rèn)20個(gè),最高支持120條/天
自定義異常
captureException(類型:Function)
入?yún)⑴渲?/p> | 含義 | 是否必傳 | 類型 |
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, []);
// 監(jiān)聽isolate異常
isolate.addErrorListener(RawReceivePort((pair) {
var error = pair[0];
var stacktrace = pair[1];
// 主動(dòng)采集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) {
// 主動(dòng)捕獲上報(bào)代碼執(zhí)行異常
ExceptionTrace.captureException(
exception: Exception(e), extra: {"user": '123'});
}
}
3.3 黑白名單設(shè)置ErrorFilter
用于設(shè)置采集的項(xiàng)的黑白名單,可以在黑名單和白名單中選擇其一,如果選擇白名單的方式,那么只有符合標(biāo)準(zhǔn)的頁面會(huì)被采集,如果選擇的是黑名單的方式,那么符合標(biāo)準(zhǔn)的頁面不會(huì)被采集。
此項(xiàng)非必須參數(shù),用于判斷是否過濾日志,包含如下屬性:
屬性 | 含義 | 默認(rèn) | 類型 |
mode | 匹配模式
| ignore | 枚舉值 ignore|match |
rules | 匹配規(guī)則集合
| [],該默認(rèn)值表示黑名單為空,日志全部上報(bào) | Array<string | RegExp > |
示例:
void main() {
QuickTrackingFlutterApmSdk(
name: '應(yīng)用或者模塊名稱',
// 過濾異常篩選
errorFilter: {
"mode": "match",
"rules": [RegExp('RangeError')],
},
....
);
}
4. Native SDK 集成
iOS SDK請參考文檔:iOS SDK
Android SDK請參考文檔:Android SDK
5.遠(yuǎn)程配置
目前我們根據(jù)不同的應(yīng)用權(quán)限提供了設(shè)備PV采樣率和單設(shè)備日志最大條數(shù)上報(bào)的設(shè)置功能,采樣率設(shè)置生效時(shí)間:修改采樣率配置后,服務(wù)端需要15分鐘下發(fā)最新的配置,SDK側(cè)在服務(wù)端配置生效后,再次冷啟動(dòng)時(shí)會(huì)拉取最新的配置,并將拉取到的配置緩存在本地,下次設(shè)備重新冷啟動(dòng)(SDK初始化)時(shí)配置生效。
【PV采樣率】解釋:設(shè)備啟動(dòng)后產(chǎn)生的頁面訪問行為會(huì)采集異常、性能、幀率的日志,通過云配采樣率控制以設(shè)備為維度的采集行為。例: PV采樣率 5% 100臺,設(shè)備通過端上的隨機(jī)計(jì)算是否能命中那5%的概率,命中即可通過PV行為采集各類型日志。
遠(yuǎn)程配置默認(rèn)規(guī)則:
flutter監(jiān)控:默認(rèn)開啟
PV采樣率:默認(rèn)5%,最高可調(diào)整到100%;pv采樣率也會(huì)影響flutter監(jiān)控,如果pv采樣率過低,當(dāng)采樣率未命中時(shí),flutter 監(jiān)控功能也不生效
頁面性能上限:支持設(shè)置單設(shè)備每天上報(bào)頁面性能的上限,默認(rèn)200個(gè),最高支持1000條/天
Dart異常上限:支持設(shè)置單設(shè)備每天上報(bào)Dart異常條數(shù)的上限,默認(rèn)20個(gè),最高支持120條/天
6. 驗(yàn)證SDK運(yùn)行
調(diào)整采樣率
在驗(yàn)證前需要將Flutter PV采樣率調(diào)至100%以確保可以命中日志采樣,線上可以按需配置
查看日志面板
如遇數(shù)據(jù)查詢不到的情況,請檢查產(chǎn)品【開關(guān)與采樣配置】中的 【Flutter監(jiān)控】是否是開啟的狀態(tài)(默認(rèn)開啟)