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 Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...