iT邦幫忙

2025 iThome 鐵人賽

DAY 20
0
Mobile Development

我的 Flutter 進化論:30 天打造「Crew Up!」的架構之旅系列 第 20

Day 20 - Firebase Performance Monitoring:數據驅動的效能優化

  • 分享至 

  • xImage
  •  

大家好,歡迎來到第二十天!在 Day 19,我們建立了完整的 Firebase Analytics 數據分析系統。今天,我們要解決另一個重要問題:如何了解和優化 App 的效能表現

從專案開發經驗來看,效能問題往往隱藏在使用者的真實使用場景中。在開發環境中一切順暢,但部署到使用者手機上卻可能遇到載入緩慢、卡頓等問題。今天我們要為 Crew Up! 建立效能監控系統,就像 App 的健康儀表板,幫助我們用真實數據發現和解決效能瓶頸。

🎯 為什麼需要效能監控?

開發環境 vs 真實環境的落差

在開發 Crew Up! 的過程中,我們發現幾個常見的效能盲點:

🔍 開發環境的假象

  • 開發機通常是高階裝置,效能強勁
  • 測試資料量少,網路環境理想
  • Debug 模式的效能不代表 Release 模式

📊 真實使用場景的挑戰

  • 使用者裝置規格差異大(從旗艦機到中低階機)
  • 網路環境不穩定(4G、WiFi、訊號弱)
  • 資料量隨時間增長,初始測試無法反映
  • 多工處理、記憶體不足等真實情況

Firebase Performance Monitoring 的完整功能

Firebase Performance Monitoring 提供了全面的效能監控能力,包含:

📊 自動收集的效能指標

Firebase 會自動為您收集以下重要指標,無需額外程式碼:

  • App 啟動時間:測量從使用者點擊圖示到 App 可互動的時間

    • 冷啟動(Cold start):App 完全關閉後的啟動
    • 溫啟動(Warm start):App 在背景被喚醒
    • 熱啟動(Hot start):App 已在前景,只是切換回來
  • 畫面渲染效能:自動追蹤每個 Screen 的渲染表現

    • Slow rendering:渲染時間 > 16ms 的幀
    • Frozen frames:完全凍結的畫面
  • HTTP 網路請求:自動監控所有 HTTP/HTTPS 請求

    • 請求回應時間
    • 成功率和失敗率
    • 請求和回應的資料大小

💡 重要提示:因為 Crew Up! 專案使用 cloud_firestore 套件,所有對 Firestore 的讀寫操作(如 getActivities()createActivity())都會被自動記錄為網路請求,並出現在 Performance Monitoring 儀表板中。這意味著您無需額外程式碼就能監控資料庫效能!

🔧 自定義追蹤能力

除了自動收集,我們還可以透過 PerformanceService 針對業務邏輯建立自定義追蹤:

  • 業務流程監控:創建 Crew 活動、加入 Crew 等關鍵操作
  • 資料庫操作:Firestore 查詢和寫入效能
  • 圖片處理:Crew 活動圖片上傳和壓縮時間
  • 複雜計算:任何可能影響使用者體驗的操作

這種架構設計遵循「關注點分離」原則,透過統一的服務層管理所有效能監控,讓程式碼更易維護。

Performance Monitoring 的價值

📈 數據驅動的優化決策

  • 了解 App 在不同裝置上的真實表現
  • 識別效能瓶頸和慢操作
  • 追蹤優化效果,量化改善成果
  • 優先處理影響最多使用者的問題

📋 套件依賴設定

# pubspec.yaml
dependencies:
  firebase_performance: ^0.11.0+2    # Firebase Performance Monitoring SDK
  firebase_core: ^4.1.0              # Firebase 核心
  flutter_riverpod: ^2.6.1           # 狀態管理

💡 重要提醒

  1. Firebase 專案設定請參考 Day 13 - Firebase 專案設定
  2. 確保 google-services.json (Android) 和 GoogleService-Info.plist (iOS) 正確配置,這些檔案包含了 Firebase Performance Monitoring 所需的專案識別資訊
  3. 在 Firebase Console 中啟用 Performance Monitoring 服務

💻 核心實作

1. PerformanceService:核心服務

參考 Day 18 的 CrashlyticsService 和 Day 19 的 AnalyticsService 設計理念,我們建立了統一的 PerformanceService

// lib/app/core/services/performance_service.dart

/// Firebase Performance Monitoring 服務
class PerformanceService {
  final FirebasePerformance _performance;

  PerformanceService({FirebasePerformance? performance})
      : _performance = performance ?? FirebasePerformance.instance;

  /// 初始化 Performance Monitoring
  /// 支援 --dart-define 參數和 Debug 模式自動啟用
  static Future<void> initialize() async {
    try {
      final performance = FirebasePerformance.instance;

      // 檢查 --dart-define 參數
      const debugEnabled = String.fromEnvironment('FIRDebugEnabled');
      const performanceDebugEnabled = String.fromEnvironment(
        'FIRPerformanceDebugEnabled',
      );

      // 決定是否啟用 Performance Monitoring
      bool shouldEnable = kDebugMode; // 預設:Debug 模式下啟用

      if (debugEnabled.isNotEmpty) {
        shouldEnable = debugEnabled == '1';
        developer.log(
          '📊 Performance Monitoring ${shouldEnable ? 'enabled' : 'disabled'} via FIRDebugEnabled=$debugEnabled',
          name: 'PerformanceService',
        );
      } else if (performanceDebugEnabled.isNotEmpty) {
        shouldEnable = performanceDebugEnabled == '1';
        developer.log(
          '📊 Performance Monitoring ${shouldEnable ? 'enabled' : 'disabled'} via FIRPerformanceDebugEnabled=$performanceDebugEnabled',
          name: 'PerformanceService',
        );
      } else if (kDebugMode) {
        developer.log(
          '📊 Performance Monitoring enabled in debug mode',
          name: 'PerformanceService',
        );
      }

      await performance.setPerformanceCollectionEnabled(shouldEnable);

      developer.log(
        '✅ Performance Monitoring initialized successfully',
        name: 'PerformanceService',
      );
    } on Exception catch (e) {
      developer.log(
        '❌ Performance Monitoring initialization failed: $e',
        name: 'PerformanceService',
        level: 1000,
      );
    }
  }

  // ... 其他方法
}

@riverpod
PerformanceService performanceService(Ref ref) => PerformanceService();

🎯 設計重點說明

與專案架構一致

遵循 Day 18 Crashlytics 和 Day 19 Analytics 的設計模式:

  • 靜態 initialize() 方法在 Firebase 初始化時呼叫
  • 使用 @riverpod 註解提供依賴注入
  • 統一的錯誤處理和日誌記錄

彈性的啟用控制機制

與 Crashlytics 不同,Performance Monitoring 在 Debug 模式下啟用更有價值(開發時即時發現效能問題)。Crew Up! 專案實作了三層優先級的控制機制:

控制優先級(由高到低):

  1. FIRDebugEnabled(最高優先級)

    • 控制整體 Firebase Debug 模式
    • 設為 1 時啟用所有 Firebase Debug 功能(包含 Performance)
    • 設為 0 時停用
  2. FIRPerformanceDebugEnabled(次優先級)

    • 僅控制 Performance Monitoring
    • 適合只想開啟效能監控,不需要其他 Firebase Debug 功能的場景
  3. kDebugMode(預設行為)

    • 如果沒有設定任何 --dart-define 參數
    • 在 Debug 模式下自動啟用

實際使用範例:

# 場景 1:在 production 環境中啟用所有 Firebase Debug(包含 Performance)
flutter run --flavor production \
  --dart-define=FIRDebugEnabled=1

# 場景 2:只啟用 Performance Monitoring Debug
flutter run --flavor production \
  --dart-define=FIRPerformanceDebugEnabled=1

# 場景 3:在 Debug 模式下明確停用 Performance Monitoring
flutter run --flavor development \
  --dart-define=FIRPerformanceDebugEnabled=0

# 場景 4:不傳任何參數,使用預設行為(Debug 模式自動啟用)
flutter run --flavor development

這種設計的優勢:

  • 靈活控制:可針對不同測試場景精確控制啟用狀態
  • 環境隔離:避免開發數據干擾正式環境分析
  • 即時驗證:配合 Firebase Console 的 Debug View 即時查看數據
  • 清晰日誌:會記錄啟用來源(透過哪個參數或預設行為),方便除錯

2. 自定義 Trace:業務邏輯效能追蹤

/// 建立自定義 Trace
Future<Trace?> startTrace(String name) async {
  try {
    final trace = _performance.newTrace(name);
    await trace.start();

    developer.log(
      '⏱️  Performance: Started trace "$name"',
      name: 'PerformanceService',
    );

    return trace;
  } on Exception catch (e) {
    developer.log(
      '❌ Performance error starting trace "$name": $e',
      name: 'PerformanceService',
    );
    return null;
  }
}

/// 停止 Trace 並記錄結果
Future<void> stopTrace(
  Trace? trace, {
  Map<String, int>? metrics,
  Map<String, String>? attributes,
  bool? success,
}) async {
  if (trace == null) return;

  try {
    // 設置自定義指標
    if (metrics != null) {
      for (final entry in metrics.entries) {
        trace.setMetric(entry.key, entry.value);
      }
    }

    // 設置自定義屬性
    if (attributes != null) {
      for (final entry in attributes.entries) {
        trace.putAttribute(entry.key, entry.value);
      }
    }

    // 設置成功/失敗屬性
    if (success != null) {
      trace.putAttribute('success', success.toString());
    }

    await trace.stop();

    developer.log(
      '✅ Performance: Stopped trace successfully',
      name: 'PerformanceService',
    );
  } on Exception catch (e) {
    developer.log(
      '❌ Performance error stopping trace: $e',
      name: 'PerformanceService',
    );
  }
}

3. 便利方法:簡化效能監控

/// 測量函數執行時間的便利方法
Future<T> measure<T>(
  String name,
  Future<T> Function() operation, {
  Map<String, String>? attributes,
}) async {
  final trace = await startTrace(name);

  try {
    final result = await operation();
    await stopTrace(trace, attributes: attributes, success: true);
    return result;
  } on Exception catch (e) {
    await stopTrace(
      trace,
      attributes: {
        ...?attributes,
        'error': e.toString().substring(0, 100),
      },
      success: false,
    );
    rethrow;
  }
}

4. 網路請求監控

/// 建立 HTTP 請求監控
Future<HttpMetric?> newHttpMetric(
  String url,
  HttpMethod httpMethod,
) async {
  try {
    final metric = _performance.newHttpMetric(url, httpMethod);
    developer.log(
      '🌐 Performance: Created HTTP metric for $url',
      name: 'PerformanceService',
    );
    return metric;
  } on Exception catch (e) {
    developer.log(
      '❌ Performance error creating HTTP metric: $e',
      name: 'PerformanceService',
    );
    return null;
  }
}

/// 停止 HTTP 請求監控
Future<void> stopHttpMetric(
  HttpMetric? metric, {
  int? statusCode,
  int? requestPayloadSize,
  int? responsePayloadSize,
  String? responseContentType,
}) async {
  if (metric == null) return;

  try {
    if (statusCode != null) metric.httpResponseCode = statusCode;
    if (requestPayloadSize != null) {
      metric.requestPayloadSize = requestPayloadSize;
    }
    if (responsePayloadSize != null) {
      metric.responsePayloadSize = responsePayloadSize;
    }
    if (responseContentType != null) {
      metric.responseContentType = responseContentType;
    }

    await metric.stop();

    developer.log(
      '✅ Performance: Stopped HTTP metric (status: $statusCode)',
      name: 'PerformanceService',
    );
  } on Exception catch (e) {
    developer.log(
      '❌ Performance error stopping HTTP metric: $e',
      name: 'PerformanceService',
    );
  }
}

5. 業務特定方法:提高開發效率

/// 監控活動載入效能
Future<T> measureActivityLoad<T>(
  String activityType,
  Future<T> Function() operation,
) async {
  return measure(
    'load_$activityType',
    operation,
    attributes: {'activity_type': activityType},
  );
}

/// 監控資料庫操作效能
Future<T> measureDatabaseOperation<T>(
  String operation,
  Future<T> Function() action, {
  String? collection,
}) async {
  return measure(
    'db_$operation',
    action,
    attributes: {
      'operation': operation,
      if (collection != null) 'collection': collection,
    },
  );
}

/// 監控使用者操作效能
Future<T> measureUserAction<T>(
  String action,
  Future<T> Function() operation, {
  Map<String, String>? metadata,
}) async {
  return measure(
    'user_$action',
    operation,
    attributes: {
      'action': action,
      ...?metadata,
    },
  );
}

🔌 Firebase 初始化整合

在 Crew Up! 專案中,我們將 Firebase 初始化邏輯封裝在 firebase_initializer.dart 中,遵循關注點分離單一職責原則

Firebase 初始化服務

// lib/app/core/services/firebase_initializer.dart

/// 確保 Firebase 在每個進程中只初始化一次
/// 支援 Hot Restart 和多入口點
Future<void> ensureFirebaseInitialized() async {
  // 如果已經有預設 app,直接返回
  try {
    Firebase.app();
    return;
  } on FirebaseException catch (e) {
    if (e.code != 'no-app') {
      rethrow;
    }
  }

  // 僅在真正沒有 app 時才初始化
  try {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );

    // 初始化 Firebase Crashlytics
    await CrashlyticsService.initialize();

    // 初始化 Firebase Performance Monitoring
    await PerformanceService.initialize();
  } on FirebaseException catch (e) {
    if (e.code == 'duplicate-app') {
      // 其他 isolate/入口點已經初始化,安全忽略
      return;
    }
    rethrow;
  }
}

在 main.dart 中使用

// lib/main.dart

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

  // 使用專用的 FirebaseInitializer 處理初始化
  await ensureFirebaseInitialized();

  runApp(
    const ProviderScope(
      child: CrewUpApp(),
    ),
  );
}

🎯 架構設計優勢

關注點分離

  • firebase_initializer.dart 專門負責 Firebase 服務初始化
  • main.dart 保持簡潔,只處理應用程式入口邏輯
  • 各 Service 類別(CrashlyticsService、PerformanceService)負責自己的初始化細節

可維護性提升

  • 新增 Firebase 服務時,只需在 ensureFirebaseInitialized() 中添加一行
  • 初始化順序集中管理,避免依賴問題
  • 符合 Day 1 建立的 Clean Architecture 原則

安全性保障

  • 防止重複初始化(Hot Restart 場景)
  • 妥善處理 race condition
  • 統一的錯誤處理

🔧 整合現有效能監控

在 Crew Up! 專案中,我們已經有 AuthPerformanceMonitor 用於認證流程的效能監控。現在我們整合 Firebase Performance Monitoring:

// lib/features/auth/data/services/auth_performance_monitor.dart

/// 認證效能指標(整合 Firebase Performance)
class AuthPerformanceMetrics {
  final String operation;
  final DateTime startTime;
  DateTime? endTime;
  bool success;
  String? errorMessage;
  final Map<String, dynamic> metadata;
  final Trace? firebaseTrace;  // 整合 Firebase Performance Trace

  // ... 其他程式碼
}

/// 認證效能監控服務
class AuthPerformanceMonitor {
  final PerformanceService? _performanceService;

  AuthPerformanceMonitor({PerformanceService? performanceService})
      : _performanceService = performanceService;

  /// 開始監控操作(同時啟動 Firebase Performance Trace)
  Future<AuthPerformanceMetrics> startOperation(
    String operation, {
    Map<String, dynamic> metadata = const {},
  }) async {
    // 啟動 Firebase Performance Trace
    final firebaseTrace =
        await _performanceService?.startTrace('auth_$operation');

    final metric = AuthPerformanceMetrics(
      operation: operation,
      startTime: DateTime.now(),
      success: false,
      metadata: metadata,
      firebaseTrace: firebaseTrace,  // 保存 Trace 實例
    );

    return metric;
  }

  /// 完成操作監控(同時停止 Firebase Performance Trace)
  Future<void> completeOperation(
    AuthPerformanceMetrics metric, {
    bool success = true,
    String? errorMessage,
  }) async {
    metric.endTime = DateTime.now();
    metric.success = success;
    metric.errorMessage = errorMessage;

    // 停止 Firebase Performance Trace
    await _performanceService?.stopTrace(
      metric.firebaseTrace,
      metrics: {'duration_ms': metric.duration.inMilliseconds},
      attributes: {
        'operation': metric.operation,
        if (errorMessage != null) 'error': errorMessage.substring(0, 100),
      },
      success: success,
    );

    // ... 其他日誌記錄
  }
}

🎯 整合設計理念

為什麼保留本地 AuthPerformanceMonitor

或許有人會問,既然有了 Firebase Performance 這麼強大的遠端監控,為什麼不直接取代本地的 AuthPerformanceMonitor?這其實是一個關於監控粒度和開發效率的權衡。

  • 開發階段的即時性:Firebase 的數據(即使在 Debug View)仍有數秒到數分鐘的延遲。而本地監控可以提供毫秒級的即時反饋,方便開發者在修改程式碼後立即驗證效能變化,例如在 AuthRetryService 中調整重試策略時。
  • 數據的詳細程度:本地監控可以記錄更豐富、更客製化的中繼資料(metadata),甚至包含一些不適合上傳到 Firebase 的敏感偵錯資訊。
  • 解耦與測試:保留本地監控層,讓我們的認證邏輯不直接依賴 Firebase。在進行單元測試(如 auth_retry_service_test.dart)時,我們只需 Mock AuthPerformanceMonitor,而無需處理與 Firebase 相關的複雜性。

因此,我們採取了「漸進式整合」的策略,讓兩套系統各司其職,在開發效率與遠端監控能力之間取得平衡。

雙重監控的價值

  • 本地監控AuthPerformanceMetrics):詳細的本地日誌,開發時除錯用
  • 遠端監控(Firebase Trace):彙整所有使用者的效能數據,發現真實問題

漸進式整合

  • 保留現有的本地監控邏輯
  • 透過依賴注入加入 PerformanceService
  • 在關鍵點同時啟動/停止 Firebase Trace
  • 不影響現有功能,向後相容

🚀 實際應用範例

監控 Crew 活動創建流程(實際專案功能)

在實際專案中,我們可以在 CreateActivityNotifiercreateActivity() 方法中整合效能監控:

// lib/features/activity/presentation/providers/create_activity_notifier.dart

class CreateActivityNotifier extends StateNotifier<CreateActivityState> {
  final PerformanceService _performanceService;

  /// 創建活動(整合 Performance Monitoring)
  Future<bool> createActivity() async {
    // 開始效能追蹤
    final trace = await _performanceService.startTrace('create_crew_activity_flow');

    try {
      developer.log('🚀 開始創建活動', name: 'CreateActivityNotifier');

      state = state.copyWith(isLoading: true, errorMessage: null);

      // 1. 構建 Activity 實體(驗證資料)
      final startBuildTime = DateTime.now();
      final activity = await _buildActivityFromState();
      final buildDuration = DateTime.now().difference(startBuildTime).inMilliseconds;

      // 2. 上傳自定義圖片(如果有)
      int uploadDuration = 0;
      if (state.customImagePath != null) {
        final startUploadTime = DateTime.now();
        await _uploadCustomImage(activity);
        uploadDuration = DateTime.now().difference(startUploadTime).inMilliseconds;
      }

      // 3. 使用 Use Case 創建活動
      final useCase = ref.read(createActivityUseCaseProvider);
      final result = await useCase.execute(activity);

      if (result.isSuccess) {
        // 成功 - 記錄詳細指標
        await _performanceService.stopTrace(
          trace,
          metrics: {
            'build_time_ms': buildDuration,
            'upload_time_ms': uploadDuration,
          },
          attributes: {
            'category': state.selectedCategory.name,
            'has_custom_image': (state.customImagePath != null).toString(),
            'duration_days': state.durationDays.toString(),
          },
          success: true,
        );

        developer.log('✅ 活動創建成功: ${activity.id}', name: 'CreateActivityNotifier');
        state = state.copyWith(isLoading: false, createdActivityId: activity.id);
        return true;
      } else {
        // 失敗 - 記錄錯誤
        await _performanceService.stopTrace(
          trace,
          attributes: {
            'error': result.error.message.substring(0, 100),
            'error_type': 'business_logic',
          },
          success: false,
        );

        state = state.copyWith(
          isLoading: false,
          errorMessage: result.error.message,
        );
        return false;
      }
    } on Exception catch (e) {
      // 異常 - 記錄錯誤
      await _performanceService.stopTrace(
        trace,
        attributes: {
          'error': e.toString().substring(0, 100),
          'error_type': 'exception',
        },
        success: false,
      );

      developer.log('❌ 創建活動時發生錯誤: $e', name: 'CreateActivityNotifier');
      state = state.copyWith(
        isLoading: false,
        errorMessage: e.toString(),
      );
      return false;
    }
  }
}

🎯 範例亮點

這個範例展示了真實專案中的實踐方式:

  1. 細粒度指標記錄:分別記錄建立實體、上傳圖片的時間
  2. 業務屬性追蹤:記錄活動類別、天數、是否有圖片等業務資訊
  3. 錯誤分類:區分業務邏輯錯誤和系統異常
  4. 完整覆蓋:無論成功或失敗都停止 Trace,維持數據完整性

這樣一來,在 Firebase Console 中您就能看到:

  • 創建 Crew 活動平均需要多少時間
  • 上傳圖片對效能的影響
  • 不同類別活動的創建效能差異
  • 最常見的失敗原因

📊 實務建議與規範

改善建議:進階功能

在實際使用 PerformanceService 後,我們加入了以下改善:

1. 自定義異常處理

/// Performance 相關異常
class PerformanceException implements Exception {
  final String message;
  final String? code;
  final dynamic originalError;

  const PerformanceException(
    this.message, {
    this.code,
    this.originalError,
  });
}

2. 配置管理

/// Performance 配置
class PerformanceConfig {
  static const int maxTraceNameLength = 100;
  static const int maxAttributeNameLength = 40;
  static const int maxAttributeValueLength = 100;
  static const int maxAttributeCount = 5;
  static const int traceTimeoutMs = 300000; // 5 分鐘

  final bool enableValidation;
  final bool enableAutoCleanup;
  final int autoCleanupIntervalMs;

  const PerformanceConfig({
    this.enableValidation = true,
    this.enableAutoCleanup = true,
    this.autoCleanupIntervalMs = 60000,
  });
}

3. 活動 Trace 追蹤

防止重複啟動同名 Trace,並自動清理過期的 Trace:

// 檢查是否已經有同名的活動 Trace
if (_activeTraces.containsKey(name)) {
  developer.log(
    '⚠️  Trace "$name" is already active, skipping duplicate start',
    name: 'PerformanceService',
  );
  return _activeTraces[name]?.trace;
}

4. 自動清理機制

超過 5 分鐘的 Trace 會被自動停止並清理:

bool get isExpired {
  final now = DateTime.now();
  final duration = now.difference(startTime).inMilliseconds;
  return duration > PerformanceConfig.traceTimeoutMs;
}

5. 驗證與限制檢查

startTracestopTrace 時自動驗證:

// 驗證 Trace 名稱格式
final validPattern = RegExp(r'^[a-z0-9_]+$');
if (!validPattern.hasMatch(name)) {
  throw PerformanceException(
    'Trace name must contain only lowercase letters, numbers, and underscores',
    code: 'INVALID_TRACE_NAME_FORMAT',
  );
}

// 驗證屬性數量
if (attributes.length > PerformanceConfig.maxAttributeCount) {
  throw PerformanceException(
    'Attributes count exceeds maximum of ${PerformanceConfig.maxAttributeCount}',
    code: 'TOO_MANY_ATTRIBUTES',
  );
}

6. 效能狀態追蹤

// 獲取當前活動的 Trace 數量
int get activeTracesCount => _activeTraces.length;

// 獲取所有活動 Trace 的名稱
List<String> get activeTraceNames => _activeTraces.keys.toList();

// 手動觸發清理過期 Trace
void cleanupNow() {
  _cleanupExpiredTraces();
}

這些改善讓 PerformanceService 更加健壯、易於除錯,並能自動避免常見的使用錯誤。

Firebase Performance 限制

Trace 限制:

  • Trace 名稱:最多 100 個字符
  • 自定義屬性:最多 5 個
  • 屬性名稱:最多 40 個字符
  • 屬性值:最多 100 個字符
  • 自定義指標:無限制

效能目標建議:

  • App 啟動:< 3 秒
  • 首頁 Crew 列表載入:< 1 秒
  • Crew 活動詳情載入:< 500ms
  • 使用者操作(加入/退出 Crew):< 500ms
  • Crew 活動圖片上傳:< 5 秒
  • 資料庫查詢(搜尋 Crew):< 1 秒

監控重點選擇

✅ 應該監控的場景

  • 關鍵業務流程(創建 Crew 活動、加入 Crew)
  • 使用者高頻操作(搜尋 Crew、篩選活動、刷新列表)
  • 資源密集操作(Crew 活動圖片上傳、大量資料載入)
  • 首次載入體驗(App 啟動、首頁 Crew 列表顯示)

❌ 不需要監控的場景

  • 簡單的 UI 操作(按鈕點擊、頁面切換)
  • 本地計算(簡單的資料處理)
  • 太細粒度的操作(單一欄位驗證)

🎯 總結

今天我們為 Crew Up! 建立了完整的 Performance Monitoring 系統:

✅ 已完成的功能

  • 完整的 PerformanceService 封裝,支援多種監控場景
  • 整合現有的 AuthPerformanceMonitor,實現雙重監控
  • 便利方法簡化常見操作(活動載入、資料庫查詢等)
  • 完善的錯誤處理和日誌記錄
  • 遵循 Clean Architecture,使用 Riverpod 依賴注入

關鍵學習

從實際開發中,我們學到:

  1. 數據驅動優化 - 用真實數據而非猜測來改善效能
  2. 漸進式整合 - 保留現有邏輯,逐步加入新功能
  3. 錯誤處理 - Performance 監控失敗不應影響 App 運作
  4. 重點監控 - 選擇關鍵場景,避免過度監控

與前幾天的關聯

  • Day 13:Firebase 專案設定,今天新增 Performance Monitoring 服務
  • Day 18:Crashlytics 錯誤追蹤,今天補足效能監控的拼圖
  • Day 19:Analytics 使用者行為,三者結合形成完整的監控體系

下一步

明天(Day 21),我們將開始探索 AI 整合,學習如何將 Google Cloud Vertex AI 整合到 Crew Up! 中,為使用者提供智能推薦和內容生成功能。

期待與您在 Day 21 相見!


📋 相關資源

📝 專案資訊

  • 專案名稱: Crew Up!
  • 開發日誌: Day 20 - Firebase Performance Monitoring:數據驅動的效能優化
  • 文章日期: 2025-10-04
  • 技術棧: Flutter 3.8+, Firebase Performance 0.11.0+, Riverpod 2.6.1, Clean Architecture

上一篇
Day 19: Firebase Analytics - 數據驅動的產品決策
下一篇
Day 21 - Google Gemini API:為 App 加入 AI
系列文
我的 Flutter 進化論:30 天打造「Crew Up!」的架構之旅22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言