Flutter性能监控插件bucketeer_flutter_client_sdk的使用

Flutter性能监控插件bucketeer_flutter_client_sdk的使用

bucketeer_flutter_client_sdk 是一个用于在 Flutter 应用中集成 Bucketeer 功能的 SDK。Bucketeer 是一个开源平台,由 CyberAgent 创建,旨在帮助团队通过功能标志做出更好的决策,减少部署前置时间并降低发布风险。

安装

要将 bucketeer_flutter_client_sdk 添加到你的 Flutter 项目中,请在 pubspec.yaml 文件中添加以下依赖项:

dependencies:
  bucketeer_flutter_client_sdk: ^版本号

然后运行 flutter pub get 来安装依赖项。

使用示例

下面是一个完整的示例,展示了如何在 Flutter 应用中初始化 bucketeer_flutter_client_sdk 并获取特征标志(Feature Flags)的值。

import 'package:bucketeer_example/snack_bar.dart';
import 'package:flutter/material.dart';
import 'package:bucketeer_flutter_client_sdk/bucketeer_flutter_client_sdk.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:ua_client_hints/ua_client_hints.dart';

import 'constant.dart';

const keyUserId = 'key_user_id';

Future<Map<String, String>> userMap() async {
  final uaData = await userAgentData();
  return {
    'platform': uaData.platform, // e.g.. 'Android'
    'platformVersion': uaData.platformVersion, // e.g.. '10'
    'device': uaData.device, // e.g.. 'coral'
    'appName': uaData.package.appName, // e.g.. 'SampleApp'
    'appVersion': uaData.package.appVersion, // e.g.. '1.0.0'
    'packageName': uaData.package.packageName, // e.g.. 'jp.wasabeef.ua'
    'buildNumber': uaData.package.buildNumber, // e.g.. '1'
  };
}

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

  [@override](/user/override)
  State<MyApp> createState() => _AppState();
}

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class _AppState extends State<MyApp>
    with WidgetsBindingObserver
    implements BKTEvaluationUpdateListener {
  late final String _listenToken;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const MyHomePage(title: 'Bucketeer Demo'),
    );
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    Future(() async {
      // Generate UserId for Demo
      final prefs = await SharedPreferences.getInstance();
      var userId = prefs.getString(keyUserId);
      if (userId == null) {
        userId = 'demo-userId-${DateTime.now().millisecondsSinceEpoch}';
        await prefs.setString(keyUserId, userId);
      }
      final config = BKTConfigBuilder()
          .apiKey(Constants.apiKey)
          .apiEndpoint(Constants.apiEndpoint)
          .featureTag(Constants.exampleFeatureTag)
          .debugging(true)
          .eventsMaxQueueSize(Constants.exampleEventMaxQueueSize)
          .eventsFlushInterval(Constants.exampleEventsFlushInterval)
          .pollingInterval(Constants.examplePollingInterval)
          .backgroundPollingInterval(Constants.exampleBackgroundPollingInterval)
          .appVersion("1.0.0")
          .build();
      final user = BKTUserBuilder()
          .id(userId)
          .customAttributes({'app_version': "1.2.3"}).build();
      final result = await BKTClient.initialize(config: config, user: user);
      if (result.isSuccess) {
        _listenToken =
            await BKTClient.instance.addEvaluationUpdateListener(this);
      } else if (result.isFailure) {
        final errorMessage = result.asFailure.message;
        debugPrint(errorMessage);
      }
    });
    WidgetsBinding.instance.addObserver(this);
  }

  [@override](/user/override)
  void dispose() {
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);
    BKTClient.instance.removeEvaluationUpdateListener(_listenToken);
  }

  [@override](/user/override)
  void onUpdate() {
    // EvaluationUpdateListener onUpdate()
    debugPrint("EvaluationUpdateListener.onUpdate() called");
  }
}

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

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final flagController =
      TextEditingController(text: Constants.exampleFeatureId);
  final goalController = TextEditingController(text: 'bucketeer-goal-id');
  final userIdController = TextEditingController(text: Constants.exampleUserId);

  Future<void> _getStringVariation(String featureId) async {
    final result = await BKTClient.instance
        .stringVariation(featureId, defaultValue: 'default value');
    debugPrint('getStringVariation: $result');
    showSnackbar(title: 'getStringVariation', message: result);
  }

  Future<void> _getIntVariation(String featureId) async {
    final result =
        await BKTClient.instance.intVariation(featureId, defaultValue: 0);
    debugPrint('getIntVariation: $result');
    showSnackbar(title: 'getIntVariation', message: '$result');
  }

  Future<void> _getDoubleVariation(String featureId) async {
    final result =
        await BKTClient.instance.doubleVariation(featureId, defaultValue: 0.0);
    debugPrint('getDoubleVariation: $result');
    showSnackbar(title: 'getDoubleVariation', message: '$result');
  }

  Future<void> _getBoolVariation(String featureId) async {
    final result =
        await BKTClient.instance.boolVariation(featureId, defaultValue: false);
    debugPrint('getBoolVariation: $result');
    showSnackbar(title: 'getBoolVariation', message: '$result');
  }

  Future<void> _getObjectVariation(String featureId) async {
    final result =
    await BKTClient.instance.objectVariation(featureId, defaultValue: const BKTNumber(1.0));
    debugPrint('objectVariation: $result');
    showSnackbar(title: 'objectVariation', message: '${result.toJson()}');
  }

  Future<void> _getEvaluation(String featureId) async {
    // ignore: deprecated_member_use
    final result = await BKTClient.instance.evaluationDetails(featureId);
    debugPrint('Successful get evaluation details');
    if (result != null) {
      showSnackbar(
          title: 'getEvaluation(${result.toString()})',
          message: 'Successful the evaluation.');
    }
  }

  Future<void> _getJSONVariation(String featureId) async {
    final result =
      // ignore: deprecated_member_use
      await BKTClient.instance.jsonVariation(featureId, defaultValue: {});
    debugPrint('getJSONVariation: $result');
    showSnackbar(title: 'getJSONVariation', message: '$result');
  }

  Future<void> _sendGoal(String goalId) async {
    await BKTClient.instance.track(goalId, value: 3.1412);
    showSnackbar(title: 'sendGoal', message: 'Successful the send goal.');
  }

  Future<void> _switchUser(String userId) async {
    // Note: Please initialize the Bucketeer again when switching the user
    final config = BKTConfigBuilder()
        .apiKey(Constants.apiKey)
        .apiEndpoint(Constants.apiEndpoint)
        .featureTag(Constants.exampleFeatureTag)
        .debugging(true)
        .eventsMaxQueueSize(Constants.exampleEventMaxQueueSize)
        .eventsFlushInterval(Constants.exampleEventsFlushInterval)
        .pollingInterval(Constants.examplePollingInterval)
        .backgroundPollingInterval(Constants.exampleBackgroundPollingInterval)
        .appVersion("1.0.0")
        .build();
    final user = BKTUserBuilder()
        .id(userId)
        .customAttributes({'app_version': "1.2.3"}).build();

    await BKTClient.instance.destroy();

    final result = await BKTClient.initialize(
      config: config,
      user: user,
    );

    if (result.isSuccess || result.asFailure.exception is BKTTimeoutException) {
      /// BKTClient.initialize success
      const client = BKTClient.instance;
      await client.updateUserAttributes(
        {'app_version': "1.2.4"},
      );
      showSnackbar(title: 'setUser', message: 'Successful the switchUser.');
    } else {
      /// Print the error
      showSnackbar(
          title: 'initialize',
          message: 'Failed with error ${result.asFailure.message}');
    }
  }

  Future<void> _getCurrentUser() async {
    final userRs = await BKTClient.instance.currentUser();
    userRs.ifSuccess((user) {
      showSnackbar(
          title: 'getUser(${user.id})', message: user.attributes.toString());
    });
    userRs.ifFailure((message, exception) {
      showSnackbar(title: 'currentUser', message: 'Failed with error $message');
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: GestureDetector(
        onTap: () {
          FocusScope.of(context).unfocus();
        },
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24.0),
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  const SizedBox(height: 36.0),
                  const Text(
                    'Feature Flag Id',
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                  TextFormField(
                    controller: flagController,
                    decoration: const InputDecoration(
                        hintText: 'bucketeer-feature-flag'),
                  ),
                  const SizedBox(height: 12),
                  const Text('GET VARIATION',
                      style: TextStyle(fontWeight: FontWeight.bold)),
                  Wrap(
                    spacing: 8,
                    children: [
                      TextButton(
                          child: const Text('GET String param'),
                          onPressed: () async {
                            return _getStringVariation(flagController.text);
                          }),
                      TextButton(
                          child: const Text('GET int param'),
                          onPressed: () async {
                            return _getIntVariation(flagController.text);
                          }),
                      TextButton(
                          child: const Text('GET double params'),
                          onPressed: () async {
                            return _getDoubleVariation(flagController.text);
                          }),
                      TextButton(
                          child: const Text('GET bool params'),
                          onPressed: () async {
                            return _getBoolVariation(flagController.text);
                          }),
                      TextButton(
                          child: const Text('GET object params'),
                          onPressed: () async {
                            return _getObjectVariation(flagController.text);
                          }),
                      TextButton(
                          child: const Text('GET json params'),
                          onPressed: () async {
                            return _getJSONVariation(flagController.text);
                          }),
                      TextButton(
                          child: const Text('GET evaluation'),
                          onPressed: () async {
                            return _getEvaluation(flagController.text);
                          }),
                    ],
                  ),
                  const SizedBox(height: 36.0),
                  const Text(
                    'Goal Id',
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                  TextFormField(
                    controller: goalController,
                    decoration: InputDecoration(hintText: goalController.text),
                  ),
                  TextButton(
                      child: const Text('SEND GOAL'),
                      onPressed: () async {
                        return _sendGoal(goalController.text);
                      }),
                  const SizedBox(height: 36.0),
                  const Text(
                    'User Id',
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                  TextFormField(
                    controller: userIdController,
                    decoration:
                        InputDecoration(hintText: userIdController.text),
                  ),
                  Row(
                    children: [
                      TextButton(
                          child: const Text('SWITCH USER'),
                          onPressed: () async {
                            return _switchUser(userIdController.text);
                          }),
                      TextButton(
                        child: const Text('GET CURRENT USER'),
                        onPressed: () async {
                          return _getCurrentUser();
                        },
                      )
                    ],
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

更多关于Flutter性能监控插件bucketeer_flutter_client_sdk的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter性能监控插件bucketeer_flutter_client_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


Bucketeer Flutter Client SDK 是一个用于在 Flutter 应用中集成 Bucketeer 功能(如 A/B 测试、功能标志等)的插件。虽然它主要用于功能管理和实验,但也可以间接地用于性能监控,通过跟踪功能标志的使用情况和用户行为来评估性能。

安装插件

首先,你需要在 pubspec.yaml 文件中添加 bucketeer_flutter_client_sdk 依赖:

dependencies:
  bucketeer_flutter_client_sdk: ^1.0.0

然后运行 flutter pub get 来安装依赖。

初始化 SDK

在使用 Bucketeer Flutter Client SDK 之前,你需要初始化它。通常,你会在 main.dart 文件中进行初始化。

import 'package:bucketeer_flutter_client_sdk/bucketeer_flutter_client_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 初始化 Bucketeer SDK
  await BucketeerClient.initialize(
    apiKey: 'YOUR_API_KEY',
    featureTag: 'YOUR_FEATURE_TAG',
    user: BucketeerUser(id: 'USER_ID'),
  );

  runApp(MyApp());
}

使用功能标志

你可以使用 BucketeerClient 来获取功能标志的值,并根据这些值来决定应用的行为。

bool isFeatureEnabled = await BucketeerClient.getBoolVariation('FEATURE_KEY', defaultValue: false);

if (isFeatureEnabled) {
  // 执行某些操作
} else {
  // 执行其他操作
}

性能监控

虽然 Bucketeer Flutter Client SDK 本身并不是一个专门的性能监控工具,但你可以通过以下方式来间接监控性能:

  1. 跟踪功能标志的使用:通过记录功能标志的使用情况,你可以分析哪些功能对性能影响较大。

  2. 用户行为跟踪:通过 Bucketeer 的事件跟踪功能,你可以记录用户的行为,并分析这些行为对性能的影响。

  3. A/B 测试:通过 A/B 测试,你可以比较不同功能版本对应用性能的影响,并选择最优的版本。

事件跟踪

你可以使用 BucketeerClient 来跟踪自定义事件:

BucketeerClient.trackEvent('event_name', metadata: {'key': 'value'});
回到顶部