开发指导

About 23 min

开发指导

场景介绍

  • 带界面的Ability的应用,比如:新闻类的应用、视频类的应用、导航类的应用、支付类的应用等等,目前我们看到的大部分应用都是带有界面的用于人机交互的应用。

  • 不带界面的Ability应用,比如:音乐播放器能在后台播放音乐、后台提供计算服务、导航服务的各类应用等。

  • 不管是带界面的Ability应用还是不带界面的Ability应用,都要打包成Hap包,最终发布到应用市场,用户通过应用市场下载安装相应的应用。

接口说明

表 1 Ability子系统的对外接口

接口名称

接口描述

Want *WantParseUri(const char *uri)

反序列化接口,由字符串生成Want对象。

const char *WantToUri(Want want)

序列化,把Want对象生成字符串。

void SetWantElement(Want *want, ElementName element);

设置ElementName对象。

void SetWantData(Want *want, const void *data, uint16_t dataLength)

设置数据。

bool SetWantSvcIdentity(Want *want, SvcIdentity sid)

设置SvcIdentity。

void ClearWant(Want *want)

清除Want的内部内存数据。

void SetMainRoute(const std::string &entry)

设置AbilitySlice主路由。

void SetUIContent(RootView *rootView)

设置布局资源。

void OnStart(const Want& intent)

Ability生命周期状态回调,Ability启动时被回调。

void OnStop()

Ability生命周期状态回调,Ability销毁时被回调。

void OnActive(const Want& intent)

Ability生命周期状态回调,Ability显示时被回调。

void OnInactive()

Ability生命周期状态回调,Ability隐藏时被回调。

void OnBackground()

Ability生命周期状态回调,Ability退到后台时被回调。

const SvcIdentity *OnConnect(const Want &want)

Service类型Ability第一次连接时被回调。

void OnDisconnect(const Want &want);

Service类型Ability断开连接被回调。

void MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply);

Service类型Ability接收消息处理。

void Dump(const std::string &extra)

dump Ability信息。

void Present(AbilitySlice *abilitySlice, const Want &want)

发起AbilitySlice跳转。

void Terminate()

退出当前AbilitySlice。

void SetUIContent(RootView *rootView)

设置当前AbilitySlice所在Ability的布局资源。

void OnStart(const Want& want)

AbilitySlice生命周期状态回调,AbilitySlice启动时被回调。

void OnStop()

AbilitySlice生命周期状态回调,AbilitySlice销毁时被回调。

void OnActive(const Want& want)

AbilitySlice生命周期状态回调,AbilitySlice显示时被回调。

void OnInactive()

AbilitySlice生命周期状态回调,AbilitySlice隐藏时被回调。

void OnBackground()

AbilitySlice生命周期状态回调,AbilitySlice退到后台时被回调。

int StartAbility(const Want &want)

启动Ability。

int StopAbility(const Want &want)

停止Service类型的Ability。

int TerminateAbility()

销毁当前的Ability。

int ConnectAbility(const Want &want, const IAbilityConnection &conn, void *data);

绑定Service类型的Ability。

int DisconnectAbility(const IAbilityConnection &conn)

解绑Service类型的Ability。

const char *GetBundleName()

获取当前ability的对应应用的包名。

const char *GetSrcPath()

获取当前ability的对应应用的安装路径。

const char *GetDataPath()

获取当前ability的对应应用的数据路径。

int StartAbility(const Want *want)

启动Ability,该接口可以不需要在基于Ability开发的应用中使用。

int ConnectAbility(const Want *want, const IAbilityConnection *conn, void *data);

绑定Service类型的Ability,该接口可以不需要在基于Ability开发的应用中使用。

int DisconnectAbility(const IAbilityConnection *conn);

解绑Service类型的Ability,该接口可以不需要在基于Ability开发的应用中使用。

int StopAbility(const Want *want)

停止Service类型的Ability,该接口可以不需要在基于Ability开发的应用中使用。

void (*OnAbilityConnectDone)(ElementName *elementName, SvcIdentity *serviceSid, int resultCode, void *data)

绑定Service Ability的回调。

void (*OnAbilityDisconnectDone)(ElementName *elementName, int resultCode, void data)

解绑Service Ability的回调。

void PostTask(const Task& task)

投递任务到异步线程进行处理。

void PostQuit()

退出当前线程的消息循环。

static AbilityEventHandler GetCurrentHandler()

获取当前线程的事件处理器。

void Run()

执行当前线程的消息循环。

#define REGISTER_AA(className)

注册开发者的Ability到框架中。

#define REGISTER_AS(className)

注册开发者的AbilitySlice到框架中。

开发步骤

创建Service类型的Ability

  1. 在my_service_ability.h中创建Ability的子类MyServiceAbility。

    class MyServiceAbility: public Ability {
    protected:
        void OnStart(const Want& want);
        const SvcIdentity *OnConnect(const Want &want) override;
        void MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply) override;
    };
    
    1
    2
    3
    4
    5
    6
  2. 调用REGISTER_AA宏将ServiceAbility注册到应用框架中,以便应用框架实例化开发者的MyServiceAbility。

    #include "my_service_ability.h"
    
    REGISTER_AA(ServiceAbility)
    
    void MyServiceAbility::OnStart(const Want& want)
    {
        printf("ServiceAbility::OnStart\n");
        Ability::OnStart(want);
    }
    
    const SvcIdentity *MyServiceAbility::OnConnect(const Want &want)
    {
        printf("ServiceAbility::OnConnect\n");
        return Ability::OnConnect(want);
    }
    
    void MyServiceAbility::MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply)
    {
        printf("ServiceAbility::MsgHandle, funcId is %u\n", funcId);
        int result = 0;
        if (funcId == 0) {
            result = IpcIoPopInt32(request) + IpcIoPopInt32(request);
        }
        // push data
        IpcIoPushInt32(reply, result);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
  3. 实现Service相关的生命周期方法。Service也是一种Ability,Ability为服务提供了以下生命周期方法,用户可以重写这些方法来添加自己的处理。用户在重写的方法里,需要调用父类对应的方法。

    • OnStart()

      该方法在创建Service的时候调用,用于做一些Service初始化且耗时较短的工作,在Service的整个生命周期只会调用一次。

      void MyServiceAbility::OnStart(const Want& want)
      {
          printf("ServiceAbility::OnStart\n");
          Ability::OnStart(want);
      }
      
      1
      2
      3
      4
      5
    • OnConnect​()

      在组件和服务连接时调用,该方法返回SvcIdentity,组件可以通过它,与服务交互。

      const SvcIdentity *MyServiceAbility::OnConnect(const Want &want)
      {
          printf("ServiceAbility::OnConnect\n");
          return Ability::OnConnect(want);
      }
      
      1
      2
      3
      4
      5
    • OnDisconnect​()

      在组件与绑定的Service断开连接时调用。

    • OnStop()

      在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。

  4. 重写消息处理方法。

    MsgHandle是Service用来处理客户端消息的方法。其中funcId是客户端传过来的消息类型,request是客户端传过来的序列化请求参数。如果用户在处理完成之后想要把结果传回去,需要把结果序列化后写入reply中。

    void ServiceAbility::MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply)
    {
        printf("ServiceAbility::MsgHandle, funcId is %d\n", funcId);
        int result = 0;
        if (funcId == PLUS) {
            result = IpcIoPopInt32(request) + IpcIoPopInt32(request);
        }
        // push data
        IpcIoPushInt32(reply, result);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  5. 注册Service。

    Service也需要在应用清单文件config.json中进行注册,注册类型type需要设置为service。

    "abilities": [{
        "name": "ServiceAbility",
        "icon": "res/drawable/phone.png",
        "label": "test app 2", 
        "launchType": "standard",
        "type": "service",
        "visible": true
    }
    ]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
  6. 启动Service。

    • Ability为用户提供了StartAbility()方法来启动另外一个Ability,因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。

      开发者可以通过Want的SetWantElement ()来设置目标服务信息。ElementName结构体的两个主要参数:第一个参数为包名称;第二个参数为目标Ability。

      {
          Want want = { nullptr };
          ElementName element = { nullptr };
          SetElementBundleName(&element, "com.company.appname");
          SetElementAbilityName(&element, "ServiceAbility");
          SetWantElement(&want, element);
          StartAbility(want);
          ClearElement(&element);
          ClearWant(&want);
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10

      StartAbility() 方法会立即执行,如果Service尚未运行,则系统首先会调用OnStart()。

    • 停止Service。

      Service一旦创建就会一直保持在后台运行,开发者可以通过调用StopAbility()来停止Service。

  7. 连接Service。

    • 如果Service需要与Page Ability或其他应用组件中的Service进行交互,则应创建用于连接的Service。Service支持其他Ability通过ConnectAbility()与其进行连接,ConnectAbility()需要传入目标Service的Want,以及IAbilityConnection的实例来处理回调。IAbilityConnection提供了两个方法供用户实现,OnAbilityConnectDone()用来处理连接的回调,OnAbilityDisconnectDone()用来处理断开连接的回调。

      {
          // ability创建IAbilityConnection对象和定义IAbilityConnection的两个方法实现
          IAbilityConnection abilityConnection = new IAbilityConnection();
          abilityConnection->OnAbilityConnectDone = OnAbilityConnectDone;
          abilityConnection->OnAbilityDisconnectDone = OnAbilityDisconnectDone;
       
          void OnAbilityConnectDone(ElementName *elementName, SvcIdentity *serviceSid, 
              int resultCode, void *data)
          {
              if (resultCode != 0) {
                  return;
              }
              // push data
              IpcIo request;
              char dataBuffer[IPC_IO_DATA_MAX];
              IpcIoInit(&request, dataBuffer, IPC_IO_DATA_MAX, 0);
              IpcIoPushInt32(&request, 10);
              IpcIoPushInt32(&request, 6);
       
              // send and getReply
              IpcIo reply;
              uintptr_t ptr = 0;
              if (Transact(nullptr, *serviceSid, 0, &request, &reply, 
                  LITEIPC_FLAG_DEFAULT, &ptr) != LITEIPC_OK) {
                  printf("transact error\n");
                  return;
              }
              int result = IpcIoPopInt32(&reply);
              printf("execute add method, result is %d\n", result);
              if (ptr != 0) {
                  FreeBuffer(nullptr, reinterpret_cast<void *>(ptr));
              }
          }
       
          void OnAbilityDisconnectDone(ElementName *elementName, 
              int resultCode, void *data)
          {
              printf("elementName is %s, %s\n", 
                  elementName->bundleName, elementName->abilityName);
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
    • 发起connect和disconnect。

      {
          // ability发起connect
          Want want = { nullptr };
          ElementName element = { nullptr };
          SetElementBundleName(&element, "com.company.appname");
          SetElementAbilityName(&element, "ServiceAbility");
          SetWantElement(&want, element);
          ConnectAbility(want, *abilityConnection, this);
       
          // ability发起disconnect
          DisconnectAbility(*abilityConnection);
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12

包管理接口使用指导

安装应用

安装接口只能给内置的系统应用使用。根据应用的安装路径,可以在安装应用时进行选择:

  • 将应用安装到系统默认的文件目录/storage/app/。
  • 将应用安装到系统外挂的存储介质中,例如micro sdcard。

这两种选择可以在创建InstallParam实例的时候指定,当InstallParam的成员变量installLocation为 INSTALL_LOCATION_INTERNAL_ONLY时,意味着应用将会被安装到/storage/app/目录下;当InstallParam的成员变量installLocation为INSTALL_LOCATION_PREFER_EXTERNAL时,意味着应用将被安装到存储介质,其安装目录是/sdcard/app/。由于安装应用的过程是异步的,所以需要使用类似信号量的机制来确保安装的回调可以被执行。

安装应用的步骤如下(示例代码以安装到系统目录为例):

  1. 将经过安全签名的应用放置于指定的目录下。

  2. 创建InstallParam实例和信号量。

    InstallParam installParam = { 
    .installLocation = INSTALL_LOCATION_INTERNAL_ONLY, // 安装到系统目录
    .keepData = false
    };
    static sem_t g_sem;
    
    1
    2
    3
    4
    5
  3. 定义回调函数。

    static void InstallCallback(const uint8_t resultCode, const void *resultMessage)
    {
         std::string strMessage = reinterpret_cast<const char *>(resultMessage);
         if (!strMessage.empty()) {
            printf("install resultMessage is %s, %d\n", strMessage.c_str(),resultCode);
         }
         sem_post(&g_sem);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  4. 调用Install接口。

    const uint32_t WAIT_TIMEOUT = 30;
    sem_init(&g_sem, 0, 0);
    std::string installPath = “/storage/bundle/demo.hap”; // hap包的存储路径
    bool result = Install(installPath.c_str(), &installParam, InstallCallback);
    struct timespec ts = {};
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += WAIT_TIMEOUT; // 超时即释放信号量
    sem_timedwait(&g_sem, &ts);
    
    1
    2
    3
    4
    5
    6
    7
    8

卸载应用

卸载应用的时候可以选择是否保留应用的数据,开发者可以通过创建的InstallParam实例的成员变量keepData来确定。当keepData为true, 卸载应用之后将保留应用的数据,当keepData为false时,卸载应用之后将不会保留应用的数据。

  1. 创建InstallParam实例和信号量。

    static sem_t g_sem;
    InstallParam installParam = {
    .installLocation = 1,
         .keepData = false // 不保留应用数据
    };
    
    1
    2
    3
    4
    5
  2. 定义回调函数。

    static void UninstallCallback(const uint8_t resultCode, const void *resultMessage)
    {
        std::string strMessage = reinterpret_cast<const char *>(resultMessage);
        if (!strMessage.empty()) {
            printf("uninstall resultMessage is %s\n", strMessage.c_str());
            g_resultMessage = strMessage;
        }
        g_resultCode = resultCode;
        sem_post(&g_sem);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  3. 调用Uninstall接口。

    sem_init(&g_sem, 0, 0);
    const uint32_t WAIT_TIMEOUT = 30;
    std::string BUNDLE_NAME = “com.huawei.demo”; // 卸载应用的包名
    Uninstall(BUNDLE_NAME.c_str(), &installParam, UninstallCallback);
    struct timespec ts = {};
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += WAIT_TIMEOUT;
    sem_timedwait(&g_sem, &ts);
    
    1
    2
    3
    4
    5
    6
    7
    8

查询已安装应用的包信息

开发者可以利用BundleManager提供的接口GetBundleInfo来查询系统内已安装应用的包信息。

  1. 创建以及初始化BundleInfo。

    BundleInfo bundleInfo;
    (void) memset_s(&bundleInfo, sizeof(BundleInfo), 0, sizeof(BundleInfo));
    
    1
    2
  2. 调用GetBundleInfo接口,指定查询应用的包名,同时指定flag来确定获取的BundleInfo中是否含有元能力信息(实例代码以含有元能力信息为例)。

    std::string BUNDLE_NAME = "com.huawei.demo";
    uint8_t ret = GetBundleInfo(BUNDLE_NAME.c_str(), 1, &bundleInfo); // flags = 1,获取包信息中含有元能力信息
    
    1
    2
  3. 使用完获取的BundleInfo之后,要及时清理掉其内部所占用的内存空间避免内存泄漏。

    ClearBundleInfo(&bundleInfo);
    
    1

Hap包打包

打包工具一般集成到开发工具或者ide中,开发者一般不涉及直接使用该工具,下面的介绍开发者可以作为了解。打包工具的jar包在开源代码中的位置:developtools/packing_tool/jar。

  • 打包命令行参数

    表 2 打包所需要的资源文件描述

    命令参数

    对应的资源文件

    说明

    是否可缺省

    --mode

    -

    为“hap”字段,打包生成hap

    --json-path

    清单文件config.json

    -

    --resources-path

    资源文件resources

    -

    --assets-path

    资源文件assets

    -

    --lib-path

    依赖库文件

    -

    --shared-libs-path

    共享库文件

    针对系统应用的共享库,特殊情况下使用

    --ability-so-path

    主功能so文件

    -

    --index-path

    资源索引

    资源索引文件由资源生成工具生成,由资源流水线会集成该工具

    --out-path

    -

    生成的hap包输出路径,默认为当前目录

    --force

    -

    是否覆盖原有同名文件,默认为false

  • 打包示例

    • 开发视图

    • 编译视图

    • 使用打包工具执行以下命令打包:

      $ java -jar hmos_app_packing_tool.jar --mode hap --json-path ./config.json --assets-path ./assets/ --ability-so-path ./libentry.so --index-path ./resources.index --out-path out/entry.hap --force true
      
      1