1# 开发指导 2 3 4## 场景介绍 5 6- 带界面的Ability的应用,比如:新闻类的应用、视频类的应用、导航类的应用、支付类的应用等等,目前我们看到的大部分应用都是带有界面的用于人机交互的应用。 7 8- 不带界面的Ability应用,比如:音乐播放器能在后台播放音乐、后台提供计算服务、导航服务的各类应用等。 9 10不管是带界面的Ability应用还是不带界面的Ability应用,都要打包成Hap包,最终发布到应用市场,用户通过应用市场下载安装相应的应用。 11 12 13## 接口说明 14 15 **表1** Ability子系统的对外接口 16 17| 接口名称 | 接口描述 | 18| -------- | -------- | 19| Want \*WantParseUri(const char \*uri) | 反序列化接口,由字符串生成Want对象。 | 20| const char \*WantToUri(Want want) | 序列化接口,把Want对象生成字符串。 | 21| void SetWantElement(Want \*want, ElementName element); | 设置ElementName对象。 | 22| void SetWantData(Want \*want, const void \*data, uint16_t dataLength) | 设置数据。 | 23| bool SetWantSvcIdentity(Want \*want, SvcIdentity sid) | 设置SvcIdentity。 | 24| void ClearWant(Want \*want) | 清除Want的内部内存数据。 | 25| void SetMainRoute(const std::string &entry) | 设置AbilitySlice主路由。 | 26| void SetUIContent(RootView \*rootView) | 设置布局资源。 | 27| void OnStart(const Want& intent) | Ability生命周期状态回调,Ability启动时被回调。 | 28| void OnStop() | Ability生命周期状态回调,Ability销毁时被回调。 | 29| void OnActive(const Want& intent) | Ability生命周期状态回调,Ability显示时被回调。 | 30| void OnInactive() | Ability生命周期状态回调,Ability隐藏时被回调。 | 31| void OnBackground() | Ability生命周期状态回调,Ability退到后台时被回调。 | 32| const SvcIdentity \*OnConnect(const Want &want) | Service类型Ability第一次连接时被回调。 | 33| void OnDisconnect(const Want &want); | Service类型Ability断开连接被回调。 | 34| void MsgHandle(uint32_t funcId, IpcIo \*request, IpcIo \*reply); | Service类型Ability接收消息处理。 | 35| void Dump(const std::string &extra) | dump Ability信息。 | 36| void Present(AbilitySlice \*abilitySlice, const Want &want) | 发起AbilitySlice跳转。 | 37| void Terminate() | 退出当前AbilitySlice。 | 38| void SetUIContent(RootView \*rootView) | 设置当前AbilitySlice所在Ability的布局资源。 | 39| void OnStart(const Want& want) | AbilitySlice生命周期状态回调,AbilitySlice启动时被回调。 | 40| void OnStop() | AbilitySlice生命周期状态回调,AbilitySlice销毁时被回调。 | 41| void OnActive(const Want& want) | AbilitySlice生命周期状态回调,AbilitySlice显示时被回调。 | 42| void OnInactive() | AbilitySlice生命周期状态回调,AbilitySlice隐藏时被回调。 | 43| void OnBackground() | AbilitySlice生命周期状态回调,AbilitySlice退到后台时被回调。 | 44| int StartAbility(const Want &want) | 启动Ability。 | 45| int StopAbility(const Want &want) | 停止Service类型的Ability。 | 46| int TerminateAbility() | 销毁当前的Ability。 | 47| int ConnectAbility(const Want &want, const IAbilityConnection &conn, void \*data); | 绑定Service类型的Ability。 | 48| int DisconnectAbility(const IAbilityConnection &conn) | 解绑Service类型的Ability。 | 49| const char \*GetBundleName() | 获取当前ability的对应应用的包名。 | 50| const char \*GetSrcPath() | 获取当前ability的对应应用的安装路径。 | 51| const char \*GetDataPath() | 获取当前ability的对应应用的数据路径。 | 52| int StartAbility(const Want \*want) | 启动Ability,该接口可以不需要在基于Ability开发的应用中使用。 | 53| int ConnectAbility(const Want \*want, const IAbilityConnection \*conn, void \*data); | 绑定Service类型的Ability,该接口可以不需要在基于Ability开发的应用中使用。 | 54| int DisconnectAbility(const IAbilityConnection \*conn); | 解绑Service类型的Ability,该接口可以不需要在基于Ability开发的应用中使用。 | 55| int StopAbility(const Want \*want) | 停止Service类型的Ability,该接口可以不需要在基于Ability开发的应用中使用。 | 56| void (\*OnAbilityConnectDone)(ElementName \*elementName, SvcIdentity \*serviceSid, int resultCode, void \*data) | 绑定Service Ability的回调。 | 57| void (\*OnAbilityDisconnectDone)(ElementName \*elementName, int resultCode, void \*data) | 解绑Service Ability的回调。 | 58| void PostTask(const Task& task) | 投递任务到异步线程进行处理。 | 59| void PostQuit() | 退出当前线程的消息循环。 | 60| static AbilityEventHandler\* GetCurrentHandler() | 获取当前线程的事件处理器。 | 61| void Run() | 执行当前线程的消息循环。 | 62| \#define REGISTER_AA(className) | 注册开发者的Ability到框架中。 | 63| \#define REGISTER_AS(className) | 注册开发者的AbilitySlice到框架中。 | 64 65 66## 开发步骤 67 68 69### 创建Service类型的Ability 70 71 721. 在my_service_ability.h中创建Ability的子类MyServiceAbility。 73 74 ``` 75 class MyServiceAbility: public Ability { 76 protected: 77 void OnStart(const Want& want); 78 const SvcIdentity *OnConnect(const Want &want) override; 79 void MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply) override; 80 }; 81 ``` 82 832. 调用REGISTER_AA宏将ServiceAbility注册到应用框架中,以便应用框架实例化MyServiceAbility。 84 85 ``` 86 #include "my_service_ability.h" 87 88 REGISTER_AA(ServiceAbility) 89 90 void MyServiceAbility::OnStart(const Want& want) 91 { 92 printf("ServiceAbility::OnStart\n"); 93 Ability::OnStart(want); 94 } 95 96 const SvcIdentity *MyServiceAbility::OnConnect(const Want &want) 97 { 98 printf("ServiceAbility::OnConnect\n"); 99 return Ability::OnConnect(want); 100 } 101 102 void MyServiceAbility::MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply) 103 { 104 printf("ServiceAbility::MsgHandle, funcId is %u\n", funcId); 105 int result = 0; 106 if (funcId == 0) { 107 result = IpcIoPopInt32(request) + IpcIoPopInt32(request); 108 } 109 // push data 110 IpcIoPushInt32(reply, result); 111 } 112 ``` 113 1143. 实现Service相关的生命周期方法。Service也是一种Ability,Ability为服务提供了以下生命周期方法,用户可以重写这些方法来添加自己的处理。用户在重写的方法里,需要调用父类对应的方法。 115 - OnStart() 116 该方法在创建Service的时候调用,用于做一些Service初始化且耗时较短的工作,在Service的整个生命周期只会调用一次。 117 118 119 ```ts 120 void MyServiceAbility::OnStart(const Want& want) 121 { 122 printf("ServiceAbility::OnStart\n"); 123 Ability::OnStart(want); 124 } 125 ``` 126 - OnConnect() 127 在组件和服务连接时调用,该方法返回SvcIdentity,组件可以通过它与服务交互。 128 129 130 ```ts 131 const SvcIdentity *MyServiceAbility::OnConnect(const Want &want) 132 { 133 printf("ServiceAbility::OnConnect\n"); 134 return Ability::OnConnect(want); 135 } 136 ``` 137 - OnDisconnect() 138 在组件与绑定的Service断开连接时调用。 139 - OnStop() 140 在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。 141 1424. 重写消息处理方法。 143 MsgHandle是Service用来处理客户端消息的方法。其中funcId是客户端传过来的消息类型,request是客户端传过来的序列化请求参数。如果用户在处理完成之后想要把结果传回去,需要把结果序列化后写入reply中。 144 145 146 ```ts 147 void ServiceAbility::MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply) 148 { 149 printf("ServiceAbility::MsgHandle, funcId is %d\n", funcId); 150 int result = 0; 151 if (funcId == PLUS) { 152 result = IpcIoPopInt32(request) + IpcIoPopInt32(request); 153 } 154 // push data 155 IpcIoPushInt32(reply, result); 156 } 157 ``` 158 1595. 注册Service。 160 Service也需要在应用清单文件config.json中进行注册,注册类型type需要设置为service。 161 162 163 ```ts 164 "abilities": [{ 165 "name": "ServiceAbility", 166 "icon": "res/drawable/phone.png", 167 "label": "test app 2", 168 "launchType": "multiton", 169 "type": "service", 170 "exported": true 171 } 172 ] 173 ``` 174 1756. 启动Service。 176 - Ability为用户提供了StartAbility()方法来启动另外一个Ability,因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。 177 开发者可以通过Want的SetWantElement ()来设置目标服务信息。ElementName结构体的两个主要参数:第一个参数为包名称;第二个参数为目标Ability。 178 179 ```ts 180 { 181 Want want = { nullptr }; 182 ElementName element = { nullptr }; 183 SetElementBundleName(&element, "com.company.appname"); 184 SetElementAbilityName(&element, "ServiceAbility"); 185 SetWantElement(&want, element); 186 StartAbility(want); 187 ClearElement(&element); 188 ClearWant(&want); 189 } 190 ``` 191 192 StartAbility() 方法会立即执行,如果Service尚未运行,则系统首先会调用OnStart()。 193 - 停止Service。 194 Service一旦创建就会一直保持在后台运行,开发者可以通过调用StopAbility()来停止Service。 195 1967. 连接Service。 197 - 如果Service需要与Page Ability或其他应用组件中的Service进行交互,则应创建用于连接的Service。Service支持其他Ability通过ConnectAbility()与其进行连接,ConnectAbility()需要传入目标Service的Want,以及IAbilityConnection的实例来处理回调。IAbilityConnection提供了两个方法供用户实现,OnAbilityConnectDone()用来处理连接的回调,OnAbilityDisconnectDone()用来处理断开连接的回调。 198 199 ```ts 200 { 201 // ability创建IAbilityConnection对象和定义IAbilityConnection的两个方法实现 202 IAbilityConnection abilityConnection = new IAbilityConnection(); 203 abilityConnection->OnAbilityConnectDone = OnAbilityConnectDone; 204 abilityConnection->OnAbilityDisconnectDone = OnAbilityDisconnectDone; 205 206 void OnAbilityConnectDone(ElementName *elementName, SvcIdentity *serviceSid, 207 int resultCode, void *data) 208 { 209 if (resultCode != 0) { 210 return; 211 } 212 // push data 213 IpcIo request; 214 char dataBuffer[IPC_IO_DATA_MAX]; 215 IpcIoInit(&request, dataBuffer, IPC_IO_DATA_MAX, 0); 216 IpcIoPushInt32(&request, 10); 217 IpcIoPushInt32(&request, 6); 218 219 // send and getReply 220 IpcIo reply; 221 uintptr_t ptr = 0; 222 if (Transact(nullptr, *serviceSid, 0, &request, &reply, 223 LITEIPC_FLAG_DEFAULT, &ptr) != LITEIPC_OK) { 224 printf("transact error\n"); 225 return; 226 } 227 int result = IpcIoPopInt32(&reply); 228 printf("execute add method, result is %d\n", result); 229 if (ptr != 0) { 230 FreeBuffer(nullptr, reinterpret_cast<void *>(ptr)); 231 } 232 } 233 234 void OnAbilityDisconnectDone(ElementName *elementName, 235 int resultCode, void *data) 236 { 237 printf("elementName is %s, %s\n", 238 elementName->bundleName, elementName->abilityName); 239 } 240 } 241 ``` 242 - 发起connect和disconnect。 243 244 ```ts 245 { 246 // ability发起connect 247 Want want = { nullptr }; 248 ElementName element = { nullptr }; 249 SetElementBundleName(&element, "com.company.appname"); 250 SetElementAbilityName(&element, "ServiceAbility"); 251 SetWantElement(&want, element); 252 ConnectAbility(want, *abilityConnection, this); 253 254 // ability发起disconnect 255 DisconnectAbility(*abilityConnection); 256 } 257 ``` 258 259 260### 包管理接口使用指导 261 262 263**安装应用** 264 265 266 安装接口只能给内置的系统应用使用。根据应用的安装路径,可以在安装应用时进行选择: 267- 将应用安装到系统默认的文件目录/storage/app/。 268 269- 将应用安装到系统外挂的存储介质中,例如micro sdcard。 270 271 272这两种选择可以在创建InstallParam实例的时候指定,当InstallParam的成员变量installLocation为 INSTALL_LOCATION_INTERNAL_ONLY时,意味着应用将会被安装到/storage/app/目录下;当InstallParam的成员变量installLocation为INSTALL_LOCATION_PREFER_EXTERNAL时,意味着应用将被安装到存储介质,其安装目录是/sdcard/app/。由于安装应用的过程是异步的,所以需要使用类似信号量的机制来确保安装的回调可以被执行。 273 274 275 安装应用的步骤如下(示例代码以安装到系统目录为例): 2761. 将经过安全签名的应用放置于指定的目录下。 277 2782. 创建InstallParam实例和信号量。 279 280 ```ts 281 InstallParam installParam = { 282 .installLocation = INSTALL_LOCATION_INTERNAL_ONLY, // 安装到系统目录 283 .keepData = false 284 }; 285 static sem_t g_sem; 286 ``` 287 2883. 定义回调函数。 289 290 ```ts 291 static void InstallCallback(const uint8_t resultCode, const void *resultMessage) 292 { 293 std::string strMessage = reinterpret_cast<const char *>(resultMessage); 294 if (!strMessage.empty()) { 295 printf("install resultMessage is %s, %d\n", strMessage.c_str(),resultCode); 296 } 297 sem_post(&g_sem); 298 } 299 ``` 300 3014. 调用Install接口。 302 303 ```ts 304 const uint32_t WAIT_TIMEOUT = 30; 305 sem_init(&g_sem, 0, 0); 306 std::string installPath = “/storage/bundle/demo.hap”; // Hap包的存储路径 307 bool result = Install(installPath.c_str(), &installParam, InstallCallback); 308 struct timespec ts = {}; 309 clock_gettime(CLOCK_REALTIME, &ts); 310 ts.tv_sec += WAIT_TIMEOUT; // 超时即释放信号量 311 sem_timedwait(&g_sem, &ts); 312 ``` 313 314 315**卸载应用** 316 317 318 卸载应用的时候可以选择是否保留应用的数据,开发者可以通过创建的InstallParam实例的成员变量keepData来确定。当keepData为true, 卸载应用之后将保留应用的数据,当keepData为false时,卸载应用之后将不会保留应用的数据。 3191. 创建InstallParam实例和信号量。 320 321 ```ts 322 static sem_t g_sem; 323 InstallParam installParam = { 324 .installLocation = 1, 325 .keepData = false // 不保留应用数据 326 }; 327 ``` 328 3292. 定义回调函数。 330 331 ```ts 332 static void UninstallCallback(const uint8_t resultCode, const void *resultMessage) 333 { 334 std::string strMessage = reinterpret_cast<const char *>(resultMessage); 335 if (!strMessage.empty()) { 336 printf("uninstall resultMessage is %s\n", strMessage.c_str()); 337 g_resultMessage = strMessage; 338 } 339 g_resultCode = resultCode; 340 sem_post(&g_sem); 341 } 342 ``` 343 3443. 调用Uninstall接口。 345 346 ```ts 347 sem_init(&g_sem, 0, 0); 348 const uint32_t WAIT_TIMEOUT = 30; 349 std::string BUNDLE_NAME = “com.example.demo”; // 卸载应用的包名 350 Uninstall(BUNDLE_NAME.c_str(), &installParam, UninstallCallback); 351 struct timespec ts = {}; 352 clock_gettime(CLOCK_REALTIME, &ts); 353 ts.tv_sec += WAIT_TIMEOUT; 354 sem_timedwait(&g_sem, &ts); 355 ``` 356 357 358**查询已安装应用的包信息** 359 360 361 开发者可以利用BundleManager提供的接口GetBundleInfo来查询系统内已安装应用的包信息。 3621. 创建以及初始化BundleInfo。 363 364 ```ts 365 BundleInfo bundleInfo; 366 (void) memset_s(&bundleInfo, sizeof(BundleInfo), 0, sizeof(BundleInfo)); 367 ``` 368 3692. 调用GetBundleInfo接口,指定查询应用的包名,同时指定flag来确定获取的BundleInfo中是否含有元能力信息(实例代码以含有元能力信息为例)。 370 371 ```ts 372 std::string BUNDLE_NAME = "com.example.demo"; 373 uint8_t ret = GetBundleInfo(BUNDLE_NAME.c_str(), 1, &bundleInfo); // flags = 1,获取包信息中含有元能力信息 374 ``` 375 3763. 使用完获取的BundleInfo之后,要及时清理掉其内部所占用的内存空间避免内存泄漏。 377 378 ```ts 379 ClearBundleInfo(&bundleInfo); 380 ``` 381 382 383### Hap包打包 384 385 386 打包工具一般集成到开发工具或者IDE中,开发者一般不涉及直接使用该工具,下面的介绍开发者可以作为了解。打包工具的jar包在开源代码中的位置:developtools/packing_tool/jar。 387- 打包命令行参数 388 **表2** 打包所需要的资源文件描述 389 390 | 命令参数 | 对应的资源文件 | 说明 | 是否可缺省 | 391 | -------- | -------- | -------- | -------- | 392 | --mode | - | 为“hap”字段,打包生成Hap | 否 | 393 | --json-path | 清单文件config.json | - | 否 | 394 | --resources-path | 资源文件resources | - | 是 | 395 | --assets-path | 资源文件assets | - | 是 | 396 | --lib-path | 依赖库TONGGUO 文件 | - | 是 | 397 | --shared-libs-path | 共享库文件 | 针对系统应用的共享库,特殊情况下使用 | 是 | 398 | --ability-so-path | 主功能so文件 | - | 是 | 399 | --index-path | 资源索引 | 资源索引文件由资源生成工具生成,由资源流水线会集成该工具 | 是 | 400 | --out-path | - | 生成的Hap包输出路径,默认为当前目录 | 是 | 401 | --force | - | 是否覆盖原有同名文件,默认为false | 是 | 402 403- 打包示例 404 405 **图1** 开发视图 406 407 ![zh-cn_image_0000001154005784](figures/zh-cn_image_0000001154005784.png) 408 409 410 **图2** 编译视图 411 412 ![zh-cn_image_0000001154167492](figures/zh-cn_image_0000001154167492.png) 413 414 415 **图3** 使用打包工具执行以下命令打包: 416 417 ![zh-cn_image_0000001200127655](figures/zh-cn_image_0000001200127655.png) 418 419 420 ``` 421 $ 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 422 ``` 423