With the booming development of mobile smart devices, a mobile multi-terminal development framework has become a general trend. Download the Flutter Analysis and Practice: Evolution and Innovation of Xianyu Technologies eBook for step-by-step analyses based on real-life problems, as well as clear explanations of the important concepts of Flutter.
We now have various Application Performance Monitoring (APM) means. During native development, we have developed many SDKs to monitor online performance data. However, Flutter has made many revolutionary changes compared with native. As a result, all native performance monitoring on Flutter pages is invalid. To solve this problem, Xianyu launched a project called "Flutter High-Availability SDK" in December 2018. This project aims to make Flutter pages as measurable as native pages.
Since performance monitoring is mature, we have sufficient resources for reference. Xianyu references the performance monitoring SDKs of the high EMAS availability of Taobao Mobile, the Matrix of WeChat, and the Hertz of Meituan to determine the performance metrics to be collected and the features required for the SDKs based on the actual Flutter situation.
1) Page sliding smoothness
Frames Per Second (FPS) is the main metric for page sliding smoothness. However, the FPS cannot be used to distinguish between a large number of slight frame lag events and a small number of serious frame lag events. These two types of frame lag events impact the user experience differently. Therefore, in addition to the FPS, we also introduced the sliding duration and frame dropping duration to measure the page sliding smoothness.
2) Page loading duration
The time consumed by page loading better reflects the interaction duration of users. The interaction duration is the period from the time when the user starts route redirection to the time when the interaction ends during page loading.
3) Page exceptions
Exceptions are basic metrics for evaluating user experience. We need to determine whether a version is qualified by collecting page exceptions and calculating the page exception rate.
1) Accuracy
Accuracy is a basic requirement of a performance monitoring SDK because developers may spend a lot of unnecessary time on troubleshooting due to false positives.
2) Online monitoring
Online monitoring states that the cost of collecting data cannot be too high, and the monitoring cannot affect app performance.
3) Easy to develop
The ultimate goal of an open-source project is that everyone gets involved and contributes to the community. Therefore, the SDK must be easy to develop, and a series of specifications are needed for development.
This section takes the collection of the instantaneous FPS as an example to describe the overall design of the SDK.
First, the FpsRecorder
class inherited from BaseRecorder
needs to be implemented. This class is used to obtain the pop and push time of pages at the business layer, as well as the time (provided by FlutterBinding
) when page rendering starts and ends and tap events occur and calculate the source data according to the time. For the instantaneous FPS, the source data is the duration of each frame.
class FpsRecorder extends BaseRecorder {
///...
@override
void onReceivedEvent(BaseEvent event) {
if (event is RouterEvent) {
///...
} else if (event is RenderEvent) {
switch (event.eventType) {
case RenderEventType.beginFrame:
_frameStopwatch.reset();
_frameStopwatch.start();
break;
case RenderEventType.endFrame:
_frameStopwatch.stop();
PerformanceDataCenter().push(FrameData (_frameStopwatch.elapsedMicroseconds));
break;
}
} else if (event is UserInputEvent) {
///...
}
}
@override
List<Type> subscribedEventList() {
return <Type>[RenderEvent, RouterEvent, UserInputEvent];
}
}
We start from beginFrame
and end at endFrame
to get the duration of each frame. The collected duration of each frame is encapsulated as FrameData
and pushed to the PerformanceDataCenter
. The PerformanceDataCenter
distributes the data to the Processor that has subscribed to the FrameData
. Therefore, we need to create an FpsProcessor
to subscribe to and process the source data.
class FpsProcessor extends BaseProcessor {
///...
@override
void process(BaseData data) {
if (data is FrameData) {
///...
if (isFinishedWithSample(currentTime)) {
///当时间间隔大于1s,则计算一次FPS
_startSampleTime = currentTime;
collectSample(currentTime);
}
}
}
@override
List<Type> subscribedDataList() {
return [FrameData];
}
void collectSample(int finishSampleTime) {
///...
PerformanceDataCenter().push(FpsUploadData(avgFps: fps));
}
///...
}
The FpsProcessor
collects the duration of each frame and calculates the instantaneous FPS of 1s. Similarly, the calculated FPS is encapsulated as an FpsUploadData
and then pushed to the PerformanceDataCenter
. The PerformanceDataCenter
sends the FpsUploadData
to the Uploader that has subscribed to it for processing. Therefore, we need to create a MyUploader
to subscribe to and process the data.
class MyUploader extends BaseUploader {
@override
List<Type> subscribedDataList() {
return <Type>[
FpsUploadData, //TimeUploadData, ScrollUploadData, ExceptionUploadData,
];
}
@override
void upload(BaseUploadData data) {
if (data is FpsUploadData) {
_sendFPS(data.pageInfoData.pageName, data.avgFps);
}
///...
}
}
The Uploader can select the UploadData
to be subscribed to by using subscribedDataList()
, and receive and report notifications by using upload()
. Theoretically, one Uploader corresponds to one upload channel. Users can report data to different places as needed by using the LocalLogUploader
and NetworkUploader
.
An SDK can be divided into four layers and uses the publish/subscribe model extensively. The four layers communicate with each other through PerformanceDataCenter
and PerformanceEventCenter
, as shown in Figure 4-12. This mode can completely decouple layers and process data more flexibly.
Figure 4-12
The API layer contains some externally exposed APIs. For example, init()
needs to be called before runApp()
, and the business layer needs to call the pushEvent()
method to provide some calling opportunities for the SDK.
The Recorder layer uses the opportunities provided by events to collect source data and submits the data to the Processor that has subscribed to the data for processing. For example, the duration of each frame in the FPS collection is a piece of source data. This layer is designed to use the source data in different places. For example, the duration of each frame can be used to calculate the FPS and the number of lagging seconds.
Before the source data is used, the BaseRecoder
must be inherited to select the event for subscription through subscribedEventList()
. Then, it processes received event through onReceivedEvent()
.
abstract class BaseRecorder with TimingObserver {
BaseRecorder() {
PerformanceEventCenter().subscribe(this, subscribedEventList());
}
}
mixin TimingObserver {
void onReceivedEvent(BaseEvent event);
List<Type> subscribedEventList();
}
At this layer, the source data is processed into the final data that can be reported and is submitted to the Uploader that has subscribed to the data for reporting. For example, in the FPS collection, the FPS over a period of time can be obtained based on the collected duration of each frame.
The BaseProcessor
must be inherited to select the subscribed data type through subscribedDataList()
. Then, it processes the received data through process()
.
abstract class BaseProcessor{
void process(BaseData data);
List<Type> subscribedDataList();
BaseProcessor(){
PerformanceDataCenter().registerProcessor(this, subscribedDataList());
}
}
This layer is implemented by users because they want to report data to different places. Therefore, the SDK provides a base class. You only need to follow the specifications of the base class to obtain the subscribed data.
The BaseUploader
must be inherited to select the subscribed data type through subscribedDataList()
. Then, it processes the received UploadData
through upload()
.
abstract class BaseUploader{
void upload(BaseUploadData data);
List<Type> subscribedDataList();
BaseUploader(){
PerformanceDataCenter().registerUploader(this, subscribedDataList());
}
}
This layer receives BaseData (source data) and UploadData
(processed data), and distributes the call opportunities to the Processor and Uploader that have subscribed to them for processing.
In the BaseProcessor
and BaseUploader
constructors, the register method of the PerformanceDataCenter
is called for registration. This operation stores Processor and Uploader instances in two corresponding maps of the PerformanceDataCenter
. This data structure allows one DataType
to map multiple subscribers.
final Map<Type, Set<BaseProcessor>> _processorMap = <Type, Set<BaseProcessor>>{};
final Map<Type, Set<BaseUploader>> _uploaderMap = <Type, Set<BaseUploader>>{};
As shown in Figure 4-13, when the PerformanceDataCenter.push()
method is called to push data, data is distributed based on the data type, and is sent to all the Processors or Uploaders that have subscribed to the data type.
Figure 4-13
The design idea of this layer is similar to the PerformanceDataCenter
layer. However, this layer receives events (the opportunities) provided by the business layer and distributes these opportunities to their Recorders that have subscribed to them for processing. The main types of events are:
We need to know that the Flutter High-Availability SDK only collects data and that subsequent data reporting and data presentation must be customized based on actual conditions. Based on this, the following briefly introduces how to use the high-availability SDK.
To use the SDK, you only need to focus on the API and Uploader layers and perform the following operations:
init()
method to initialize the SDK before calling the runApp()
methodpushEvent()
method to provide some necessary opportunities for the SDK, such as the pop and push events of the routeXianyu has optimized the data accuracy of the Flutter High-Availability SDK many times, solved many problems in exception scenarios, and implemented a refactor. So far, the SDK has been running stably on Xianyu. No stability issues caused by the high-availability SDK have occurred, and the data collection accuracy has stabilized after several optimizations.
Flutter high availability focuses on the collection of performance data. Therefore, Xianyu needs to use the existing capabilities of the Alibaba Group for data reporting and data presentation. Xianyu uses the EMAS backend data processing and frontend data presentation capabilities of Taobao Mobile to report and display the data collected online by the high-availability SDK. This allows Flutter pages to compete with native pages.
Flutter Analysis and Practice: Design of the Performance Stability Monitoring Solution
Flutter Analysis and Practice: Evolution and Innovation of Flutter-Based Architecture
56 posts | 4 followers
FollowXianYu Tech - September 10, 2020
Alibaba Clouder - September 21, 2020
XianYu Tech - September 8, 2020
XianYu Tech - September 7, 2020
XianYu Tech - September 9, 2020
XianYu Tech - September 7, 2020
56 posts | 4 followers
FollowHelp enterprises build high-quality, stable mobile apps
Learn MoreAn enterprise-level continuous delivery tool.
Learn MoreProvides comprehensive quality assurance for the release of your apps.
Learn MoreAlibaba Cloud (in partnership with Whale Cloud) helps telcos build an all-in-one telecommunication and digital lifestyle platform based on DingTalk.
Learn MoreMore Posts by XianYu Tech