大部分情况下我们需要对产品,特定功能进行数据监测收集(性能,crash,api异常),以便对后续的改进提供有效的数据依据。作为一名前端开发,我们有必要收集用户在使用产品中各种行为、异常数据。

Firebase产品免费又很好用,可惜域名被国内ban了没法用呀。

数据监测的产品有GrowingIO,神策之类的,最后我选了免费的百度移动统计,早先对百度统计的“实时访客”好感甚佳…

在github上找到一位朋友写的wrap百度移动统计SDK的包。(v7lin/fake_analytics)这位朋友还wrap了很多其他的SDK(微信、新浪微博、支付宝、okhttp)。

这也是我为什么选择在这个时间点玩flutter,大部分你遇到要用的东西,应该都有人写好了,除非特别生僻的领域。

pubspec.yml

1
2
3
4
dependencies:
fake_analytics:
git:
url: https://github.com/v7lin/fake_analytics.git

这位仁兄的包引不进来,于是我自己把源码复制到项目里来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
BaiduAnalytics.dart

class BaiduAnalytics {
static const String _METHOD_STARTWORK = 'startWork';
static const String _METHOD_SIGNUP = 'signUp';
static const String _METHOD_SIGNIN = 'signIn';
static const String _METHOD_SIGNOUT = 'signOut';
static const String _METHOD_TRACKEVENT = 'trackEvent';
static const String _METHOD_STARTEVENTTRACKING = 'startEventTracking';
static const String _METHOD_STOPEVENTTRACKING = 'stopEventTracking';
static const String _METHOD_STARTPAGETRACKING = 'startPageTracking';
static const String _METHOD_STOPPAGETRACKING = 'stopPageTracking';

static const String _ARGUMENT_KEY_APPKEY = 'appKey';
static const String _ARGUMENT_KEY_APPCHANNEL = 'appChannel';
static const String _ARGUMENT_KEY_ENABLEDEBUG = 'enableDebug';

static const String _ARGUMENT_KEY_USERID = 'userId';

static const String _ARGUMENT_KEY_EVENTID = 'eventId';
static const String _ARGUMENT_KEY_EVENTLABEL = 'eventLabel';

static const String _ARGUMENT_KEY_PAGENAME = 'pageName';

static const MethodChannel _channel =
const MethodChannel('plugins.flutter.io/fake_analytics');

/// 开始统计
Future<void> startWork({
@required String appKey,
@required AsyncValueGetter<String> appChannel,
bool enableDebug: false,
}) async {
assert(appKey != null && appKey.isNotEmpty);
assert(appChannel != null);
String channelId = await appChannel();
assert(channelId != null && channelId.isNotEmpty);
await _channel.invokeMethod(
_METHOD_STARTWORK,
<String, dynamic>{
_ARGUMENT_KEY_APPKEY: appKey,
_ARGUMENT_KEY_APPCHANNEL: channelId,
_ARGUMENT_KEY_ENABLEDEBUG: enableDebug,
},
);
}

BaiduAnalyticsObserver.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
typedef String ScreenNameExtractor(RouteSettings settings);

String defaultNameExtractor(RouteSettings settings) => settings.name;

class BaiduAnalyticsObserver extends RouteObserver<PageRoute<dynamic>> {

BaiduAnalyticsObserver({
@required this.analytics,
this.nameExtractor = defaultNameExtractor,
Function(PlatformException error) onError,
}) : _onError = onError;

final BaiduAnalytics analytics;
final ScreenNameExtractor nameExtractor;
final void Function(PlatformException error) _onError;


@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
super.didPush(route, previousRoute);

if (previousRoute != null && previousRoute is PageRoute) {
String pageName = nameExtractor(previousRoute.settings);
if(pageName != null) analytics.stopPageTracking(
pageName: pageName,
);
}
if (route != null && route is PageRoute) {
String pageName = nameExtractor(route.settings);
if(pageName != null) analytics.startPageTracking(
pageName: pageName,
);
}
}

Observer主要hook了didPush和didPop,也就是进入页面和返回上一个页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class MyApp extends StatelessWidget {

static BaiduAnalytics analytics = BaiduAnalytics();
static BaiduAnalyticsObserver observer = BaiduAnalyticsObserver(analytics: analytics);

// This widget is the root of your application.
@override
Widget build(BuildContext context) {

if (Platform.isAndroid) {
analytics.startWork(
appKey: 'f05f6201d8',
appChannel: () => Future.value('official'),
enableDebug: true,
);
} else if (Platform.isIOS) {
analytics.startWork(
appKey: '404ff27775',
appChannel: () => Future.value('official'),
enableDebug: true,
);
}

return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
navigatorObservers: <NavigatorObserver>[observer],

当在Flutter的MaterialApp组件中可以传递一组NavigatorObserver,来对路由变化进行观察,这样我们就可以通过实现这个Observer,来对导航级别的行为进行监测。
在入口页初始化一个observer,初始化analytics,填入你申请到的appKey启动sdk。
导航的时候给路由设置一个name来命名要进入的页面。

1
2
3
4
5
6
7
8
9
10
void _goSearch(){
var routeName = '/search/'+currentType;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchPage( config: SearchConfig(type: currentType) ),
settings: RouteSettings(name: routeName)
)
);
}

最后收集到的数据。用户的行为路径,首页->搜索结果页->过滤->结果页

数据汇总

百度统计移动SDK还提供了事件监测,某些特定的页面还可以上报特定的事件来做针对性分析。

监测的难点主要在于大部分厂商提供的是原生(android、ios)的SDK,你可能需要写Flutter插件来跟原生代码通讯交互集成。不会很复杂,但就是费时间(很幸运百度移动统计有人写过哈哈

在Flutter内部主要是通过navigation observer来监测页面级别的行为,给每个路由命好特定的名称,最后通过收集到路由名称的打开时序,来确定用户的访问路径达到一定的分析目的。