HarmonyOS 鸿蒙Next 如何优雅的在网络框架中设置请求时的loading和请求返回时取消

发布于 1周前 作者 songsunli 最后一次编辑是 5天前 来自 鸿蒙OS

如下,是封装的一个网络请求类

import { http } from '[@kit](/user/kit).NetworkKit'
import NetConstant from './NetConstant'
import { showToast } from '../utils/ToastUtil'
import { BaseResponse } from './BaseResponse'
import { LogUtil } from '../log/LogUtil'
import { LoadingDialog } from '../view/LoadingDialog'

const httpRequest = http.createHttp()
const loadingDialogController: CustomDialogController = new CustomDialogController({
  builder: LoadingDialog({ loadingTips: "this.loadingTips" }),
  autoCancel: true,
  gridCount: 2
})

async function requestHttp<T>(url: string = "",
  method: http.RequestMethod = http.RequestMethod.GET, data?: object): Promise<T> {
  let requestUrl = NetConstant.BASE_URL + url
  if (method === http.RequestMethod.GET) {
    if (data && Object.keys(data).length) {
      requestUrl += '?' + Object.keys(data).map(key => {
        if (data[key]) {
          return `${key}=${data[key]}`
        }
        return ''
      }).join('&')
    }
  }
  const token = AppStorage.get(NetConstant.ACCESS_TOKEN) as string
  let headers: Record<string, Object> = {}
  headers['Content-Type'] = 'application/json'
  if (token.length > 0) {
    headers['Authorization'] = 'Bearer ' + token
  }
  const config: http.HttpRequestOptions = {
    header: headers,
    method,
    readTimeout: NetConstant.HTTP_READ_TIMEOUT,
    extraData: method === http.RequestMethod.GET ? '' : data
  }
  try {
    const res = await httpRequest.request(requestUrl, config)
    if (res.responseCode === 401) {
      showToast('token超时')
      return Promise.reject(new Error('token 不存在或超时'))
    } else if (res.responseCode === 404) {
      showToast('请求地址不正确')
      return Promise.reject(new Error('token 请求地址不正确'))
    } else {
      const result = JSON.parse(res.result as string) as BaseResponse<T>
      if (result.code === 200 || result.code === 0) {
        LogUtil.error(JSON.stringify(result.data))
        LogUtil.info('转换前')
        let finalResult: T = result.data as T
        LogUtil.info('转换后')
        return finalResult
      } else {
        showToast(result.msg)
        return Promise.reject(new Error(result.msg))
      }
    }
  } catch (err) {
    showToast(err.message)
    return Promise.reject(err)
  } finally {
    httpRequest.destroy()
  }
}

export default class Request {
  export
  function

  httpGet<T>(url: string, data?: object): Promise<T> {
    return requestHttp<T>(url, http.RequestMethod.GET, data)
  }

  export
  function

  httpPost<T>(url: string, data?: object): Promise<T> {
    return requestHttp<T>
    (url, http.RequestMethod.POST, data)
  }

  export
  function

  httpPut<T>(url: string, data?: object): Promise<T> {
    return requestHttp<T>(url, http.RequestMethod.PUT, data)
  }

  export
  function

  httpDelete<T>(url: string, data?: object): Promise<T> {
    return requestHttp<T>(url, http.RequestMethod.DELETE, data)
  }
}

[@Entry](/user/Entry)
[@Component](/user/Component)
export struct LoadingPage {
  [@Prop](/user/Prop) flag: boolean;

  build() {
    Row() {
      LoadingProgress().color(Color.White).width(50).height(50)
    }
    .height(this.flag ? '100%' : 0)
    .width('100%')
    .position({ x: 0, y: 0 })
    .backgroundColor('#4D000000')
    .justifyContent(FlexAlign.Center)
  }
}

[@Component](/user/Component)
export struct HomePage {
  [@State](/user/State) homePageBean: HomePageBean = new HomePageBean()
  [@State](/user/State) coreAreaArrayOne: Array<CoreAreaInfo> = [new CoreAreaInfo()]
  [@State](/user/State) coreAreaArrayTwo: Array<CoreAreaInfo> = [new CoreAreaInfo()]
  [@State](/user/State) resHotAreaDTOList: ResHotAreaDTOList = new ResHotAreaDTOList()
  [@State](/user/State) publicBenefitPageVOList: Array<PublicBenefitPageVO> = new Array()
  [@State](/user/State) list: Array<BannerInfo> | Array<RightTop> = []
  this
  .showLoading = false

  aboutToAppear(): void {
    showLoading = true;
    LoadingPage({ flag: this.showLoading });
    httpGet<HomePageBean>(Constants.HOME_PAGE_MAIN_DATA).then(
      homePageBean => {

        showLoading = false
        const data = JSON.stringify(homePageBean)
        console.log('Michael 主页 JSON:', data)

        this.coreAreaArrayOne = homePageBean.coreAreaListOne
        this.coreAreaArrayTwo = homePageBean.coreAreaListTwo
        this.resHotAreaDTOList = homePageBean.resHotAreaDTOList
        this.publicBenefitPageVOList = homePageBean.publicBenefitPageVOList
        this.list = homePageBean.bannerList
      })
  } 

应用会有很多类似的这样的界面 如果每个界面都像上面那么写,势必很麻烦,那么如何在我封装的框架中进行请求的显示与隐藏的逻辑呢? 可提供你们的通用做法和demo


更多关于HarmonyOS 鸿蒙Next 如何优雅的在网络框架中设置请求时的loading和请求返回时取消的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

2 回复

// CommonWindow.ets

import window from '@ohos.window';

import common from '@ohos.app.ability.common';

import { BusinessError } from '@ohos.base';

import { entryName } from './MainPage';

export class CommonWindow {

  private storage: LocalStorage | null = null;

  private subWindow: window.Window | null = null;

  private windowStage1: window.WindowStage | null = null

  private context: common.UIAbilityContext | null = null;

  private init() {

    this.context = getContext(this) as common.UIAbilityContext;

    let data: Data = { subWindowStage: null, storage: null };

    this.context.eventHub.emit("createWindow", data);

    this.windowStage1 = data.subWindowStage;

    this.storage = data.storage;

    console.log("aboutToAppear end createWindowStage");

    this.context.eventHub.on("closeWindow", (data: Data) => {

      this.destroySubWindow();

    })

  }

  showWindow() {

    this.init();

    if (this.subWindow) {

      console.log("subWindow is already exist");

      return;

    }

    try {

      if (!this.windowStage1) {

        console.error("this.windowStage1 is null");

        return;

      }

      this.windowStage1.createSubWindow('mySubWindow', (err: BusinessError, data) => {

        const errCode: number = err.code;

        if (errCode) {

          console.error('Failed to create the subWindow. Cause: ' + JSON.stringify(err));

          return;

        }

        this.subWindow = (data as window.Window);

        console.info('Succeeded in creating the subWindow. Data: ' + JSON.stringify(data));

        if (!this.subWindow) {

          console.info('Failed to load the content. Cause: windowClass is null');

        } else {

          let names: Array<'status' | 'navigation'> = [];

          this.subWindow.setWindowSystemBarEnable(names);

          this.subWindow.setWindowTouchable(true);

          this.loadContent(entryName);

          this.showSubWindow();

        }

      });

    } catch (exception) {

      console.error('Failed to create the window. Cause: ' + JSON.stringify(exception));

    }

  }



  private showSubWindow() {

    if (this.subWindow) {

      this.subWindow.showWindow((err: BusinessError) => {

        const errCode: number = err.code;

        if (errCode) {

          console.error('Failed to show the window. Cause: ' + JSON.stringify(err));

          return;

        }

        console.info('Succeeded in showing the window.');

      });

    } else {

      console.info('showSubWindow subWindow not created.');

    }

  }

  private destroySubWindow() {

    if (this.subWindow) {

      this.subWindow.destroyWindow((err) => {

        const errCode: number = err.code;

        if (errCode) {

          console.error('Failed to destroy the window. Cause:' + JSON.stringify(err));

          return;

        }

        this.subWindow = null

      });

    } else {

      console.info('showSubWindow subWindow not created.');

    }

  }

  private loadContent(path: string) {

    if (this.subWindow) {

      let that = this;

      let para: Record<string, number> = { 'PropA': 66 };

      that.storage = new LocalStorage(para);

      if (that.storage != null && this.subWindow != null) {

        that.storage.setOrCreate("windowObj", this.subWindow)

      }

      this.subWindow.loadContentByName(path, this.storage, (err: BusinessError) => {

        const errCode: number = err.code;

        if (errCode) {

          return;

        }

        if (this.subWindow) {

          this.subWindow.setWindowBackgroundColor('#cc000e03')

        }

      });

    } else {

      console.info('loadContent subWindow not created.');

    }

  }

}

export interface Data {

  subWindowStage: window.WindowStage | null,

  storage: LocalStorage | null

}

// MainPage.ets

import window from '@ohos.window';

export const entryName: string = 'loadingPage';

@Entry({ routeName: entryName, storage: LocalStorage.getShared() })

@Component

export struct MainPage {

  @LocalStorageLink('PropA') var

  number | undefined = 1;

  private subWindow: window.Window | undefined;

  build() {

    Column() {

      LoadingProgress().width(150)

    }

    .justifyContent(FlexAlign.Center)

    .height('100%')

    .width('100%')

  }

  // 页面生命周期:打开沉浸式

  onPageShow() {

    window.getLastWindow(getContext(this), (err, win) => {

      // 获取当前窗口的属性

      let prop: window.WindowProperties = win.getWindowProperties();

      // 打印当前窗口属性

      console.log(JSON.stringify(prop));

      win.setWindowLayoutFullScreen(true)

    })

  }

  // 页面生命周期:关闭沉浸式

  onPageHide() {

    window.getLastWindow(getContext(this), (err, win) => {

      win.setWindowLayoutFullScreen(false)

    })

  }

  aboutToAppear() {

    this.varA = LocalStorage.getShared().get<number>("PropA");

    this.subWindow = LocalStorage.getShared().get<window.Window>("windowObj");

  }

}

EntryAbility.ets中

1、增加subWindowStage定义

private subWindowStage: window.WindowStage | undefined = undefined;

2、onCreate增加监听

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {

    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');

    const that: EntryAbility = this

    this.context.eventHub.on("createWindow", (data: Data) => {

      if (that.subWindowStage != undefined) {

        data.subWindowStage = that.subWindowStage

      } else {

        hilog.info(0x0000, 'testTag', '%{public}s', 'that.subWindowStage == undefined');

      }

    })

  }

3、onWindowStageCreate中增加赋值 this.subWindowStage = windowStage

使用:

public httpRequest(reqMethod: http.RequestMethod, url: string): Promise<Object> {

  // 每一个httpRequest对应一个HTTP请求任务,不可复用

  let httpRequest = http.createHttp();

  let promise = httpRequest.request(url, {

    method: reqMethod,

    readTimeout: 10000,

    connectTimeout: 10000

  });

  new CommonWindow().showWindow();

  //Processes the data and returns.

  return new Promise((resolve, reject) => {

  promise.then((resp) => {

  this.closeSubWindow()

  if (resp.responseCode === http.ResponseCode.OK) {

  //Obtains the returned data.

  const respData:Object = typeof resp.result === 'string' ? JSON.parse(resp.result) : resp.result

  resolve(respData)

} else {

  httpRequest.destroy();

  reject('error')

}

}).catch((err:BusinessError) => {

  this.closeSubWindow()

  httpRequest.destroy();

  reject('error');

})

})

}

closeSubWindow() {

  let context = getContext(this) as common.UIAbilityContext;

  context.eventHub.emit("closeWindow", null);

}

更多关于HarmonyOS 鸿蒙Next 如何优雅的在网络框架中设置请求时的loading和请求返回时取消的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,要在网络框架中优雅地设置请求时的loading和请求返回时取消,可以遵循以下步骤:

  1. 定义状态管理:在数据层或ViewModel中定义请求状态,如loadingsuccesserror等。

  2. 网络请求发起:在发起网络请求时,将状态设置为loading,并显示加载动画或提示。

  3. 请求取消机制

    • 使用CancellationToken或类似机制管理请求的生命周期。
    • 在UI层或ViewModel层提供一个取消请求的接口,比如cancelRequest()
    • 在请求返回或用户触发取消操作时,调用cancelRequest()取消网络请求。
  4. 请求返回处理

    • 当请求成功返回时,更新状态为success,并处理返回的数据。
    • 若请求失败(包括取消),更新状态为error,并显示错误信息。
    • 无论成功或失败,都需要将loading状态重置。
  5. UI层响应:根据数据层或ViewModel层的状态变化,动态显示或隐藏加载动画、成功提示或错误提示。

通过以上步骤,可以在HarmonyOS鸿蒙Next中优雅地管理网络请求的状态,确保请求时的loading显示和请求返回时的取消操作得以妥善处理。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部