🎉

HarmonyOS 運動プロジェクト開発:超便利な RCP ネットワークライブラリのカプセル化(下)

に公開

HarmonyOS 核心技術##運動開発## Remote Communication Kit(遠隔通信サービス)

これまでの記事では、機能が完全な RCP ネットワークライブラリのカプセル化方法について詳しく紹介し、そのコア機能と高度な特性についても探求しました。本稿では、HarmonyOS 運動プロジェクトでこのネットワークライブラリを使用して、具体的なネットワークリクエスト機能を実現する方法を示します。

まえがき

HarmonyOS 運動プロジェクトにおいて、ネットワークリクエストは機能を実現するための鍵となる要素の一つです。運動データの取得、ユーザー情報の同期、運動ビデオリソースのロードなど、安定性、効率性、そして使いやすさを兼ね備えたネットワークライブラリが必要です。本稿では、実際のコードサンプルを通じて、カプセル化された RCP ネットワークライブラリを使用してこれらの機能を実現する方法を紹介します。

  1. 例外処理のカプセル化

実際の開発において、例外処理はネットワークリクエストにおいて欠かせない部分です。カスタム例外クラスを定義することで、ネットワークリクエスト中に発生し得る各种のエラーをより効果的に管理することができます。

(1)カスタム例外クラス

API リクエスト中のエラー情報をカプセル化するための ApiException クラスを定義しました。

export class ApiException extends Error {
  apiCode?: number;
  apiMessage?: string;

  constructor(apiCode?: number, apiMessage?: string) {
    super();
    this.name = "ApiException";
    this.apiCode = apiCode;
    this.apiMessage = apiMessage;
  }
}

コアポイントの解説:

  1. カスタムプロパティ:apiCodeapiMessage を使用して、API が返すエラーコードとエラーメッセージを保存します。

  2. コンストラクター:コンストラクターを使用して例外オブジェクトを初期化し、エラーコードとエラーメッセージを設定します。

  3. ネットワークリクエストのカプセル化

ネットワークリクエストの実装を簡素化するために、ApiRequest クラスをカプセル化し、ネットワークリクエストとエラー処理を統一して管理します。

(1)シングルトンパターン

ApiRequest クラスはシングルトンパターンを使用し、グローバルで唯一のインスタンスを保証します。


import { DataResult, ErrorCodes, ErrorData, LibToast, NetworkException, SuccessData } from "lib_base";
import { RcpNetworkService } from "lib_base/src/main/ets/utils/rcpnet/RcpService";
import { ApiResult } from "../data/models/ApiResult";
import { ApiException } from "./ApiException";

export interface IApiRequestHandleBusinessError{
  handleBusinessError(apiException: ApiException): boolean
}

export class ApiRequest{

  private static instance: ApiRequest


  static getInstance (): ApiRequest {
    if (!ApiRequest.instance) {
      ApiRequest.instance = new ApiRequest()
    }
    return ApiRequest.instance
  }

  private _net?: RcpNetworkService | undefined;
  private _i_api_equest_handle_business_error?: IApiRequestHandleBusinessError | undefined;

  public set i_api_equest_handle_business_error(value: IApiRequestHandleBusinessError | undefined) {
    this._i_api_equest_handle_business_error = value;
  }

  public set net(value: RcpNetworkService | undefined) {
    this._net = value;
  }

  public getService() : RcpNetworkService{
    return this._net!;
  }


  

}

コアポイントの解説:

  1. シングルトンパターン:getInstance メソッドを使用して、ApiRequest のグローバルな唯一性を保証します。
  2. 依存性注入:set メソッドを使用して RcpNetworkServiceIApiRequestHandleBusinessError を注入し、クラスの柔軟性を高めます。

(2)統一リクエストメソッド

ApiRequest クラスは、ネットワークリクエストを処理し、結果をカプセル化するための統一リクエストメソッド remoteApi を提供します。

export interface IApiRequestHandleBusinessError {
  handleBusinessError(apiException: ApiException): boolean;
}

export class ApiRequest {
  
  public static async remoteApi<T>(api:()=>Promise<ApiResult<T>>): Promise<DataResult<ApiResult<T>|null>>{
    try{
      const data = await api()
      if(data.success && data.success){
        let bean = new SuccessData<ApiResult<T>>(data)
        return bean;
      } else {
        let bean = ErrorData.fromError(new ApiException(data.code,data.msg) as Error,data.code?.toString(),data.msg)
        return bean;
      }
    }catch (e) {
      if(e instanceof NetworkException){
        let bean = ErrorData.fromError(e)
        //リクエストがフレームワークによってキャンセルされた場合、クライアントは例外処理に入らず、ビジネストーストを避ける
        if(e.code !== ErrorCodes.REQUEST_CANCEL){
          LibToast.show(e.message)
        }
        return bean;
      }
      throw e as Error
    }

  }
}

コアポイントの解説:

  1. リクエストの実行:渡された api 関数を使用して具体的なネットワークリクエストを実行します。
  2. 成功処理:リクエストが成功した場合、SuccessData オブジェクトを返します。
  3. 失敗処理:リクエストに失敗した場合、エラーの種類に応じて ErrorData オブジェクトを返します。
  4. ネットワーク例外処理:NetworkException をキャッチし、エラーコードに応じてトーストメッセージを表示するかどうかを決定します。

(3)ビジネスロジック処理

ApiRequest クラスはまた、ビジネスロジックとエラーを処理するための service メソッドを提供します。

export class ApiRequest {
  // ... その他のコード ...

  public static async service<T>(source:()=>Promise<DataResult<ApiResult<T>|null>>,
    request : (data:DataResult<T|null>)=>void,
    apiError : (apiError:ErrorData)=>void,
    netError? : (netError:ErrorData)=>void,
  ){
    let data = await source();
    if(data instanceof SuccessData){
      request(data.data)
    }else {
      let error = data as ErrorData
      if(error.error&&error.error instanceof ApiException){
        //ビジネス例外
        if(ApiRequest.instance._i_api_equest_handle_business_error){
          ApiRequest.instance._i_api_equest_handle_business_error.handleBusinessError(error.error)
        }
        apiError(error)
      }else {
        //ネットワーク例外
        if(netError){
          netError(error)
        }
      }
    }
  }
}

コアポイントの解説:

  1. リクエストの実行:source 関数を使用してネットワーク要求を実行します。

  2. 成功処理:リクエストが成功した場合、request コールバック関数を使用してデータを処理します。

  3. エラー処理:エラーの種類に応じて apiError または netError コールバック関数を呼び出します。

  4. ビジネスロジックの分離:IApiRequestHandleBusinessError インターフェースを使用してビジネスロジックエラーを処理します。

  5. 公共リクエストヘッダーのカプセル化

実際の開発では、多くのリクエストがデバイス情報、ユーザートークンなど、公共リクエストヘッダーを含める必要があります。CommonHeaderInterceptor クラスを使用して公共リクエストヘッダーをカプセル化しました。

export async function getCommonHeaders(): Promise<Record<string, string>> {
  return {
    "device": LibDevice.getDeviceInfo(),
    "machineCode": await USystem.getUniqueKey(),
"terminalType": "1", // ターミナルタイプの例
"timestamp": new Date().toISOString(),
"versionNumber": LibDevice.getAppVersionCode().toString(),
"osId": Platform.appPlatform, // osId の例
"versionCode": LibDevice.getAppVersionName(),
"token": AccountManager.getToken()
};
}

export class CommonHeaderInterceptor implements rcp.Interceptor {
async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
const commonHeaders = await getCommonHeaders();
const keys = Object.keys(commonHeaders);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (!context.request.headers) {
context.request.headers = {};
}
context.request.headers[key] = commonHeaders[key];
}
return next.handle(context);
}
}

コアポイントの解説

  1. 公共ヘッダー情報getCommonHeaders 関数を使用して公共ヘッダー情報を取得します。
  2. インターセプターの実装intercept メソッド内で、公共ヘッダー情報をリクエストヘッダーに追加します。
  3. 動的追加:ループを使用して公共ヘッダー情報を動的に追加し、各リクエストが必要な情報を含むようにします。

4. レスポンスコンバーターのカプセル化

レスポンスデータをより効果的に処理するために、CommonTextResponseConverter クラスをカプセル化し、レスポンス内容を処理します。

export class CommonTextResponseConverter extends TextResponseConverter {
  convert(response: rcp.Response): string | object | null {
    if (response.toJSON()) {
      return response.toJSON();
    } else {
      return super.convert(response);
    }
  }
}

コアポイントの解説:

  1. JSON 変換:レスポンス内容を優先的に JSON 形式に変換します。

  2. フォールバック処理:JSON に変換できない場合は、親クラスの convert メソッドを使用してレスポンス内容を処理します。

  3. 実践応用ケース:運動データの取得

サーバーからユーザーの運動データ(運動記録、運動計画など)を取得する必要があると仮定します。カプセル化された RCP ネットワークライブラリを使用してこの機能を実現します。

(1)リクエストメソッドのカプセル化

export async function getAllLookSubjectList(params: Record<"parentId", string>, requestKeyFun?: (str: string) => void): Promise<ApiResult<Subject[]>> {
  return ApiRequest.getInstance().getService().request<ApiResult<Subject[]>>>({
    act: AllLOOK_SUBJECT_LIST,
    method: RequestMethod.POST,
    contentType: RcpContentType.JSON,
    content: params
  }, requestKeyFun);
}

コアポイントの解説:

  1. リクエストの設定:RequestOptions を使用してリクエストの基本情報を設定します(API パス、リクエストメソッド、コンテンツタイプなど)。
  2. リクエストの送信:ApiRequest.getInstance().getService().request メソッドを使用してリクエストを送信します。
  3. コールバック関数:requestKeyFun を使用してリクエストキー値を提供し、リクエストのキャンセルなどの操作に使用します。

(2)リクエストメソッドの呼び出し

実際のページやコンポーネントでは、カプセル化されたリクエストメソッドを使用して運動データを取得できます。

aboutToAppear(): void {
  super.aboutToAppear();
  this.showLoading();
  ApiRequest.service<Subject[]>(() => {
    return LookResponsitory.getInstance().getAllLookSubjectList("1", (requestKey) => {
      if (requestKey) {
        this.addRequestId(requestKey);
      }
    });
  }, (data) => {
    this.hideLoading();
    if (data.data && data.data.length > 0) {
      this.tabs = data.data;
    } else {
      this.setDefalutTab();
    }
  }, (apiError) => {
    this.hideLoading();
    this.setDefalutTab();
    // ビジネス例外
    // LibToast.show(apiError.message ?? "取得エラー");
  }, () => {
    this.hideLoading();
    this.setDefalutTab();
    // ネットワークなどの例外
    // LibToast.show("ネットワークエラー");
  });
}

コアポイントの解説:

  1. リクエストの実行:ApiRequest.service メソッドを使用してネットワーク要求を実行します。

  2. 成功処理:リクエストが成功した場合、返されたデータを処理します。

  3. エラー処理:エラーの種類に応じて、対応するコールバック関数を使用してエラーを処理します。

  4. ローディング状態:リクエスト開始時にローディング状態を表示し、リクエスト終了時にローディング状態を隠します。

  5. まとめ

本稿の実践ケースを通じて、カプセル化された RCP ネットワークライブラリを使用して具体的なネットワークリクエスト機能を実現する方法を示しました。カスタム例外クラスの定義、リクエストメソッドのカプセル化、公共リクエストヘッダーとレスポンスコンバーターの処理、具体的なリクエストロジックの実装などを行い、効率的にネットワーク要求タスクを完了することができました。カプセル化されたネットワークライブラリは、基本的なリクエストとレスポンスの処理機能に加えて、エラー処理、ログ記録、セッション管理、ネットワークステータスの検出などの高度な特性を備えており、ほとんどのネットワーク要求シーンに対応できる柔軟性を持っています。

Discussion