【HarmonyOS】纯血鸿蒙真实项目开发---经验总结贴

项目场景:

将已有的Web网页接入到原生App。

涉及到一些网页回退 、webviewController执行时机报错1710000001 、位置定位数据获取拉起呼叫页面系统分享能力使用等。


问题描述

  1. 我们在选项卡组件中,在每个TabContent内容页中使用web组件加载网页。
    在最开始接入操作时,出现了Web组件加载某个页面,出现白屏、页面显示不出来。

解决方案:

经过对问题定位,发现需要设置是否开启文档对象模型存储接口 (DOM Storage API)权限,默认未开启

typescript 复制代码
Web({ src: this.webSrc, controller: this.webviewController })
  .domStorageAccess(true);

问题描述

  1. 每个TabContent内容页中使用web组件加载的网页,会涉及到一些页面的返回。如果是用户点击返回,网页端会进行回退操作,若用户使用侧滑返回网页端无法检测,会直接退出应用。

解决方案:

为了解决这个问题,我查阅到了有关侧滑返回或点击返回按钮时触发的自定义组件生命周期onBackPress(仅@Entry装饰的自定义组件生效)

typescript 复制代码
@Entry
@Component
struct IndexComponent {
  @State textColor: Color = Color.Black;

  onBackPress() {
    this.textColor = Color.Red;
    console.info('IndexComponent onBackPress');
  }

  build() {
    Column() {
      Text('Hello World')
        .fontColor(this.textColor)
        .fontSize(30)
        .margin(30)
    }.width('100%')
  }
}

返回true表示页面自己处理返回逻辑,不进行页面路由;返回false表示使用默认的路由返回逻辑,不设置返回值按照false处理。

重点:在每个TabContent内容页加载的@Entry页面里,使用onBackPress是不生效的。我们只能在Tab所在的@Entry页面里,使用onBackPress才会生效。
为了控制TabContent中的Web页面回退,我们需要在Tab所在的页面里创建webviewController并将它传递给子组件。

typescript 复制代码
父组件示例代码:
import { Page1, Page2 } from './Page1'

@Entry
@Component
struct demo {
  //controller创建
  @Provide webcontroller1: webview.WebviewController = new webview.WebviewController();
  @Provide webcontroller2: webview.WebviewController = new webview.WebviewController();
  //返回逻辑
  onBackPress() {
    if (this.webcontroller1.accessStep(-1)) {
      this.webcontroller1.backward()
      // 执行用户自定义返回逻辑
      return true;
    } else if (this.webcontroller2.accessStep(-1)) {
      this.webcontroller2.backward();
      // 执行用户自定义返回逻辑
      return true;
    }  else {
      //执行系统默认返回逻辑,返回上一个page页
      return false;
    }
  }

  build() {
    Tabs(){
      TabContent(){
        Page1()
      }.tabBar('示例一')
      TabContent(){
        Page2()
      }.tabBar('示例二')
    }
  }
}
typescript 复制代码
子组件示例代码:
import { webview } from '@kit.ArkWeb'

@Entry
@Component
export struct Page1/2 {
 @Consume webcontroller1/2:webview.WebviewController

 build() {
   RelativeContainer() {
     Web({src:'https://www.baidu.com',controller:this.webcontroller1/2})
       .domStorageAccess(true)
   }
   .height('100%')
   .width('100%')
 }
}

问题描述

  1. 因为每个Web页面的控制器是由父组件传入的,我们在使用时可能会出现710000001的报错,经查阅论坛发现,该报错原因是Web组件还未创建,我们的WebviewController控制器先执行了。

高发于在emitter中调用WebviewController控制器,尽量避免在emitter中使用,若必须使用参照下面的解决方案

解决方案:

解决这个问题,我并没有发现可以统一解决各种情况的方法。以下有多种解决方法:

  • 1\] 在web组件的生命周期onPageEnd中使用控制器

  • 3\]在onDisAppear中使用控制器

  1. 我们在一些场景下可能会需要用户的具体位置,以下是关于用户授权和逆地址编码转化,获取地址位置描述的方案总结。

解决方案:

typescript 复制代码
获取用户位置授权
  onClickGetLocation = async () => {
    //首次权限请求
    if (this.step == 0) {
      const steps = await abilityAccessCtrl.createAtManager()
        .requestPermissionsFromUser(getContext(),
          ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'])
      this.step++
    }
    // 二次请求用户同意权限
    await this.checkPermissions()
    //获取当前位置
    let Location = await geoLocationManager.getCurrentLocation();
    await this.locationChange(Location)

    return JSON.stringify(this.returnInfor)
  }
typescript 复制代码
若用户拒绝定位权限二次拉起
  async checkPermissions() {
    let grantStatus1: boolean = await WebUtils.checkPermissionGrant('ohos.permission.LOCATION') ===
    abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; // 获取精确定位权限状态
    let grantStatus2: boolean = await WebUtils.checkPermissionGrant('ohos.permission.APPROXIMATELY_LOCATION') ===
    abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED; // 获取模糊定位权限状态
    // 精确定位权限只能跟模糊定位权限一起申请,或者已经有模糊定位权限才能申请精确定位权限
    if (!grantStatus2 && !grantStatus1) {
      // 用户拒绝二次拉起弹窗
      let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
      let context: Context = getContext(this) as common.UIAbilityContext;

      await atManager.requestPermissionsFromUser(context,
        ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'])
      await atManager.requestPermissionOnSetting(context, ['ohos.permission.APPROXIMATELY_LOCATION'])
        .then((data: Array<abilityAccessCtrl.GrantStatus>) => {
          console.info('data:' + JSON.stringify(data));
        })
        .catch((err: BusinessError) => {
          console.error('data:' + JSON.stringify(err));
        });
    } else {
      // 已经授权,可以继续访问目标操作
      return
    }
  }
typescript 复制代码
/** * 定位回调 */
  //获取用户位置信息
  private async locationChange(location: geoLocationManager.Location) {
    if (location) {
      let reverseGeocodeRequest: geoLocationManager.ReverseGeoCodeRequest = {
        'latitude': location.latitude, // 表示纬度信息,正值表示北纬,负值表示南纬。取值范围为-90到90。仅支持WGS84坐标系。
        'longitude': location.longitude, // 表示经度信息,正值表示东经,负值表是西经。取值范围为-180到180。仅支持WGS84坐标系。
        // 指定返回位置信息的最大个数。取值范围为大于等于0,推荐该值小于10。默认值是1。
        'maxItems': 1
      };
      // 逆地址编码转化,获取地址位置描述
      let data = await geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest);
      if (data) {
        if (data[0].locality !== undefined) {
          let res = data[0] as DeatilPositionViewPage
          let placeName = res.placeName
          let countryName = res.countryName
          let provience = placeName.slice(0, placeName.indexOf('省') + 1)
          let city = placeName.slice(placeName.indexOf('省') + 1, placeName.indexOf('市') + 1)
          let area = placeName.slice(placeName.indexOf('市') + 1, placeName.indexOf('区') + 1)
          let deatil = placeName.slice(placeName.indexOf('区') + 1)
          let address = `${countryName} ${provience} ${city} ${area} ${deatil}`
          this.returnInfor = {
            address: address,
            longitude: res.longitude,
            latitude: res.latitude
          }
        }
      }
    }
  };
typescript 复制代码
运行使用代码块
@Entry
@Component
export struct Page1 {
  @State returnInfor: returnDeatilPosition = {
    address: '',
    longitude: 0,
    latitude: 0
  }
  //判断是否为首次请求
  step = 0
  
  //写入上面三个方法

  build() {
    RelativeContainer() {
     Button(this.returnInfor)
       .onclick(async()=>{await this.onClickGetLocation()})
    }
    .height('100%')
    .width('100%')
  }
}

问题描述

  1. 我们在一些场景下可能会需要拉起用户的拨号界面,以下是关于拉起拨号的方案总结。

值得注意的是如果这里是混合开发需要的功能,我们可以把该方法写在.onLoadIntercept((event) => {})这个方法里面。具体相关使用自行查阅官方文档,较为简单不做赘述。

解决方案:

typescript 复制代码
import { call, observer } from '@kit.TelephonyKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 调用查询能力接口

@Entry
@Component
struct callphone {
  build() {
    Text('1111').onClick(()=>{
      let isSupport = call.hasVoiceCapability();
      if (isSupport) {
        // 如果设备支持呼叫能力,则继续跳转到拨号界面,并显示拨号的号码
        call.makeCall("4009609206", (err: BusinessError) => {
        });

      }
    })
  }
}

问题描述

  1. 我们在一些场景下可能会需要用到系统分享能力,以下是关于系统分享的方案总结。

解决方案:

typescript 复制代码
async share(ctx: Context, url: string, title: string) {
    let shareData: systemShare.SharedData = new systemShare.SharedData({
      utd: uniformTypeDescriptor.UniformDataType.HYPERLINK,
      content: url,
      title: title,
      description: '分享此链接',
    });
相关推荐
风中飘爻3 小时前
鸿蒙生态:鸿蒙生态校园行心得
华为·harmonyos
Aqua Cheng.3 小时前
华为开发岗暑期实习笔试(2025年4月16日)
java·算法·华为·动态规划
Linux运维老纪6 小时前
交换机之配置系统基本信息(Basic Information of the Configuration System for Switches)
linux·网络·mysql·华为·云计算·运维开发
高心星19 小时前
HarmonyOS 5.0应用开发——MVVM模式的应用
harmonyos·mvvm·鸿蒙5.0·备忘录应用
别说我什么都不会20 小时前
【仓颉三方库】工具类—— compress4cj
harmonyos
别说我什么都不会20 小时前
【仓颉三方库】工具类—— uuid4cj
harmonyos
__Benco1 天前
OpenHarmony - 小型系统内核(LiteOS-A)(十),魔法键使用方法,用户态异常信息说明
人工智能·harmonyos
NapleC2 天前
HarmonyOS:一多能力介绍:一次开发,多端部署
华为·harmonyos
我爱学习_zwj2 天前
【鸿蒙HarmonyOS】深入理解router与Navigation
华为·harmonyos