普通视图

发现新文章,点击刷新页面。
昨天 — 2025年1月18日首页

鸿蒙NEXT开发浅进阶到精通02:APP隐私弹窗和启动页自定义创建

2025年1月18日 17:02

写在前面

在APP开发中呈现给用户的第一页面就是隐私政策和用户协议弹窗(后文简称隐私弹框)了,用老实话来讲,一个APP和用户没有什么法律纠纷就没有,有用到时,这两个文件就起到大的用途。 再白话一点讲,没这个弹框,上架审核不会通过的。而开发一个鸿蒙Next APP的隐私弹窗呢,本文将会从产品业务逻辑层面和代码层面逐一叙述,看完和CV本文代码,可满足绝大多少APP的场景需要,当然演示项目,UI还需自行优化。这里有个好消息就是鸿蒙元服务的这一块弹框是不需要开发的,官方只需要你在AGC平台上填入对应的链接即可。

image.png

概念概述

隐私弹框与APP内的其他常见弹框,如拨打电话弹框-删除提示弹框的场景,但是在产品和出现逻辑上有本质区别,隐私弹框讲究的是,用户首次下载弹出,不点击同意,就退出APP,点击同意后进入APP,再次打开除非重新下载安装不会再弹出。而且重要一点要防止用户什么也不点击,使用屏幕侧滑返回,来去掉弹框直接使用APP。

实战流程和过程注解

在上文我们提到隐私弹框和其他弹框的本质区别:

  1. 用户首次下载弹出
  2. 不点击同意或者点击不同意/取消等,就退出APP
  3. 点击同意后进入APP,以后再进入APP不会弹出
  4. 放在屏幕侧滑,让弹框消失,用户越过隐私弹框使用APP

那么我们也逐一展开,首先看下实际中一些朋友遇到的问题和想法 小白:这个弹窗干啥用的?

大白答:为了各种要求规定吧,反正你不点同意,这个APP主页用户股东是进不去了,那这个同意一次就OK了,卸载之前或者注销和切换账号前就不用弹出。

那么既然后一次性必点属性,肯定要设置一个全局持久变量是最基本的(如果有账号相关就住这之前去请求后端的这个字段)

小白:那这个弹框在哪出现嘞,有尝试过在主页面index中的aboutTo里弹出,好像也行哦!

大白答:行是行,但根据路由的尿性,用户点击返回,或者侧滑返回,也能进入页面,这时候就审核不过,因为用户并没有同意你的隐私协议,就会存在争议和风险。

所以启动页就特么诞生了,又能做广告宣传海报,又能防止用户无门槛进入APP/元服务

水了这么多,上代码(这里以简单的无后端账号信息 的demo举例个栗子)

一、设置启动页

在EntryAbility中,进入pages/inex之前,创建一个Windows展示

image.png

   // 创建一个子窗口 子窗口加载广告 广告播完 窗口销毁
    let isFirstOpen = AppStorage.get('isFirstOpen') as boolean
    if (!isFirstOpen) {
      const win = await windowStage.createSubWindow('ad_window')
      await win.showWindow()
      win.setUIContent('pages/Start')
    }

这里有个布尔值,就是持久化来的,判断要不要创建这个启动页,在客户要求有启动页的时候,可以设置在页面内部控制这个变量,是否再弹出弹框,这里Demo直接控制是否有启动页(v的时候要注意哦) 至于为什么设置启动页,上文提到是一举两得,还有一个层面是笔者不设置启动页时,对于用户侧滑屏幕操作越过这个弹框,不好控制。有不设置启动页,也能防止用户越过弹框也还请评论区交流

二、页面有了,创建弹窗,在这个里面用那个持久布尔值isFirstOpen来做判断,可以用首选项也可以本地持久化

启动页代码

import { window } from '@kit.ArkUI'
import { agreementPolicyDialog } from '../components/agreementPolicyDialog'
import common from '@ohos.app.ability.common'
PersistentStorage.persistProp('isFirstOpen', false)

const context = getContext(this) as common.UIAbilityContext

@Entry
@Component
struct Start {
  @StorageLink('isFirstOpen') isFirstOpen: boolean = false

  async onPageShow() {

    if (this.isFirstOpen) {
      this.closeWin()

    } else {
      this.controller.open()
    }
  }

  controller: CustomDialogController = new CustomDialogController({
    builder: agreementPolicyDialog(), customStyle: true, cancel: () => {
      context.terminateSelf()
    }
  })

  onBackPress(): boolean | void {
    if (!this.isFirstOpen) {
      context.terminateSelf()
      // this.controller.close()
    }
    return true
  }

  //关闭广告
  closeWin() {
    window.findWindow('ad_window').destroyWindow()
  }

  build() {
    Stack({ alignContent: Alignment.TopEnd }) {


    }.height('100%').width('100%').backgroundColor(Color.White)
  }
}

这里代码就是启动页页面,弹框也在这里弹出,有意思的是我们设置启动页背景为纯白色,在不添加企业海报广告或者logo时,弹出弹框的UI效果跟元服务类似。

创建弹框结构就截图吧,这里注意要给到点击的字段隐私政策和协议的标题,使用text内部套span即可设置隐私和协议的颜色与点击事件

image.png

在设置弹框的时候,要注意cancel函数 的调用,放在返回事件中调用一次,防止用户返回,返回也执行这个取消函数内的退出APP代码

三、意外并重要的知识点--无损退出APP 的代码

image.png 这行代码要注意,收到那个导入window方法和创建实例再在页面中调用,一是在取消按钮下,一是在onBackPress这个生命函数中使用,因为用户看到弹窗,不惦记取消和确定,直接返回也是一种“不同意”,也是要退出APP的,这段代码及其珍贵,看到这里一定要给个赞哦,,

四、注意一次性布尔值的判断逻辑

image.png 那他同意了,就好办了,设置为true,销毁这个启动页,就自动进入主页面了,如果保留海报页仅控制这个弹窗的出现次数,就住EntryAbility中取消判断页面的创建,这里做一个定时器销毁启动页即可,这里不过多赘述

那不同意,也好办,你甚至不用设置false,直接关闭弹窗并退出APP了

image.png

因为你在初始化这个持久变量时,就设置了false(这里也展示了如何注册持久化变量,这个要放在整个应用启动前,在老版鸿蒙编辑器中,mac版是可以放在entryability中的,现在应该是不让了,有兴趣的朋友可以试试)

HarmonyOSNext 端云一体化(4)

作者 万少
2025年1月18日 00:54

HarmonyOSNext 端云一体化(4)

在上一章节我们讲了数据库数据表的一些基本操作。如query、upsert、delete和calculateQuery。这一章节主要来讲解各种查询条件操作。如 查询班级年龄大于30的同学等。

查询条件解释

谓词,用来代替或者展示其客体性质、特征或者客体之间关系的词项

这些查询条件在端云一体中解释中叫做谓词。云数据库中提供丰富的谓词查询来构建查询条件。根据谓词查询方法构造自己的

DatabaseQuery对象。

查询条件谓词一览

关键字 说明
equalTo 表示等于的条件判断,用于查询中筛选出与指定值相等的数据
notEqualTo 表示不等于的条件判断,筛选出与指定值不相等的数据
beginsWith 表示以某个值开头,用于查询开头匹配特定字符串的数据
endsWith 表示以某个值结尾,用于查询结尾匹配特定字符串的数据
contains 表示包含某个值,用于查询包含特定字符串的数据
greaterThan 表示大于,用于数值类型数据的比较,筛选出大于指定值的数据
greaterThanOrEqualTo 表示大于或等于,筛选出大于或等于指定值的数据
lessThan 表示小于,用于数值类型数据的比较,筛选出小于指定值的数据
lessThanOrEqualTo 表示小于或等于,筛选出小于或等于指定值的数据
in 用于判断某个值是否在指定的集合内,常用于查询符合多个值中某一个的数据
isNull 用于判断某个字段是否为空值
isNotNull 用于判断某个字段是否不为空值
orderByAsc 按升序排列,用于对查询结果按照指定字段进行从小到大的排序
orderByDesc 按降序排列,用于对查询结果按照指定字段进行从大到小的排序
limit 限制查询结果返回的数量
beginGroup 开始一个逻辑分组,用于将多个条件组合在一起作为一个逻辑单元
endGroup 结束一个逻辑分组
or 逻辑或,用于连接多个条件,只要其中一个条件满足则整个逻辑表达式为真
and 逻辑与,用于连接多个条件,只有所有条件都满足时整个逻辑表达式才为真

谓词使用示例

equalTo 查询id为20的数据

this.condition.equalTo("id", 20) 

notEqualTo 查询id不等于20的数据

this.condition.notEqualTo("id", 20) 

beginsWith 查询name字段以b开头的数据

this.condition.beginsWith("name", "b") 

endsWith 查询name字段以k结尾的数据

this.condition.endsWith("name", "k") 

contains 查询name字段包含k的数据

this.condition.contains("name", "k") 

greaterThan 查询price字段大于30的数据

this.condition.greaterThan("price", 30) 

greaterThanOrEqualTo 查询price字段大于或者等于30的数据

this.condition.greaterThanOrEqualTo("price", 30) 

lessThan 查询price字段小于30的数据

this.condition.lessThan("price", 30) 

lessThanOrEqualTo 查询price字段小于或者等于30的数据

this.condition.lessThanOrEqualTo("price", 30) 

in 查询name字段包含在["book","aaaa","bbbb"]的中数据

this.condition.in("name", ["book", "aaaa", "bbbb"]) 

isNull 查询name字段是否为null

this.condition.isNull("name") 

isNotNull 查询name字段是否非null

this.condition.isNotNull("name") 

orderByAsc 根据id,进行升序

this.condition.orderByAsc("id") 

orderByDesc 根据id,进行降序

this.condition.orderByDesc("id") 

limit 查询2条数据,从第1条开始

this.condition.limit(2, 1) 

or 逻辑或,查询name=book 或者 price>30的数据

this.condition.equalTo("name", "book").or().greaterThan('price', 30) 

and 逻辑与,查询name=book123 并且 price>30的数据

this.condition.equalTo("name", "book123").and().greaterThan('price', 30) 

beginGroup 和 endGroup 表示一对逻辑分组

 // 条件1:   name=book并且price>30

  // 条件2:   id=20或者price>30

  // 需求: 查询 条件1 和 条件2 同时满足的数据
  this.condition
    .beginGroup()
    .equalTo('name', 30)
    .and()
    .greaterThan('price', 30)
    .endGroup()
    .and()
    .beginGroup()
    .equalTo('id', 20)
    .or()
    .greaterThan('price', 30)
    .endGroup();

加强

上面的谓词,也是根据实际语义搭配一起使用。比如:查询name=book的前2条数据

总结

本章主要介绍了HarmonyOSNext端云一体化中的数据库查询条件操作:

  1. 介绍了查询条件中的谓词概念,它用于展示或描述数据的性质、特征或关系

  2. 详细列举了常用的查询谓词,包括:

    • 比较类:equalTo、notEqualTo、greaterThan、lessThan等
    • 字符串匹配:beginsWith、endsWith、contains
    • 空值判断:isNull、isNotNull
    • 集合操作:in
    • 排序限制:orderByAsc、orderByDesc、limit
    • 逻辑组合:and、or、beginGroup、endGroup
  3. 通过具体示例展示了各种谓词的使用方法,包括基本查询和复杂的组合查询

  4. 说明了谓词可以根据实际需求灵活组合使用,以实现更复杂的查询功能


如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯,欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。

昨天以前首页

HarmonyOS Next 端云一体化(3)

作者 万少
2025年1月17日 21:36

HarmonyOS Next 端云一体化(3)

上一章我们主要讲解了如何新建数据库、新建数据表已经部署数据库。这一章主要学习如何对数据库、数据表进行 CRUD 的操作。

操作数据库的方式

我们操作数据库的方式一共有 4 种。

  1. 可视化 - AGC 平台上直接编辑数据
  2. 可视化 - DevEco Studio 中直接编辑数据
  3. 编程 - 客户端通过代码的方式操作数据
  4. 编程 - 云函数通过代码的方式操作数据

方式 1、2 都是为了让开发人员简单、方便管理数据。但是实际的业务场景中,我们更多要关注的是 3、4 的方式。那么本章主要讲的是 方式 3-客户端通过代码的方式操作数据 。后续再讲到云函数的时候再来补充方式 4。

生成客户端-数据模型

先解释下这个功能是做什么的。因为我们的目标是要在 客户端来查询数据库的数据,那必不可少需要在客户端中定义数据表实体的类型。然后 DevEco Studio 提供了比较便捷的根据数据实体生成客户端-数据模型。

image-20250117001110198

我这里红色的提示是因为我之前已经生成过了,所以提示是否覆盖。

成功后边得到如下内容:entry/src/main/ets/common/types/Book.ts

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 * Generated by the CloudDB ObjectType compiler. DO NOT EDIT!
 */
import { cloudDatabase } from "@kit.CloudFoundationKit";

class Book extends cloudDatabase.DatabaseObject {
  id: number;
  name: string;
  price: number;
  publish: Date;
  hot: boolean;
  cover: string;

  naturalbase_ClassName(): string {
    return "Book";
  }
}

export { Book };

简单使用

接下来我们就可以进入客户端查询数据库的步骤了。

  1. 首先我们需要创建一个数据库示例,每一个存储区就是一个数据库 cloudDatabase.DatabaseZone
  2. 然后指定查询条件,比如全部查询、查询 id 等于 1 等等 condition
  3. 进行查询,接收返回的数据
import { cloudDatabase } from '@kit.CloudFoundationKit';
import { Book } from '../common/types/Book';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct PageDB {
  // 数据库实例,初始化时为 undefined
  agcDataBase: cloudDatabase.DatabaseZone | undefined = undefined;
  // 查询条件实例,初始化时为 undefined
  condition: cloudDatabase.DatabaseQuery<cloudDatabase.DatabaseObject> | undefined = undefined;
  // 初始化数据库连接的方法
  fn1 = () => {
    this.agcDataBase = cloudDatabase.zone('Study');
    promptAction.showToast({ message: `初始化成功` });
  }
  // 查询数据库的方法
  fn2 = async () => {
    try {
      // 创建查询条件实例
      this.condition = new cloudDatabase.DatabaseQuery(Book);
      // 设置查询结果的最大数量为 10
      this.condition.limit(10);
      // 执行查询并获取结果
      const resultArray = await this.agcDataBase?.query(this.condition);
      // 显示查询结果
      AlertDialog.show({ message: JSON.stringify(resultArray, null, 2) });
    } catch (e) {
      promptAction.showToast({ message: `${e.message} ${e.code}` });
      console.error(e.message, e.code);
    }
  }

  build() {
    Column({ space: 10 }) {
      Button("初始化1")
        .onClick(this.fn1)

      Button("查询2")
        .onClick(this.fn2)
    }
    .height('100%')
    .width('100%')
  }
}

image-20250117001731385

对数据表的操作

端云一体提供了基本的对数据表的操作。主要分成以下几种

操作类型 说明
query 查询
upsert 新增或者编辑
delete 删除
calculateQuery 计算

query

就是查询,上面的示例中已经使用过了。

upsert - 新增

现在主要演示 使用 upsert 实现新增

upsert 方法可以接收一个或者多个数据实体。如果该数据之前不存在,这时的 upsert 表示新增,反之表示更新。

操作成功后,会返回成功影响了的数据的数量。

比如新增:

fn3 = async () => {
  try {
    const book = new Book();
    book.id = parseInt(Date.now().toString().slice(0, 6)); // 正常应该是自增的,但是这个自增会出bug
    book.name = "book";
    book.price = 99;
    book.publish = new Date();
    book.hot = true;
    book.cover = "xxxx";
    const res = await this.agcDataBase?.upsert(book);
    AlertDialog.show({ message: `新增成了${res}条` });
  } catch (e) {
    promptAction.showToast({ message: `${e.message} ${e.code}` });
    console.error(e.message, e.code);
  }
};

Button("新增3").onClick(this.fn3);

image-20250117011316637

需要注意的是:我们当前的角色是 World,此时是没有 新增、编辑、删除权限的。所以为了方便操作,可以修改调整权限

clouddb/objecttype/Book.json

    {
      "role": "World",
      "rights": [
        "Read",
        "Upsert",
        "Delete"
      ]
    },

当数据表信息发生了修改时,需要在 AGC 平台上删除之前的数据区+数据表。然后重新部署。

upsert - 编辑

这里我们可以根据 id 来编辑一下数据。 数据库里面存放着id:10 的数据,我们就来修改它。

image-20250117201256407

fn4 = async () => {
  try {
    const book = new Book();
    //  固定修改id为10的数据
    book.id = 10;
    book.name = "更新book";
    book.price = 999;
    book.publish = new Date();
    book.hot = true;
    book.cover = "更新 xxxx";

    const res = await this.agcDataBase?.upsert(book); // 因为数据 id已经存在,所以此时是编辑
    AlertDialog.show({ message: `编辑成功${res}条` });
  } catch (e) {
    promptAction.showToast({ message: `${e.message} ${e.code}` });
    console.error(e.message, e.code);
  }
};

Button("更新4").onClick(this.fn4);

delete - 删除

执行删除 delete 方法时,也是需要传入一个或者多个删除的元素。

我们这里就可以根据 id:10 的元素执行删除。

fn5 = async () => {
  try {
    const book = new Book();
    //  固定修改id为10的数据
    book.id = 10;
    const res = await this.agcDataBase?.delete(book); // 因为数据 id已经存在,所以此时是编辑
    AlertDialog.show({ message: `删除成功${res}条` });
  } catch (e) {
    promptAction.showToast({ message: `${e.message} ${e.code}` });
    console.error(e.message, e.code);
  }
};

Button("删除5").onClick(this.fn5);

calculateQuery - 计算

calculateQuery 从数据库中查询符合条件的数据,并对指定字段进行算术计算。主要提供了以下的计算功能。

名称 说明
AVERAGE 0 计算平均数。
SUM 1 计算总和。
MAXIMUM 2 计算最大值。
MINIMUM 3 计算最小值。
COUNT 4 计算记录总数。

image-20250117203951021

fn6 = async () => {
  try {
    // 创建查询条件实例
    this.condition = new cloudDatabase.DatabaseQuery(Book);
    // 设置查询结果的最大数量为 10
    this.condition.limit(10);
    // 执行查询并获取结果
    const resultArray = await this.agcDataBase?.calculateQuery(
      this.condition,
      "price",
      cloudDatabase.QueryCalculate.SUM
    );
    // 显示查询结果
    AlertDialog.show({ message: JSON.stringify(resultArray, null, 2) });
  } catch (e) {
    promptAction.showToast({ message: `${e.message} ${e.code}` });
    console.error(e.message, e.code);
  }
};

Button("计算6 总价格").onClick(this.fn6);

总结

本章主要介绍了在 HarmonyOS Next 中如何通过客户端代码操作云数据库,主要包含以下几个要点:

  1. 操作数据库的四种方式,重点介绍了客户端代码操作方式
  2. 使用 DevEco Studio 自动生成客户端数据模型,简化开发流程
  3. 详细讲解了数据库的基本操作:
    • 初始化数据库连接(DatabaseZone)
    • 查询数据(query)
    • 新增/更新数据(upsert)
    • 删除数据(delete)
    • 数据计算(calculateQuery)
  4. 介绍了各种操作的参数说明和返回值,并提供了完整的示例代码

以上是对客户端操作数据库的基本功能演示。下一章会重点来讲解查询语法。condition


如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯,欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。

仓颉-环境搭建-Mac篇

作者 HarderCoder
2025年1月17日 10:44

下载地址

可以在 官网 下载正式版

也可以在 gitcode下载开发者测试版(需要申请测试)

image.png

选择对应架构的SDK

这里我以Mac为例。SDK包含Arm架构和Intel架构,目前SDK都是64位的,暂时不提供32位

如何区分是哪种架构,可以点击在关于本机里查看,带有Apple字样的是Arm架构,其他的是Intel架构。 其他Linux类似

image.png

image.png

配置环境

将下载好的SDK放入你常用的工作目录中,解压,然后进入到SDK目录,执行以下命令 source cangjie/envsetup.sh

部分标准库需要使用openssl 3.x的动态库,所以我们最好安装好

下载openssl openssl-library.org/source/ 或者将电脑中已有的动态库拷出来放入到如下路径

image.png

详细可参考Linux 版本工具链的支持与安装

下载VSCode及插件

VSCode地址

同样根据你电脑所属的架构,下载对应的IDE。只有这样适配性才是最好的

image.png

下载插件

官网 VSCode插件

测试版本VSCode插件

image.png

下载之后进行解压

配置插件

打开VSCode的插件Tab,选择从本地安装,然后选择vsix所在路径

image.png

image.png

配置SDK路径

打开VSCode扩展Tab,然后搜索Cangjie可以看到对应插件,然后点击设置,如图配置对应的选项。选择CJNative并将SDK Path设置为你存放仓颉SDK的路径就行

image.png

具体可参考 仓颉语言 IDE 插件使用指南

运行样例工程

按如图或使用 Cmd+Shift+P 调出命令视图,然后搜索create,选择 Create Cangejie Project,一步步创建出工程,最终的结构如图

image.png

然后我们可以选择点击右上角按钮运行或打开终端执行cjpm run

image.png

HarmonyOS Next鸿蒙开发:下载文件

2025年1月17日 07:48

我们做项目过程中难免会有下载文件的场景,那么下载文件会涉及到一个是文件保存到DocumentViewPicker选择器,还有文件流保存的知识。

选择目录组件

项目业务需求中,有选择文件的需求。 选择文件,系统提供了DocumentViewPicker.save()方法。

 //直接强转为文件流数据
  let dvp=new picker.DocumentViewPicker()
   //打开保存文件操作
  let result= await dvp.save({newFileNames:[Date.now()+'1.png']})

我这里是用到了axios的网络请求下载,当然也可以用官方的http请求方式进行网络请求,需要增加网络请求权限,axios安装也很简单。

网络请求

 安装axios
 ohpm i @ohos/axios 

网络申请权限,在resource目录下的module.json5文件下module节点下添加即可。

"requestPermissions":[
    {
       "name":"ohos.permission.INTERNET"
    }
 ]

文件流-保存文件

关于文件流的介绍请点击官方查看,这里要注意就是看是否更新,因鸿蒙发展很快,有很多时候,不注意看,就用旧的组件,比如官方不推荐@ohos.fileio,而推荐@ohos.file.fs

image.png

保存文件

let f = fs.openSync(path,fs.OpenMode.CREATE|fs.OpenMode.WRITE_ONLY)
fs.writeSync(f.fd,res.data)
fs.closeSync(f)//关闭文件

完整代码

import axios, { AxiosResponse } from '@ohos/axios';
import { fileIo as fs, picker } from '@kit.CoreFileKit';
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct DownloadPage {
  @State message: string = 'Hello World';

  @State isShowProgress: boolean = false;


  build() {
    Row() {
      Column() {

        Button("下载").onClick(async ()=>{
          console.log("1")
          let res:AxiosResponse=await axios(
           // "http://m804.music.126.net/20241117075918/8d6bdbcd1b021f293361ea41f50f9f17/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/9444693488/94b2/45cb/21d6/9e34cbd9902ee8a917e6d0781557b94e.mp3?authSecret=000001933753cb5d08d80a3b19341b18"
            "https://pic.rmb.bdstatic.com/bjh/c305e5a85f66835661f880b24b9876be9233.png"
          )
          //直接强转为文件流数据
          let dvp=new picker.DocumentViewPicker()
          //打开保存文件操作
          let result= await dvp.save({newFileNames:[Date.now()+'1.png']})

          let path=result[0]
          let f = fs.openSync(path,fs.OpenMode.CREATE|fs.OpenMode.WRITE_ONLY)
          fs.writeSync(f.fd,res.data)
          fs.closeSync(f)//关闭文件
          console.log("保存路径为::"+JSON.stringify(result))
        })
      }
      .width('100%')
    }
    .height('100%')
  }
}

image.png

选择文件保存路径:

image.png

打开文件夹-下载目录可以看到下载的图片了 image.png

直接在模拟器上预览图 image.png

《鸿蒙HarmonyOS 5.0开发教程》基础篇06:颜色值和像素单位

作者 没有猫饼
2025年1月16日 23:44

鸿蒙应用的页面开发,在处理组件样式时会涉及到很多的颜色取值和长度大小取值。

一、颜色取值

ArkTS 中提供了 4 种描述资源颜色的类型,用来设置文字颜色、背景颜色还是边框颜色等。

颜色类型 说明 示例
Color 枚举类型 ArkTS 内置的颜色枚举类型 Color.Orange
颜色英文单词 支持一些基础的颜色单词 "green"
16进制 支持HEX格式的颜色取值 "#000000"、0xFFFFFF
rgb格式 以rgb三原色方式设置颜色(还可以添加a设置透明度) rgb(0, 0, 0)、rgba(0, 0, 0, 0.5)

说明:以上 4 种颜色取值,适用于所有需要设置颜色样式的场景。

示例代码:

build() {
    Column() {
      Text("文字颜色")
        .fontColor("red")

      Row() {
        Column()
          .width(100).height(100)
          .backgroundColor('#eeeeee')

        Column()
          .width(100).height(100)
          .backgroundColor(0x999999)
      }

      Row() {
        Column()
          .width(100).height(100)
          .backgroundColor('rgb(125, 125, 1)')

        Column()
          .width(100).height(100)
          .backgroundColor('rgba(125, 125, 1, 0.5)')

        Column()
          .width(100).height(100)
          .borderWidth(1)
          .borderColor(Color.Orange)
      }
    }
  }

代码预览效果如下:

image-20250116234930437

二、像素单位

ArkTS 中也提供了 4 种像素单位(官方文档),用来设置组件的宽高大小、位置、文字的字体大小等样式。

像素单位 说明
px 屏幕物理像素单位。
vp 屏幕密度相关像素,根据屏幕像素密度转换为屏幕物理像素,当数值不带单位时,默认单位vp。
fp 字体像素,与vp类似适用屏幕密度变化,随系统字体大小设置变化。
lpx 视窗逻辑像素单位,lpx单位为实际屏幕宽度与逻辑宽度的比值。

说明:

  • 在鸿蒙应用的开发中,最常用的两个像素单位是 vp 和 fp;
  • vp 单位可用于所有需要设置像素大小的样式,例如组件宽高大小、位置移动,以及文字大小;
  • fp 单位专门用于设置文字字体大小,和 vp 不同的是,通过 fp 设置的文字大小,会随着设备系统字体大小一起缩放;

示例代码:

build() {
  Column() {
    Column() {
      Text('以vp作为单位的文字')
        .fontSize(20)
      Text('以fp作为单位的文字')
        .fontSize('20fp')
    }
    .width('200vp').height(100).border({ width: 1 })
  }
  .height('100%').width('100%')
}

说明:除了官方提到的 4 种像素单位以外,% 这些常规的单位在 ArkTS 中都可以正常使用。

代码预览效果如下:

image-20250116235222854

HarmonyOS Next 端云一体化(2)

作者 万少
2025年1月16日 17:22

HarmonyOS Next 端云一体化(2)

本章节主要讲一下端云一体化中的数据库操作

介绍

云数据库是端云协同的数据库产品,具备端云数据协同管理、统一数据模型及丰富数据管理 API 接口等能力。它采用基于对象模型

数据存储结构,数据以对象(Object) 形式存于不同存储区,每个对象为一条完整数据记录。 对象类型(ObjectType) 定义存储对

象集合,不同对象类型对应不同数据结构。存储区(Zone) 是独立的数据存储区域,每个存储区的对象类型定义完全相同。

学习流程

我们会按照以下流程来进行学习。

  1. 新建存储区:也就是新建数据库
  2. 创建对象类型:创建一个用于存储数据条目的对象类型。
  3. 添加数据条目:在刚刚创建的对象类型内添加一条条数据,并配置数据所在的存储区。
  4. 部署云数据库:数据成功添加后,您可以直接将该数据部署至 AGC 云端。您也可以等所有对象类型和数据条目开发完成后,再统一批量部署到 AGC 云端。

存储区

我们这里新建一个存储区 Study 用来存放稍后用到的数据。

image-20250116105318813

然后我们在 DevEco Studio 的 云端配置中指定存储区。

clouddb/db-config.json

{
"defaultCloudDBZoneName": "Study", // 存储区的名称
"defaultDataStorageLocation": "CN"
}

创建对象类型

什么是创建对象类型

创建对象类型可以理解为就是为我们的数据定义类型。类似于使用接口或者 class 来定义数据,但是这里操作的范围要更大。

  1. 定义基本的数据类型,如数字、字符串、布尔、日期时间等。
  2. 定义字段的权限,因为这个对象类型本身是数据表,定义权限相当于设置了哪一类用户拥有哪些权限。如读取、编辑、新增、删除等
  3. 定义索引,考虑数据量大需要比较方便的查找到要操作的数据,需要定义索引。

比如,我们需要根据书籍数据来定义类型。 Book

字段 类型 说明
id number id
name string 书名
price number 价格
publish date 出版日期
hot boolean 是否热门
cover string 封面

对象类型的规范

新建 Book 对象类型文件

image-20250116130255215

这个对文件需要按照以下格式来编写。其中的字段的一些规范需要特别注意。

{
  "objectTypeName": 对象实体名称
  "fields": 对象中的属性
  "indexes": 索引
  "permissions" 权限
}

objectTypeName

表示实体对象的名称,我们这里可以是 Book

{
  "objectTypeName": "Book"
}

fields

fields 表示这个对象中属性的一些规则。

参数 必选(M)/可选(O) 说明
fieldName M 字段名称。输入要求具体如下:字段的名称长度必须大于或等于 1 个字符,小于或等于 30 个字符,只能包含以下 3 种类型,并且至少包含“字母”类型:字母(A-Z 或 a-z)数字(0-9)特殊字符:_字段名称必须以字母开头,以字母或者数字结尾。字段名称中不区分字母的大小写。修改对象类型时,支持删除字段。字段名称不允许使用系统保留字段名称: naturalbase_version、naturalbase_deleted、naturalbase_operationtype、naturalbase_creator、naturalbase_accesstime、naturalbase_operationtime、naturalbase_syncstatus、naturalbase_changedfieldsbitmap、naturalbase_lastmodifier、cmin、cmax、xmin、xmax、ctid、oid、tableoid、xc_node_id、tablebucketid、rowid。说明当前 Cloud Foundation Kit 暂不支持自增类型字段 IntAutoIncrement 或 LongAutoIncrement。
fieldType M 字段的数据类型。当前支持的数据类型:String、Boolean、Byte、Short、Integer、Long、Float、Double、ByteArray、Text、Date、IntAutoIncrement(数字-自增)
belongPrimaryKey O 设置该字段是否为对象类型的主键,默认值为 false。至少设置一个字段为主键。支持设置复合主键,由多个字段组合成为主键,一个复合主键包含的字段小于等于 5 个,复合主键字段顺序与字段的顺序一致。数据类型为 ByteArray、Text、Date、Double、Float 和 Boolean 的字段不支持设置为主键。主键的值不允许更改。
notNull O 设置字段值是否为非空,默认值为 false。数据类型为 ByteArray 和 Date 的字段不支持设置为非空。主键默认非空,且不允许更改。设置为非空的字段不支持加密和敏感。
isNeedEncrypt O 设置字段是否需要加密,开启全程加密数据管理功能,默认值为 false。选择加密后,该字段对应的数据会加密存储在存储区中。主键字段不支持加密。加密的字段不支持设置为非空。加密的字段不支持设置为敏感字段。一个对象类型中包含的加密字段和敏感字段的总数需小于或等于 5 个。字段设置为加密后,不支持导出该字段的数据值。数据类型为 ByteArray、Text 的字段不支持加密。对象类型创建成功后,不支持修改加密属性。
isSensitive O 设置字段是否为敏感字段,默认值为 false。选择敏感后,该字段对应的数据会加密存储在存储区中。敏感字段不支持设置为主键。敏感字段不支持设置为非空。敏感字段不支持设置为加密。敏感字段不支持设置为默认值。对象类型创建成功后,不支持修改敏感属性。仅支持数据类型为 Byte、Short、Integer、Long、Float、Double、String 和 Date 的字段设置为敏感字段。敏感字段不支持设置为索引。一个对象类型中包含的加密字段和敏感字段的总数需小于或等于 5 个。
defaultValue O 字段为非空时,必须设置默认值。主键不支持设置默认值。加密字段和敏感字段不支持设置默认值。数据类型为 ByteArray、Date 不支持为其设置默认值。数据类型为 Text 的字段设置默认值时,默认值的长度小于或等于 200 个字符。

按照我们想要的书籍的规定。可以这样

fieldName fieldType belongPrimaryKey
id IntAutoIncrement true
name String -
price Double -
publish Date -
hot Boolean -
cover String -
{
  "objectTypeName": "Book",
  "fields": [
    {
      "fieldName": "id",
      "fieldType": "IntAutoIncrement",
      "belongPrimaryKey": true,
      "notNull": true
    },
    {
      "fieldName": "name",
      "fieldType": "String"
    },
    {
      "fieldName": "price",
      "fieldType": "Double"
    },
    {
      "fieldName": "publish",
      "fieldType": "Date"
    },
    {
      "fieldName": "hot",
      "fieldType": "Boolean"
    },
    {
      "fieldName": "cover",
      "fieldType": "String"
    }
  ]
}

indexes

indexes中为该对象类型配置索引、索引包含的字段、以及索引包含的字段的排序方式。

参数 必选(M)/可选(O) 说明
indexName M 索引名称。输入要求具体如下:索引的名称长度必须大于或等于 1 个字符,小于或等于 30 个字符,只能包含以下 3 种类型,并且至少包含“字母”类型:字母(A-Z 或 a-z)数字(0-9)特殊字符:_索引名称必须以字母开头。索引名称中不区分字母的大小写。修改对象类型时,仅支持新增或者删除索引。当删除索引后,本次提交前不允许新增同名索引。每个对象类型可以设置小于或等于 16 个索引。数据类型为 ByteArray 和 Text 的字段不支持设置为索引。
indexList > fieldName M 索引包含的字段。支持设置组合索引,由多个字段组合成为索引,一个组合索引包含的字段不超过 5 个。
indexList > sortType M 索引包含的字段的排序方式,支持 ASC 升序或 DESC 降序。

这里我们为书籍的 id 和书籍的名称提供索引。

{
  "indexes": [
    {
      "indexName": "id_Index",
      "indexList": [
        {
          "fieldName": "id",
          "sortType": "ASC"
        }
      ]
    },
    {
      "indexName": "price_Index",
      "indexList": [
        {
          "fieldName": "price",
          "sortType": "DESC"
        }
      ]
    }
  ]
}

permissions

permissions 字段用来设置该数据表的操作权限的。

目前提供的角色和权限有以下分类。

参数 必选(M)/可选(O) 说明
role M 用户角色,包括:World:代表所有用户,包含认证和非认证用户。该角色默认拥有 Read 权限,可自定义配置 Upsert 和 Delete 权限。但是,不建议将 Upsert 和 Delete 权限配置给所有人角色。当对象类型中设置了加密字段之后,表示开启全程加密功能,此时所有人角色将不会拥有 Read、Upsert 和 Delete 权限,且不允许修改。Authenticated:经过 AGC 登录认证的用户。该角色默认拥有 Read 权限,可自定义配置 Upsert 和 Delete 权限。当对象类型中设置了加密字段之后,表示开启全程加密功能,此时认证用户角色将不会拥有 Read、Upsert 和 Delete 权限,且不允许修改。Creator:经过认证的数据创建用户。该角色默认拥有所有权限,且可自定义配置所有权限。每条数据都有其对应的数据创建人(即应用用户),每个数据创建者仅可以 Upsert 或者 Delete 自己创建的数据,不能 Upsert 或者 Delete 他人创建的数据。数据创建者的信息保存在数据记录的系统表中。Administrator:应用开发者,主要是指通过 AGC 控制台或 FaaS(Function as a Service,函数即服务)侧访问云数据库的角色。该角色默认拥有所有权限,且可自定义配置所有权限。Administrator 可以管理并配置其他角色的权限。
rights M 授予角色的权限,包括 Read、Upsert(包含新增和修改)和 Delete 权限。

接下来,我们为 这些角色设置以下的权限。

角色 Read Upsert Delete
World
Authenticated
Creator
Administrator

代码如下:

{
  "permissions": [
    {
      "role": "World",
      "rights": ["Read"]
    },
    {
      "role": "Authenticated",
      "rights": ["Read", "Upsert"]
    },
    {
      "role": "Creator",
      "rights": ["Read", "Upsert", "Delete"]
    },
    {
      "role": "Administrator",
      "rights": ["Read", "Upsert", "Delete"]
    }
  ]
}

创建数据条目

我们搭建好了对象类型,现在可以根据它来插入数据了。

  1. 新建数据文件

    image-20250116160900005


    image-20250116160911750

  2. 插入数据 这里可以看到一些基本数据

    {
      "cloudDBZoneName": "Study",
      "objectTypeName": "Book",
      "objects": [
        {
          "id": 1,
          "name": "string1",
          "price": 10.5,
          "publish": 1737014949576,
          "hot": true,
          "cover": "string1"
        },
        {
          "id": 2,
          "name": "string2",
          "price": 20.5,
          "publish": 1737014949576,
          "hot": false,
          "cover": "string2"
        }
      ]
    }
    

    字段解释

    • cloudDBZoneName:配置存储区名称。
    • objects:配置当前对象类型中所有字段的值,即写入数据。一个对象(object)即为一条数据,您可以通过新建一个对象(object)来为字段赋新值,也可以修改某个对象(object)下字段的值(主键或加密字段的值不支持修改)

部署云数据库

刚才我们都是在本地搭建的数据库,现在我们需要将这些数据同步到 AGC 平台的数据库上。

部署的方式也很简单

  1. 部署

image-20250116161334518

  1. 成本

image-20250116170856484

  1. 刷新 AGC 平台上的数据库

    image-20250116170956502

总结

本文详细介绍了 HarmonyOS 云数据库的基本操作流程:

  1. 存储区(Zone)创建

    • 创建名为 Study 的存储区
    • 在 DevEco Studio 中配置存储区信息
  2. 对象类型(ObjectType)定义

    • 详细讲解了对象类型的规范和组成部分
    • 包含 objectTypeName、fields、indexes 和 permissions 四个主要部分
    • 以 Book 对象为例,展示了完整的对象类型定义过程
  3. 数据操作

    • 创建数据条目的具体步骤
    • 展示了如何在 JSON 文件中定义数据内容
    • 说明了 cloudDBZoneName 和 objects 的配置方法
  4. 云端部署

    • 介绍了如何将本地数据库部署到 AGC 平台
    • 展示了部署过程和成本预估
    • 说明了如何在 AGC 平台上刷新数据库

通过这些步骤,我们完成了一个基本的云数据库的创建、配置和部署过程,为后续的端云协同开发打下了基础。


如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯,欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。

HarmonyOSNext 端云一体化(1)

作者 万少
2025年1月16日 11:54

HarmonyOSNext 端云一体化(1)

Cloud Foundation Kit(云开发服务)可以按需为应用提供云函数、云数据库、云存储等云端服务。应用运行所需的服务器和环境可以皆由云端平台提供,开发者只需关注应用的业务逻辑,而无需关心基础设施(例如:服务器、操作系统、容器等)。

DevEco Studio 中还提供了端云一体化开发的开发体验,您可以基于统一的技术栈,高效、协同地完成端、云代码的编写、调试、编译和部署,极大提高构建 HarmonyOS 应用和元服务的效率。

应用场景

  • 应用后端 快速构建应用或者元服务的后端服务,从而大幅简化应用开发与运维相关的事务,快速完成应用的构建
  • 计算密集型任务 当应用中出现计算密集型任务时,可以在云端及时申请足够的算力来支撑任务的执行。当任务结束时,可以立即释放资源,避免浪费。****
  • 适配类应用 通过 Cloud Foundation Kit 实现协议类型的转换,比如实现 IoT 中不同设备的协议适配。以及接入第三方平台,通过第三方平台提供的接口,实现业务的接入或者协作。
  • 突发大量访问 传统架构服务在某些特殊场景下,可能出现大量的访问。为保证业务高峰时,系统能稳定运行,一般需要购买高性能、昂贵的服务器,组建集群负载均衡。但是,当业务回落时,就导致了大量服务器的资源浪费。

包含的资源

端云一体主要包含的资源有

  • 云函数
  • 云数据库
  • 云存储

适配的项目类型

端云一体可以用在开发鸿蒙的应用或者是元服务上。因为考虑应用的上架需要软著+备案,而元服务的上架只需要备案。所以后续的教程中为了更加方便学习,我们会在元服务的环境下进行。

学习流程

稍后我们会延续着以下步骤,让大家可以尽可能的掌握端云一体的开发流程。

image-20250116090935066

  1. AGC 平台新建元服务
  2. DevEco Studio 新建对应的项目工程
  3. 云函数的基本使用
  4. 云数据库的基本使用
  5. 云存储的基本使用

AGC 平台新建元服务

在 AGC 平台上新建服务的具体步骤可以参考这个文章 HarmonyOS Next 最新 元服务新建到上架全流程,这里就不再叙述了。

DevEco Studio 新建对应的项目工程

本地在使用 DevEco Studio 新建工程时,需要新建的是端云一体化的元服务项目。

image-20250116091157505


此时,你的项目中会看到额外的云端相关的目录结构。

image-20250116091237635

特别需要注意的是,目前模拟器是不支持运行端云一体的项目的。所以为了方便测试,最好是具体真机

运行到真机

image-20250116095501501

  • 测试下云函数

image-20250116095523881

  • 测试下云数据库

    还没有在云端配置云数据库,所以此时的测试是没有反应的。

    image-20250116095646627

  • 测试下云存储

    image-20250116095730657

云端环境一览

我们需要知道的是,在本地操作云端的资源时,本质上都是对云端的资源的一些操作。最终的操作成功与否,都会反映到真正的云端环境。

我们登录 AGC 平台,然后找到对应的项目。这里是元服务的名称,不是项目的名称,你的元服务是归属于某个项目的

image-20250116100212297

云函数一览

云函数其实就是存放后端逻辑的部分。也是我们后期编写后端业务主要区域。

image-20250116100026310

云数据库一览

image-20250116100343797

这里的字段解释如下

字段 说明
对象类型 表示你的数据中用到的实体的类型,比如用户、购物车都是不同的实体类型,也可以理解为你的数据表格
存储区 理解为数据库的名称,你可以在这里管理多个存储区-数据库
数据 你存储的实际的数据
  • 对象类型

    image-20250116100642788

  • 存储区

    image-20250116100701709

  • 数据

    image-20250116100720676

云存储一览

云存储就是存放你物理文件的地方,比如图片、视频、音频等等。

image-20250116100749650

DevEco Studio 中的云端资源

小伙伴们应该还记得,我们在新建端云一体云服务的时候,本地工程中也是多了一个文件夹的。 CloudProgram

image-20250116100943842

它里面的主要目录结构如下。

  1. clouddb 存放数据库相关的文件
  2. cloudfunctions 存放云函数相关的文件
  3. cloud-config.json 存放云端环境相关的配置

后期我们开发的思路可以是以下两种方式:

  1. 全部都在 AGC 平台上操作,如编写云函数逻辑代码、搭建数据库、数据表
  2. 尽量都在本地 DevEco Studio 工具中编写云函数逻辑代码、调试代码、搭建数据库、数据集表

以上两种方式都可以。但是为了刚好的开发体验,我们是建议使用方式 2.

总结

本文介绍了 HarmonyOS Next 中端云一体化开发的基础概念和环境搭建。通过 Cloud Foundation Kit,开发者可以更便捷地使用云函数、云数据库和云存储等云端服务,从而专注于业务逻辑的开发,而无需过多关注基础设施的搭建和维护。

主要内容包括:

  • 端云一体化的应用场景和主要资源
  • AGC 平台上创建元服务的流程
  • DevEco Studio 中创建端云一体化项目的步骤
  • 云端环境(云函数、云数据库、云存储)的基本介绍
  • 本地开发环境的目录结构和开发方式建议

通过本文的学习,读者可以对 HarmonyOS Next 的端云一体化开发有一个整体的认识,为后续深入学习打下基础。建议开发者优先选

择在 DevEco Studio 中进行开发,这样可以获得更好的开发体验。


如果你兴趣想要了解更多的鸿蒙应用开发细节和最新资讯,欢迎在评论区留言或者私信或者看我个人信息,可以加入技术交流群。

[转载] 告别构建错误, iOS 开发架构难题全面解析, 避免 CPU 架构陷阱

作者 wyanassert
2025年1月15日 16:15

原文地址

前言

如果你经常开发 iOS 中的第三方框架,那么你可能会遇到以下错误:

1
"Could not find module *** for target 'x86_64-apple-ios-simulator'."

或者:

1
"building for iOS Simulator, but linking in dylib built for iOS, file, '.../Frameworks/xxx.framework/xxx' for architecture arm64."

要解决这个问题,我们需要了解 CPU 架构和 Xcode 构建设置的一些知识,今天我们就来聊聊这个。

理解 CPU 架构

每个 CPU 都有一组可以执行的指令。这些指令主要分为两种类型:

CISC(复杂指令集计算)

  • 复杂且强大的指令。
  • 每条指令执行多个任务。
  • 例如:x86 处理器(由 Intel 和 AMD 使用)。

RISC(精简指令集计算)

  • 简单且快速的指令。
  • 每条指令执行一个任务。
  • 例如:ARM 处理器(由 Qualcomm、MediaTek 和苹果的 M1 芯片使用)。

什么是 32 位和 64 位

32 位:一次可以处理 32 位数据。
64 位:一次可以处理 64 位数据,允许更多的计算能力和内存使用。

常见架构

x86:Intel 和 AMD 使用的 32 位架构。

x86_64:x86 的 64 位版本,更强大,能处理更多数据。

ARM:Qualcomm 和 MediaTek 使用的 32 位架构。

ARM64:ARM 的 64 位版本,更强大,苹果的 M1 芯片使用。

主要的制造商

Intel 和 AMD:制造 x86 和 x86_64 处理器。

Qualcomm 和 MediaTek:制造 ARM 和 ARM64 处理器。

Apple:在 Apple Silicon 系列(M1、M1、M2、M3、M4 等)Mac 中使用
ARM64 处理器。

向 M 系列过渡与 Rosetta 的作用

M 系列处理器的引入,始于 M1,标志着苹果及其生态系统的重大转变。由于 M 系列基于 ARM,现有为 x86 构建的软件无法在这些新芯片上原生运行。为弥补这一差距,苹果推出了 Rosetta,它是一种兼容层,主要作用是允许 x86 软件在 M 系列处理器上运行。

M1 MacBooks 推出后,Xcode 最初就是使用 Rosetta 支持 x86 应用程序。

虽然这让开发者可以继续无缝工作,但 Rosetta 只是 Apple Silicon 过渡期的临时解决方案。随着 Xcode 12 的推出,苹果使 Xcode 能够在 ARM 上原生运行,全力支持 M 系列 MacBooks,而不再依赖于 Rosetta。

iOS 14 之前的模拟器仅限于 x86,并通过 Rosetta 在 M 系列 Mac 上运行。自 iOS 14 起,模拟器更新支持 ARM 和 x86,这意味着虽然模拟器可以在 M 系列 Mac 上原生运行,但未为 ARM 优化的应用程序仍会通过 Rosetta 运行。这种双重架构支持确保了过渡期间的兼容性和性能。

要检查你的应用程序正在使用哪种架构,你可以使用活动监视器。在活动监视器中,有一个名为”Kind”的列,显示应用程序是运行在 Intel(x86)还是 Apple(ARM)架构下。这一功能仅在 M 系列 Mac 上可用。

descript

Apple 物理设备架构

  • arm64:也称为 AArch64,现代 64 位 iOS 设备(iPhone 5S及更新机型),包括 A7、A8、A9、A10 和 A11 芯片的设备。
  • arm64e:较新的 64 位 iOS 设备,带有 A12仿生芯片及更新版本(例如,iPhone XS、XR、11、12、13 等)。
  • armv7:较旧的 32 位 iOS 设备(iPhone 3GS、4、4S)。
  • armv7s:略新的 32 位设备(iPhone 5、5C)。

Apple 模拟器架构

  • x86_64:基于 Intel 的 Mac 上的模拟器。
  • i386:用于较旧 iOS 版本的 32 位模拟器(主要是遗留支持)。
  • arm64:Apple Silicon(M1、M2)Mac 上的模拟器。

在了解了 CPU 架构基础知识、列出设备和模拟器架构,并讨论了 M
系列的历史和 Rosetta
的作用后,为解决之前提到的错误,我们需要知道如何找出我们集成的框架所支持的架构。

此外,我们需要调整构建设置,如 EXCLUDED_ARCHS 和
ONLY_ACTIVE_ARCH。接下来聊聊这些。

确定支持的架构

要确定 .xcframework 支持的架构,我们可以在终端中使用 lipo -info 命令。通过检查 .xcframework 中的目录,我们可以识别出支持哪些架构。以下是使用 lipo -info ~/Downloads/Bugly.framework/Bugly 查看 Bugly 2.6.0 版本的示例:

descript

根据命令输出可以看出 Bugly 2.6.0 版本支持 armv7、i386、x86_64 和 arm64 架构。

理解 EXCLUDED_ARCHS 和 ONLY_ACTIVE_ARCH

EXCLUDED_ARCHS

  • 定义:Xcode 中的一个构建设置,用于指定在构建目标时要排除的架构。
  • 用途:排除某些架构以避免兼容性问题或不必要的构建。
  • 示例:EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64 在为 iOS 模拟器构建时排除 arm64 架构,以确保兼容基于 Intel 的 Mac 上的 x86_64 模拟器。

ONLY_ACTIVE_ARCH

  • 定义:一个构建设置,决定 Xcode 是仅构建活动架构还是所有指定架构。
  • 用途:通过仅构建活动架构来加快开发过程中的构建速度。
  • 示例:ONLY_ACTIVE_ARCH = YES 仅构建活动架构,在开发过程中减少构建时间。通常在 Debug 配置中设置为 YES,在 Release 配置中设置为 NO,以确保最终构建支持所有所需架构。

说白了一个是黑名单,一个是白名单。

之前提到的错误

错误消息”Could not find module *** for target ‘x86_64-apple-ios-simulator’.”通常表示我们尝试使用的框架或模块不可用于我们目标的架构,它需要原生运行而不是使用 Rosetta,但我们试图导入仅为 x86_64 构建的框架或可测试应用:

descript

解决方法

我们可以实施一种变通方法来让测试目标运行,但这取决于使用的机器:

Apple Silicon M 系列:在 EXCLUDED_ARCHS 中排除 arm64。

descript

基于 Intel 的:在 Debug 模式下使用”仅构建活动架构”,这将允许项目成功运行。

descript

更好的解决方案

如果你可以控制框架的构建,建议打包的时候支持缺失的架构,特别是 arm64 模拟架构。

在分发的时候,优先使用 ios-arm64_i386_x86_64-simulator 或 ios-arm64_x86_64-simulator,更旧的模拟器一般就不用支持了。

如果你无法控制框架的构建,也无法联系开发者,你需要使用 EXCLUDED_ARCHS 排除缺失的架构。然而,这种方法可能限制你只能在物理设备上运行,特别是对于 Apple M 系列。此外,它要求你在使用此框架的所有依赖项中排除缺失的架构。例如,如果你使用的核心框架依赖于缺少架构的库,则核心框架和导入核心的项目都必须排除相同的架构。因此,在解决此类错误时要谨慎和耐心。

❌
❌