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
  • 概述:本文代碼示例演示瞭如何在WPF中使用LiveCharts庫創建動態條形圖。通過創建數據模型、ViewModel和在XAML中使用`CartesianChart`控制項,你可以輕鬆實現圖表的數據綁定和動態更新。我將通過清晰的步驟指南包括詳細的中文註釋,幫助你快速理解並應用這一功能。 先上效果: 在 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • 概述:本示例演示了在WPF應用程式中實現多語言支持的詳細步驟。通過資源字典和數據綁定,以及使用語言管理器類,應用程式能夠在運行時動態切換語言。這種方法使得多語言支持更加靈活,便於維護,同時提供清晰的代碼結構。 在WPF中實現多語言的一種常見方法是使用資源字典和數據綁定。以下是一個詳細的步驟和示例源代 ...
  • 描述(做一個簡單的記錄): 事件(event)的本質是一個委托;(聲明一個事件: public event TestDelegate eventTest;) 委托(delegate)可以理解為一個符合某種簽名的方法類型;比如:TestDelegate委托的返回數據類型為string,參數為 int和 ...
  • 1、AOT適合場景 Aot適合工具類型的項目使用,優點禁止反編 ,第一次啟動快,業務型項目或者反射多的項目不適合用AOT AOT更新記錄: 實實在在經過實踐的AOT ORM 5.1.4.117 +支持AOT 5.1.4.123 +支持CodeFirst和非同步方法 5.1.4.129-preview1 ...
  • 總說周知,UWP 是運行在沙盒裡面的,所有許可權都有嚴格限制,和沙盒外交互也需要特殊的通道,所以從根本杜絕了 UWP 毒瘤的存在。但是實際上 UWP 只是一個應用模型,本身是沒有什麼許可權管理的,許可權管理全靠 App Container 沙盒控制,如果我們脫離了這個沙盒,UWP 就會放飛自我了。那麼有沒... ...
  • 目錄條款17:讓介面容易被正確使用,不易被誤用(Make interfaces easy to use correctly and hard to use incorrectly)限制類型和值規定能做和不能做的事提供行為一致的介面條款19:設計class猶如設計type(Treat class de ...
  • title: 從零開始:Django項目的創建與配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 後端開發 tags: Django WebDev Python ORM Security Deployment Op ...
  • 1、BOM對象 BOM:Broswer object model,即瀏覽器提供我們開發者在javascript用於操作瀏覽器的對象。 1.1、window對象 視窗方法 // BOM Browser object model 瀏覽器對象模型 // js中最大的一個對象.整個瀏覽器視窗出現的所有東西都 ...