HarmonyOS NEXT應用開發之異常處理案例

来源:https://www.cnblogs.com/HarmonyOSNext/p/18145971
-Advertisement-
Play Games

介紹 本示例介紹了通過應用事件打點hiAppEvent獲取上一次應用異常信息的方法,主要分為應用崩潰、應用卡死以及系統查殺三種。 效果圖預覽 使用說明: 點擊構建應用崩潰事件,3s之後應用退出,然後打開應用進入應用異常頁面,隔1min左右後,顯示上次異常退出信息。 點擊構建應用卡死事件,需手動退出, ...


介紹

本示例介紹了通過應用事件打點hiAppEvent獲取上一次應用異常信息的方法,主要分為應用崩潰、應用卡死以及系統查殺三種。

效果圖預覽

image

使用說明

  1. 點擊構建應用崩潰事件,3s之後應用退出,然後打開應用進入應用異常頁面,隔1min左右後,顯示上次異常退出信息。
  2. 點擊構建應用卡死事件,需手動退出,然後打開應用進入應用異常頁面,隔1min左右後,顯示上次異常退出信息。

實現思路

  1. 構建應用異常。源碼參考ApplicationException.ets
 handleOperate(index: number) {
    switch (index) {
      case 0:
      // 在按鈕點擊函數中構造一個APP_CRASH場景,觸發應用崩潰事件
        let result: object = JSON.parse('');
        break;
      case 1:
      // 在按鈕點擊函數中構造一個APP_FREEZE場景,觸發應用卡死事件,500ms之後執行無限迴圈
        while (true) {
        }
    }
  }
  1. 應用退出後,進入本頁面,等待訂閱消息通知,待收到訂閱消息後,通過EventSubscription.ets中的onReceive函數,接收到異常信息數據, 並通過AppStorage.setOrCreate('appEventGroups',異常信息數據)雙向綁定異常信息,源碼參考代碼可參考EventSubscription.ets
import hiAppEvent from '@ohos.hiviewdfx.hiAppEvent';
import { logger } from '@ohos/base';

const TAG: string = 'eventSubscription';

export function eventSubscription() {
  // 添加應用事件觀察者方法,可用於訂閱應用事件
  hiAppEvent.addWatcher({
    // 開發者可以自定義觀察者名稱,系統會使用名稱來標識不同的觀察者
    name: "mst",
    // 開發者可以訂閱感興趣的系統事件,此處是訂閱了崩潰事件
    appEventFilters: [
      {
        domain: hiAppEvent.domain.OS,
        names: [hiAppEvent.event.APP_CRASH, hiAppEvent.event.APP_FREEZE]
      }
    ],
    // TODO:知識點:獲取事件組信息。開發者可以自行實現訂閱實時回調函數,以便對訂閱獲取到的事件數據進行自定義處理
    onReceive: async (domain: string, appEventGroups: Array<hiAppEvent.AppEventGroup>) => {
      logger.info(TAG, `HiAppEvent onReceive: domain=${domain}`);
      // 獲取事件組信息,與ApplicationException文件中的@StorageLink('faultMessage') faultMessage進行雙向數據綁定
      AppStorage.setOrCreate('appEventGroups', appEventGroups);
    }
  });
}
  1. @StorageLink('appEventGroups')接收訂閱事件函數傳遞的事件組信息,調用getFaultMessage函數對信息進行處理,將處理後的信息通過 this.faultDataSource.pushData(message) 添加到懶載入數據源中,並通過this.faultDataSource.persistenceStorage()執行持久化存儲,最後通過使用LazyForEach將數據信息載入到頁面上。 具體源碼參考ApplicationException.ets
@Component
struct FaultArea {
  // 懶載入數據源
  @State faultDataSource: FaultDataSource = new FaultDataSource();
  // 雙向數據綁定懶載入數據源的數組長度
  @StorageLink('faultDataSourceLength') faultDataSourceLength: number = 0;
  // 雙向數據綁定事件組,與AppStorage.setOrCreate進行綁定,此變數發生變化觸發getFaultMessage函數
  @StorageLink('appEventGroups') @Watch('getFaultMessage') appEventGroups: Array<hiAppEvent.AppEventGroup> = [];
  @Consume eventIndex: number;

  async aboutToAppear() {
    logger.info(TAG, `aboutToAppear start`);
    // 獲取Preferences實例
    PreferencesManager.getPreferences(this.faultDataSource);
  }

  // 獲取應用異常信息
  async getFaultMessage() {
    logger.info(TAG, `getAppEventGroups start`);
    if (this.appEventGroups && this.appEventGroups.length > 0) {
      // 遍歷事件組
      this.appEventGroups.forEach((eventGroup: hiAppEvent.AppEventGroup) => {
        // 遍歷事件對象集合
        eventGroup.appEventInfos.forEach(async (eventInfo: hiAppEvent.AppEventInfo) => {
          let message: string = '';
          message += `HiAppEvent eventInfo.domain=${eventInfo.domain}\n` // 事件領域
            + `HiAppEvent eventInfo.name=${eventInfo.name}\n`  // 事件名稱
            + `HiAppEvent eventInfo.eventType=${eventInfo.eventType}\n` // 事件名稱
            + `HiAppEvent eventInfo.params.time=${eventInfo.params['time']}\n` // 事件發生的時間
            + `HiAppEvent eventInfo.params.crash_type=${eventInfo.params['crash_type']}\n`
            + `HiAppEvent eventInfo.params.foreground=${eventInfo.params['foreground']}\n`
            + `HiAppEvent eventInfo.params.bundle_version=${eventInfo.params['bundle_version']}\n`
            + `HiAppEvent eventInfo.params.bundle_name=${eventInfo.params['bundle_name']}\n`
            + `HiAppEvent eventInfo.params.exception=${JSON.stringify(eventInfo.params['exception'])}\n`
            + `HiAppEvent eventInfo.params.hilog.size=${eventInfo.params['hilog'].length}\n`;
          // TODO:知識點:將異常信息存儲到數組faultMessage當中
          this.faultDataSource.pushData(message);
        })
      })
    }
    // TODO:知識點:持久化存儲異常信息集合
    this.faultDataSource.persistenceStorage();
  }

  build() {
    List() {
      // 添加判斷,如果異常信息集合的信息條數大於0,遍歷異常信息
      if (this.faultDataSourceLength > 0) {
        // 性能:動態載入數據場景可以使用LazyForEach遍曆數據。https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/arkts-rendering-control-lazyforeach-0000001524417213-V3
        LazyForEach(this.faultDataSource, (message: string) => {
          ListItem() {
            Text(message)
              .textAlign(TextAlign.Start)
          }
        }, (item: string) => item)
      } else {
        ListItem() {
          // 根據被點擊事件的下標響應指定的信息
          Text(this.eventIndex === 0 ? $r('app.string.crash_event_message') :
            (this.eventIndex === 1 ? $r('app.string.freeze_event_message') :
              (this.faultSign ? $r('app.string.data_delay_toast') :
              $r('app.string.no_message'))))
        }
      }
    }
    .width('92%')
    .height(300)
    .shadow(ShadowStyle.OUTER_DEFAULT_SM)
    .borderRadius($r('app.string.ohos_id_corner_radius_default_m'))
    .padding($r('app.string.ohos_id_card_padding_start'))
  }
}
  1. 以上代碼中有引用懶載入數據類和持久化存儲類,源碼可參考DataSource.ets和 PreferencesManager.ets
// DataSource.ets
export class FaultDataSource extends BasicDataSource {
  // 懶載入數據
  private faultMessage: Array<string> = [];

  // TODO:知識點:獲取懶載入數據源的數據長度
  totalCount(): number {
    return this.faultMessage.length;
  }

  // 獲取指定數據項
  getData(index: number): string {
    return this.faultMessage[index];
  }

  // TODO:知識點:存儲數據到懶載入數據源中
  pushData(data: string): void {
    this.faultMessage.push(data);
    // 在數組頭部添加數據
    this.notifyDataAdd(this.faultMessage.length - 1);
    AppStorage.setOrCreate('faultDataSourceLength', this.totalCount());
  }

  // TODO:知識點:持久化存儲異常信息集合
  persistenceStorage(): void {
    PreferencesManager.putFaultMessage(this.faultMessage);
  }
}

// PreferencesManager.ets
 /**
   * 存儲數據異常信息
   * @param faultMessage 異常信息集合
   */
  public static putFaultMessage(faultMessage: Array<string>) {
    logger.info(`putMessage start`);
    try {
      // TODO:知識點:通過 dataPreferencesManager.put方法存儲數據
      dataPreferencesManager.put('faultMessage', JSON.stringify(faultMessage), async (err: BusinessError) => {
        if (err) {
          logger.error("Failed to put value of 'faultMessage'. code =" + err.code + ", message =" + err.message);
          return;
        }
        logger.info('Succeeded in putting value of faultMessage.');
        dataPreferencesManager.flush();
      })
    } catch (err) {
      let code = (err as BusinessError).code;
      let message = (err as BusinessError).message;
      logger.error("Failed to put value of 'catch err'. code =" + err.code + ", message =" + err.message);
    }
  }

  /**
   * 獲取數據異常信息
   * @param faultMessage 異常信息集合
   */
  public static getFaultMessage(faultDataSource:FaultDataSource) {
    logger.info(`getFaultMessage start`);
    try {
      // TODO:知識點:通過dataPreferencesManager.get方法獲取異常信息數據
      let promise = dataPreferencesManager.get('faultMessage', []);
      promise.then(async (data: dataPreferences.ValueType) => {
        if (typeof data === 'string') {
          let faultData: Array<string> = JSON.parse(data);
          // 將異常數據添加到懶載入數據源中
          faultData.forEach((item: string) => {
            faultDataSource.pushData(item);
          })
          // 雙向數據綁定懶載入數據源長度,更新數據源長度
          AppStorage.setOrCreate('faultDataSourceLength',faultDataSource.totalCount())
          logger.info('Succeeded in getting value of faultMessage.');
        }
      })
    } catch (err) {
      logger.error("Failed to get value of 'catch err'. code =" + err.code + ", message =" + err.message);
    }
  }

高性能知識點

本示例使用了LazyForEach進行數據懶載入,將疊加獲取到的應用異常信息進行渲染。

工程結構&模塊類型

aplicationexception                             // har類型
|---model
|   |---DataSource.ets                          // 模型層-懶載入數據源
|   |---EventSubscription.ets                   // 數據模型層-訂閱應用事件
|   |---MockData.ets                            // 數據模型層-模擬數據
|   |---PreferencesManager.ets                  // 數據模型層-持久化存儲
|---view
|   |---PreferencesManager.ets                  // 視圖層-應用異常頁面

模塊依賴

本實例依賴common模塊來實現日誌的列印、資源的調用以及公共組件FunctionDescription的引用。

參考資料

應用事件打點HiAppEvent 數據懶載入LazyForEach


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 介紹 在ArkTS中調用相機拍照和錄像,以及如何使用媒體庫介面進行媒體文件的增、刪、改、查操作。本示例用到了 許可權管理能力 相機模塊能力介面 圖片處理介面 音視頻相關媒體業務能力介面 媒體庫管理介面 設備信息能力介面 文件存儲管理能力介面 彈窗能力介面 效果預覽 使用說明 1.啟動應用,在許可權彈窗中 ...
  • 介紹 本示例介紹如何使用Text組件實現驗證碼場景,並禁用對內容的選中、複製、游標。 效果圖預覽 使用說明 單擊組件可彈出輸入法 在進行驗證碼輸入時,無法對中間單個數字進行更改,無法選中輸入內容,無游標 實現思路 因為要禁用複製、選中等功能,這裡使用了Text組件,而不是TextInput ForE ...
  • 介紹 本示例通過模擬下載場景介紹如何將Native的進度信息實時同步到ArkTS側。 效果圖預覽 使用說明 點擊“Start Download“按鈕後,Native側啟動子線程模擬下載任務 Native側啟動子線程模擬下載,並通過Arkts的回調函數將進度信息實時傳遞到Arkts側 實現思路 前端進 ...
  • 前言 轉場動畫是一種在電影、視頻和演示文稿中使用的動畫效果,用於平滑地切換不同的場景或幻燈片。轉場動畫可以增加視覺吸引力,改善觀眾的觀看體驗。 常見的轉場動畫包括淡入淡出、滑動、旋轉、放大縮小等效果。這些動畫效果可以在場景之間創建無縫的過渡,使觀眾感到自然流暢。 在電影中,轉場動畫通常用於切換不 ...
  • 介紹 翻頁動效是應用開發中常見的動效場景,常見的有書籍翻頁,日曆翻頁等。本例將介紹如何通過ArkUI提供的顯示動畫介面animateTo實現翻頁的效果。 效果圖預覽 使用說明 本例通過setInterval函數每秒調用一次翻頁動畫,實現連續翻頁效果。 實現思路 如圖,左右兩側分別代表打開書籍的左右兩 ...
  • 介紹 本示例介紹使用第三方庫的PullToRefresh組件實現列表的下拉刷新數據和上滑載入後續數據。 效果圖預覽 使用說明 進入頁面,下拉列表觸發刷新數據事件,等待數據刷新完成。 上滑列表到底部,觸發載入更多數據事件,等待數據載入完成。 實現思路 使用第三方庫pullToRefresh組件,將列表 ...
  • 介紹 本示例介紹了文本寬度過寬時,如何實現文本首尾相接迴圈滾動並顯示在可視區,以及每迴圈滾動一次之後會停滯一段時間後再滾動。 效果圖預覽 使用說明: 1.進入頁面,檢票口文本處,實現文本首尾相接迴圈滾動,且在同一可視區,滾動完成之後,停滯一段時間後繼續滾動。 實現思路 由於ArkUI中的Marque ...
  • 介紹 本示例介紹在開發應用以適應深色模式時,對於深色和淺色模式的適配方案,採取了多種策略如下: 固定屬性適配:對於部分組件的顏色屬性,如背景色或字體顏色,若保持不變,可直接設定固定色值或引用固定的資源文件。 雙資源目錄適配:在resources目錄下新增dark子目錄,用於存放深色模式下的特定顏色配 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...