HarmonyOS鸿蒙Next开发者学习笔记

HarmonyOS鸿蒙Next开发者学习笔记

Ability

Ability

概述

Ability可分为 FA(Feature Ability)和 PA(Particle Ability)。

FA(Feature Ability)

  • 支持 Page Ability( 用户交互 )

PA(Particle Ability)

  • 支持 Service Ability(后台运行任务 )
  • 支持 Data Ability(对外部提供统一的数据访问 )

Page Ability

概念

  • setMainRoute() 指定默认展示的AbilitySlice。
  • addActionRoute() 更改默认展示的AbilitySlice。
public class MyAbility extends Ability {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        setMainRoute(MainSlice.class.getName());
        addActionRoute("action.pay", PaySlice.class.getName());
        addActionRoute("action.scan", ScanSlice.class.getName());
    }
}

addActionRoute()方法中使用的动作命名,需要在应用配置文件(config.json)中注册:

{
  "module": {
    "abilities": [
      {
        "skills": [
          {
            "actions": [
              "action.pay",
              "action.scan"
            ]
          }
        ]
      }
    ]
  }
}

生命周期

Page生命周期回调
  • onStart():首次创建Page实例时触发,进入INACTIVE状态。
  • onActive():Page进入前台时调用,进入ACTIVE状态。
  • onInactive():Page失去焦点时调用,进入INACTIVE状态。
  • onBackground():Page不再对用户可见时调用,进入BACKGROUND状态。
  • onForeground():Page重新回到前台时调用。
  • onStop():系统将要销毁Page时调用。
@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    setMainRoute(FooSlice.class.getName());
}
@Override
public void onActive() {
    super.onActive();
    // 获取在onInactive()中被释放的资源。
}

@Override
public void onInactive() {
    super.onInactive();
    // 实现Page失去焦点时应表现的恰当行为。
}

@Override
public void onBackground() {
    super.onBackground();
    // 释放Page不可见时无用的资源。
}

@Override
public void onForeground() {
    super.onForeground();
    // 重新申请在onBackground()中释放的资源。
}

@Override
public void onStop() {
    super.onStop();
    // 通知用户进行系统资源的释放。
}

AbilitySlice生命周期

AbilitySlice和Page具有相同的生命周期状态和同名的回调。

必须重写AbilitySlice的onStart()回调,并在此方法中通过setUIContent()方法设置页面:

@Override
protected void onStart(Intent intent) {
    super.onStart(intent);
    setUIContent(ResourceTable.Layout_main_layout);
}

Page与AbilitySlice生命周期关联

当AbilitySlice处于前台且具有焦点时,其生命周期状态随着所属Page的生命周期状态的变化而变化。

AbilitySlice间导航

同一Page内导航

通过present()方法实现导航。

@Override
protected void onStart(Intent intent) {
    ...
    Button button = ...;
    button.setClickedListener(listener -> present(new TargetSlice(), new Intent()));
    ...
}

从导航目标AbilitySlice返回时,能够获得其返回结果时,使用presentForResult()实现导航。

@Override
protected void onStart(Intent intent) {
    ...
    Button button = ...;
    button.setClickedListener(listener -> presentForResult(new TargetSlice(), new Intent(), 0));
    ...
}

系统将回调onResult()来接收和处理返回结果,需要重写该方法。

@Override
protected void onResult(int requestCode, Intent resultIntent) {
    if (requestCode == 0) {
        // Process resultIntent here.
    }
}

不同Page间导航

AbilitySlice相互不可见,无法通过present()presentForResult()方法直接导航到其他Page的AbilitySlice。

可以通过配置Intent的Action导航到目标AbilitySlice。

Page间的导航可以使用startAbility()startAbilityForResult()方法,获得返回结果的回调为onAbilityResult()

跨设备迁移

实现IAbilityContinuation接口

  • onStartContinuation():Page请求迁移后,系统首先回调此方法,在此回调中决策当前是否可以执行迁移。
  • onSaveData():如果onStartContinuation()返回true,则系统回调此方法,在此回调中保存必须传递到另外设备上以便恢复Page状态的数据。
  • onRestoreData():源侧设备上Page完成保存数据后,系统在目标侧设备上回调此方法,在此回调中接受用于恢复Page状态的数据。
  • onCompleteContinuation():目标侧设备上恢复数据一旦完成,系统就会在源侧设备上回调Page的此方法,可以在此检查迁移结果是否成功,并在此处理迁移结束的动作。
  • onRemoteTerminated():如果开发者使用continueAbilityReversibly()而不是continueAbility(),则此后可以在源侧设备上使用reverseContinueAbility()进行回迁。

请求迁移

try {
    continueAbility();
} catch (IllegalStateException e) {
    // Maybe another continuation in progress.
    ...
}
try {
    continueAbilityReversibly();
} catch (IllegalStateException e) {
    // Maybe another continuation in progress.
    ...
}

请求回迁

Service Ability

概念

用于后台运行任务,不提供用户交互界面,在后台运行。

是单例的,须在Service里创建新的线程来处理,防止造成主线程阻塞,应用程序无响应。

创建

  1. 创建Ability的子类,实现Service相关的生命周期方法。
public class ServiceAbility extends Ability {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
    }

    @Override
    public void onCommand(Intent intent, boolean restart, int startId) {
        super.onCommand(intent, restart, startId);
    }

    @Override
    public IRemoteObject onConnect(Intent intent) {
        super.onConnect(intent);
        return null;
    }

    @Override
    public void onDisconnect(Intent intent) {
        super.onDisconnect(intent);
    }

    @Override
    public void onStop() {
        super.onStop();
    }
}
  1. 注册Service
{
  "module": {
    "abilities": [
      {
        "name": ".ServiceAbility",
        "type": "service",
        "visible": true
      }
    ]
  }
}

启动

通过startAbility()方法来启动Service。

Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
    .withDeviceId("")
    .withBundleName("com.huawei.hiworld.himusic")
    .withAbilityName("com.huawei.hiworld.himusic.entry.ServiceAbility")
    .build();
intent.setOperation(operation);
startAbility(intent);

连接

通过connectAbility()方法与其进行连接。

private IAbilityConnection connection = new IAbilityConnection() {
    @Override
    public void onAbilityConnectDone(ElementName elementName, IRemoteObject iRemoteObject, int resultCode) {
        // 在这里开发者可以拿到服务端传过来IRemoteObject对象,从中解析出服务端传过来的信息
    }

    @Override
    public void onAbilityDisconnectDone(ElementName elementName, int resultCode) {
    }
};
connectAbility(intent, connection);

声明周期

启动Service

该Service在其他Ability调用startAbility()时创建,然后保持运行。

其他Ability通过调用stopAbility()来停止Service,Service停止后,系统会将其销毁。

连接Service

该Service在其他Ability调用connectAbility()时创建,客户端可通过调用disconnectAbility()断开连接。

多个客户端可以绑定到相同Service,而且当所有绑定全部取消后,系统即会销毁该Service。

前台Service

只需在Service创建的方法里,调用keepBackgroundRunning()将Service与通知绑定。

// 创建通知,其中1005为notificationId
NotificationRequest request = new NotificationRequest(1005);
NotificationRequest.NotificationNormalContent content = new NotificationRequest.NotificationNormalContent();
content.setTitle("title").setText("text");
NotificationRequest.NotificationContent notificationContent = new NotificationRequest.NotificationContent(content);
request.setContent(notificationContent);

// 绑定通知,1005为创建通知时传入的notificationId
keepBackgroundRunning(1005, request);

在配置文件中配置如下:

{
  "name": ".ServiceAbility",
  "type": "service",
  "visible": true,
  "backgroundModes": ["dataTransfer", "location"]
}

Data Ability

概念

管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据。

概念

  • URI介绍:
Scheme://[authority]/[path][?query][#fragment]
  • scheme:协议方案名,固定为"dataability",代表Data Ability所使用的协议类型。
  • authority:设备ID,如果为跨设备场景,则为目的设备的IP地址;如果为本地设备场景,则不需要填写。
  • path:资源的路径信息,代表特定资源的位置信息。
  • query:查询参数。
  • fragment:可以用于指示要访问的子资源。

例:

  • 跨设备场景:dataability://device_id/com.huawei.dataability.persondata/person/10
  • 本地设备:dataability:///com.huawei.dataability.persondata/person/10

访问

可通过DataAbilityHelper类来访问当前应用或其他应用提供的共享数据。

声明使用权限

{
  "reqPermissions": [
    {
      "name": "com.example.myapplication5.DataAbility.DATA"
    }
  ]
}

创建DataAbilityHelper

DataAbilityHelper helper = DataAbilityHelper.creator(this);

访问Data Ability

访问文件

FileDescriptor openFile(Uri uri, String mode)方法来操作文件。

FileDescriptor fd = helper.openFile(uri, "r");
FileInputStream fis = new FileInputStream(fd);
访问数据库

增、删、改、查以及批量处理等方法来操作数据库。

ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates)
int insert(Uri uri, ValuesBucket value)
int batchInsert(Uri uri, ValuesBucket[] values)
int delete(Uri uri, DataAbilityPredicates predicates)
int update(Uri uri, ValuesBucket value, DataAbilityPredicates predicates)
DataAbilityResult[] executeBatch(ArrayList<DataAbilityOperation> operations)
DataAbilityHelper helper = DataAbilityHelper.creator(this);
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
ResultSet resultSet = helper.query(uri, columns, predicates);
resultSet.goToFirstRow();
do {
    // 在此处理ResultSet中的记录;
}while(resultSet.goToNextRow());
DataAbilityHelper helper = DataAbilityHelper.creator(this);
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.insert(uri, valuesBucket);
DataAbilityHelper helper = DataAbilityHelper.creator(this);
ValuesBucket[] values = new ValuesBucket[2];
values[0] = new ValuesBucket();
values[0].putString("name", "Tom");
values[0].putInteger("age", 12);
values[1] = new ValuesBucket();
values[1].putString("name", "Tom1");
values[1].putInteger("age", 16);
helper.batchInsert(uri, values);
DataAbilityHelper helper = DataAbilityHelper.creator(this);
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.between("userId", 101, 103);
helper.delete(uri, predicates);
DataAbilityHelper helper = DataAbilityHelper.creator(this);
DataAbilityPredicates predicates = new DataAbilityPredicates();
predicates.equalTo("userId", 102);
ValuesBucket valuesBucket = new ValuesBucket();
valuesBucket.putString("name", "Tom");
valuesBucket.putInteger("age", 12);
helper.update(uri, valuesBucket, predicates);
DataAbilityHelper helper = DataAbilityHelper.creator(this);
ValuesBucket value1 = initSingleValue();
DataAbilityOperation opt1 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value1).build();
ValuesBucket value2 = initSingleValue2();
DataAbilityOperation opt2 = DataAbilityOperation.newInsertBuilder(insertUri).withValuesBucket(value2).build();
ArrayList<DataAbilityOperation> operations = new ArrayList<>();
operations.add(opt1);
operations.add(opt2);
DataAbilityResult[] result = helper.executeBatch(insertUri, operations);

创建

需要为应用添加一个或多个Ability的子类,来提供程序与其他应用之间的接口。

需要首先确定好使用何种类型的数据。

确定数据存储方式
  • 文件数据:如文本、图片、音乐等。
  • 结构化数据:如数据库等。
实现UserDataAbility
文件存储

重写FileDescriptor openFile(Uri uri, String mode)方法来操作文件。

MessageParcel messageParcel = MessageParcel.obtain();
File file = new File(uri.getDecodedPathList().get(1));
if (mode == null || !"rw".equals(mode)) {
    file.setReadOnly();
}
FileInputStream fileIs = new FileInputStream(file);
FileDescriptor fd = fileIs.getFD();
return messageParcel.dupFileDescriptor(fd);
数据库存储

初始化数据库连接。

private static final String DATABASE_NAME = "UserDataAbility.db";
private static final String DATABASE_NAME_ALIAS = "UserDataAbility";
private OrmContext ormContext = null;

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    DatabaseHelper manager = new DatabaseHelper(this);
    ormContext = manager.getOrmContext(DATABASE_NAME_ALIAS, DATABASE_NAME, BookStore.class);
}

编写数据库操作方法。

public ResultSet query(Uri uri, String[] columns, DataAbilityPredicates predicates) {
    if (ormContext == null) {
        HiLog.error(this.getClass().getSimpleName(), "failed to query, ormContext is null");
        return null;
    }
    OrmPredicates ormPredicates = DataAbilityUtils.createOrmPredicates(predicates, User.class);
    ResultSet resultSet = ormContext.query(ormPredicates, columns);
    if (resultSet == null) {
        HiLog.info(getClass(), "resultSet is null");
    }
    return resultSet;
}

public int insert(Uri uri, ValuesBucket value) {
    if (ormContext == null) {
        HiLog.error(getClass().getSimpleName(), "failed to insert, ormContext is null");
        return -1;
    }
    String path = uri.getPath();
    if (buildPathMatcher().getPathId(path) != PathId) {
        HiLog.info(getClass(), "UserDataAbility insert path is not matched");
        return -1;
    }
    User user = new User();
    user.setUserId(value.getInteger("userId"));
    user.setFirstName(value.getString("firstName"));
    user.setLastName(value.getString("lastName"));
    user.setAge(value.getInteger("age"));
    user.setBalance(value.getDouble("balance"));
    boolean isSuccessed = true;
    try {
        isSuccessed = ormContext.insert(user);
    } catch (DataAbilityRemoteException e) {
        HiLog.error(TAG, "insert fail: " + e.getMessage());
        throw new RuntimeException(e);
    }
    if (!isSuccessed) {
        HiLog.error(getClass().getSimpleName(), "failed to insert");
        return -1;
    }
    isSuccessed = ormContext.flush();
    if (!isSuccessed) {
        HiLog.error(getClass().getSimpleName(), "failed to insert flush");
        return -1;
    }
    DataAbilityHelper.creator(this, uri).notifyChange(uri);
    int id = Math.toIntExact(user.getRowId());
    return id;
}

注册UserDataAbility

{
  "name": ".UserDataAbility",
  "type": "data",
  "visible": true,
  "uri": "dataability://com.example.myapplication5.DataAbilityTest",
  "permissions": [
    "com.example.myapplication5.DataAbility.DATA"
  ]
}

Intent

基本概念

Intent是对象之间传递信息的载体。

Intent的构成元素包括Operation与Parameters。

  • Operation
    • Action:表示动作,通常使用系统预置Action,应用也可以自定义Action。
    • Entity:表示类别,通常使用系统预置Entity,应用也可以自定义Entity。
    • Uri:表示Uri描述。
    • Flags:表示处理Intent的方式。
    • BundleName:表示包描述。
    • AbilityName:表示待启动的Ability名称。
    • DeviceId:表示运行指定Ability的设备ID。
  • Parameters:一种支持自定义的数据结构,开发者可以通过Parameters传递某些请求所需的额外信息。

当Intent用于发起请求时,根据指定元素的不同,分为两种类型:

  • 如果同时指定了BundleName与AbilityName,则根据Ability的全称(例如,“com.demoapp.FooAbility”)来直接启动应用。
  • 如果未同时指定BundleName和AbilityName,则根据Operation中的其他属性来启动应用。

根据Ability的全称启动应用

通过构造包含BundleName与AbilityName的Operation对象,可以启动一个Ability、并导航到该Ability。

Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
    .withDeviceId("")
    .withBundleName("com.demoapp")
    .withAbilityName("com.demoapp.FooAbility")
    .build();
intent.setOperation(operation);
startAbility(intent);

根据Operation的其他属性启动应用

请求方

在Ability中构造Intent以及包含Action的Operation对象,并调用startAbilityForResult()方法发起请求。然后重写onAbilityResult()回调方法,对请求结果进行处理。

private void queryWeather() {
    Intent intent = new Intent();
    Operation operation = new Intent.OperationBuilder()
        .withAction(Intent.ACTION_QUERY_WEATHER)
        .build();
    intent.setOperation(operation);
    startAbilityForResult(intent, REQ_CODE_QUERY_WEATHER);
}

@Override
protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
    switch (requestCode) {
        case REQ_CODE_QUERY_WEATHER:
            // Do something with result.
            ...
            return;
        default:
            ...
    }
}
处理方
  • 作为处理请求的对象,首先需要在配置文件中声明对外提供的能力,以便系统据此找到自身并作为候选的请求处理者。
{
  "module": {
    "abilities": [
      {
        "skills": [
          {
            "actions": [
              "ability.intent.QUERY_WEATHER"
            ]
          }
        ]
      }
    ]
  }
}
  • 在Ability中配置路由以便支持以此action导航到对应的AbilitySlice。
@Override
protected void onStart(Intent intent) {
    ...
    addActionRoute(Intent.ACTION_QUERY_WEATHER, DemoSlice.class.getName());
    ...
}
  • 在Ability中处理请求,并调用setResult()方法暂存返回结果。
@Override
protected void onActive() {
    ...
    Intent resultIntent = new Intent();
    setResult(0, resultIntent);
    ...
}

更多关于HarmonyOS鸿蒙Next开发者学习笔记的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

这是在做什么?搬运官方文档?

更多关于HarmonyOS鸿蒙Next开发者学习笔记的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


HarmonyOS鸿蒙Next开发者学习笔记主要涉及以下内容:

系统架构

鸿蒙Next采用分布式架构,支持多设备协同,包括手机、平板、智能穿戴等。其核心是分布式软总线、分布式数据管理和分布式任务调度。

开发框架

鸿蒙Next提供ArkUI框架,支持声明式UI开发,简化界面设计。ArkTS是推荐的开发语言,基于TypeScript,增强类型安全和开发效率。

分布式能力

鸿蒙Next强调设备间的无缝连接,支持跨设备调用服务、共享数据和协同任务。开发者可以利用分布式API实现多设备间的交互。

安全机制

鸿蒙Next内置多层安全防护,包括应用沙箱、数据加密和权限管理,确保用户数据和应用的安全性。

开发工具

DevEco Studio是官方推荐的集成开发环境,支持代码编写、调试和模拟器测试,提供丰富的模板和插件,提升开发效率。

生态支持

鸿蒙Next积极构建开发者生态,提供丰富的API和文档,支持第三方应用快速接入,推动系统生态的扩展和完善。

这些内容涵盖了鸿蒙Next的核心技术和开发要点,帮助开发者快速上手并构建高效、安全的分布式应用。

HarmonyOS Next是华为推出的新一代操作系统,旨在构建全场景智能生态。开发者学习笔记应涵盖以下关键点:

  1. 系统架构:理解分布式架构,支持多设备协同。
  2. 开发工具:熟悉DevEco Studio,掌握IDE的使用。
  3. 应用开发:学习ArkUI框架,掌握声明式UI开发。
  4. 分布式能力:掌握设备间数据共享与任务协同。
  5. 安全机制:了解系统级安全与隐私保护。
  6. 性能优化:学习如何优化应用性能,提升用户体验。

通过系统学习与实践,开发者可以高效构建跨设备应用,提升开发效率与用户体验。

回到顶部