效能分析
效能移動應用使用者來說相當重要,使用者希望應用程式有流暢的捲動和優雅的動畫,不願看到卡頓和掉幀現象。我們如何確保我們的應用程式在各種裝置上不會受到卡頓的影響?
以下兩種方式可供選擇:首先,我們可以在不同的裝置對應用程式進行手動測試。這種方式適用於較小的應用程式,但隨著應用程式擴充套件性的提升,它將變得更加繁瑣。另外,我們可以執行整合測試,執行特定任務並記錄效能時間軸。然後,我們可以檢驗結果,以確定是否需要對我們應用程式的特定部分進行改善。
在本文中,我們將學習如何在執行特定任務時編寫記錄效能時間軸的測試,並將結果的摘要儲存到本地檔案中。
步驟:
-
編寫一個捲動清單的測試專案;
-
記錄應用程式的效能;
-
將結果儲存到磁碟;
-
執行測試;
-
檢查結果。
1. 編寫一個捲動清單的測試專案
在這一小節,我們將記錄當捲動清單條目時應用程式的效能。為了專注於效能分析,這一小節在元件測試中 Scrolling in integration tests(清單捲動整合測試) 的基礎上進行。
請按照基礎章節的指南新建一個應用程式,編寫一個測試程式。最終,確保應用程式按預期執行。
2. 記錄應用程式的效能
然後,我們需要再應用程式的清單捲動的時候記錄它的效能。使用 IntegrationTestWidgetsFlutterBinding
類別中的 traceAction()
方法實現這項功能。
這種方式執行提供的方法,並將應用程式效能的詳細訊息記錄在 Timeline
中。在這個範例中,我們提供一個方法,用以捲動清單的條目並確保指定條目是否被顯示出來。當方法執行完成的時候,traceAction()
會回傳一個 Timeline
。
當執行一個以上的 traceAction
的時候需要指定 reportKey
。預設情況下,所有的 Timelines
都會存在 timeline
裡,在這個例子中,reportKey
被修改為了 scrolling_timeline
:
await binding.traceAction(
() async {
// Scroll until the item to be found appears.
await tester.scrollUntilVisible(
itemFinder,
500.0,
scrollable: listFinder,
);
},
reportKey: 'scrolling_timeline',
);
3. 將結果儲存到磁碟
我們已經獲取了一個效能時間軸,我們需要一種方式來對它進行檢驗,
Timeline
物件提供所有已發生事件的相關詳細訊息,但它不提供快捷方式檢視結果。
因此,我們可以將 Timeline
轉換成 TimelineSummary
,
TimelineSummary
透過執行兩個任務可以使我們更容易的檢查結果:
-
將一個 json 檔案寫入磁碟,它包含了
Timeline
中包含的資料的摘要。此摘要包括掉幀數量,最慢建立時間等的訊息。 -
它可以將完整的
Timeline
以 json 檔案的形式儲存在磁碟上,可以使用 Chrome 瀏覽器的追蹤工具開啟此檔案。追蹤工具在這裡:chrome://tracing
。
為了捕獲結果內容,需要在 test_driver
資料夾中新建一個 perf_driver.dart
檔案,並加入如下程式碼:
import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';
Future<void> main() {
return integrationDriver(
responseDataCallback: (data) async {
if (data != null) {
final timeline = driver.Timeline.fromJson(
data['scrolling_timeline'] as Map<String, dynamic>,
);
// Convert the Timeline into a TimelineSummary that's easier to
// read and understand.
final summary = driver.TimelineSummary.summarize(timeline);
// Then, write the entire timeline to disk in a json format.
// This file can be opened in the Chrome browser's tracing tools
// found by navigating to chrome://tracing.
// Optionally, save the summary to disk by setting includeSummary
// to true
await summary.writeTimelineToFile(
'scrolling_timeline',
pretty: true,
includeSummary: true,
);
}
},
);
}
你可以自定義 integrationDriver
函式的 responseDataCallback
方法,預設情況下,它會將結果寫入 integration_response_data.json
檔案,不過你也可以透過這個例子裡的方法重寫為生成摘要。
4. 執行測試
在我們為了捕獲一個效能 Timeline
設定了測試程式碼,並且將結果的摘要儲存在了磁碟上,我們可以使用以下指令執行測試程式碼:
flutter drive \
--driver=test_driver/perf_driver.dart \
--target=integration_test/scrolling_test.dart \
--profile
--profile
指令行選項代表著應用將以 profile 模式 (效能模式) 執行,這種模式下執行的應用會比 debug 模式更接近最終使用者的體驗。
5. 檢查結果
在測試程式碼執行成功以後,在專案根目錄下的 build
資料夾裡包含以下兩個檔案:
-
scrolling_summary.timeline_summary.json
包含摘要。可以使用任何文字編輯器開啟它並檢視其中包含的訊息。透過更高階的設定,我們可以在每次測試時儲存摘要並建立一個結果圖。 -
scrolling_timeline.timeline.json
包含完整的時間軸資料。使用 Chorme 瀏覽器的追蹤工具開啟這個檔案。追蹤工具在這裡:chrome://tracing
。追蹤工具提供了一個便捷的使用者介面,用以檢測時間軸資料並發現其中導致效能問題的源頭。
摘要的範例
{
"average_frame_build_time_millis": 4.2592592592592595,
"worst_frame_build_time_millis": 21.0,
"missed_frame_build_budget_count": 2,
"average_frame_rasterizer_time_millis": 5.518518518518518,
"worst_frame_rasterizer_time_millis": 51.0,
"missed_frame_rasterizer_budget_count": 10,
"frame_count": 54,
"frame_build_times": [
6874,
5019,
3638
],
"frame_rasterizer_times": [
51955,
8468,
3129
]
}
完整範例
integration_test/scrolling_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:scrolling/main.dart';
void main() {
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('Counter increments smoke test', (tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp(
items: List<String>.generate(10000, (i) => 'Item $i'),
));
final listFinder = find.byType(Scrollable);
final itemFinder = find.byKey(const ValueKey('item_50_text'));
await binding.traceAction(
() async {
// Scroll until the item to be found appears.
await tester.scrollUntilVisible(
itemFinder,
500.0,
scrollable: listFinder,
);
},
reportKey: 'scrolling_timeline',
);
});
}
test_driver/perf_driver.dart
import 'package:flutter_driver/flutter_driver.dart' as driver;
import 'package:integration_test/integration_test_driver.dart';
Future<void> main() {
return integrationDriver(
responseDataCallback: (data) async {
if (data != null) {
final timeline = driver.Timeline.fromJson(
data['scrolling_timeline'] as Map<String, dynamic>,
);
// Convert the Timeline into a TimelineSummary that's easier to
// read and understand.
final summary = driver.TimelineSummary.summarize(timeline);
// Then, write the entire timeline to disk in a json format.
// This file can be opened in the Chrome browser's tracing tools
// found by navigating to chrome://tracing.
// Optionally, save the summary to disk by setting includeSummary
// to true
await summary.writeTimelineToFile(
'scrolling_timeline',
pretty: true,
includeSummary: true,
);
}
},
);
}