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相关的生命周期方法。
115
116   Service也是一种Ability,Ability为服务提供了以下生命周期方法,用户可以重写这些方法来添加自己的处理。用户在重写的方法里,需要调用父类对应的方法。
117
118   - OnStart()
119
120     该方法在创建Service的时候调用,用于做一些Service初始化且耗时较短的工作,在Service的整个生命周期只会调用一次。
121
122      ```
123      void MyServiceAbility::OnStart(const Want& want)
124      {
125          printf("ServiceAbility::OnStart\n");
126          Ability::OnStart(want);
127      }
128      ```
129   - OnConnect​()
130
131     在组件和服务连接时调用,该方法返回SvcIdentity,组件可以通过它与服务交互。
132
133      ```
134      const SvcIdentity *MyServiceAbility::OnConnect(const Want &want)
135      {
136          printf("ServiceAbility::OnConnect\n");
137          return Ability::OnConnect(want);
138      }
139      ```
140   - OnDisconnect​()
141
142     在组件与绑定的Service断开连接时调用。
143
144   - OnStop()
145
146     在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。
147
1484. 重写消息处理方法。
149
150   MsgHandle是Service用来处理客户端消息的方法。其中funcId是客户端传过来的消息类型,request是客户端传过来的序列化请求参数。如果用户在处理完成之后想要把结果传回去,需要把结果序列化后写入reply中。
151
152   ```
153   void ServiceAbility::MsgHandle(uint32_t funcId, IpcIo *request, IpcIo *reply)
154   {
155       printf("ServiceAbility::MsgHandle, funcId is %d\n", funcId);
156       int result = 0;
157       if (funcId == PLUS) {
158           result = IpcIoPopInt32(request) + IpcIoPopInt32(request);
159       }
160       // push data
161       IpcIoPushInt32(reply, result);
162   }
163   ```
164
1655. 注册Service。
166
167   Service也需要在应用清单文件config.json中进行注册,注册类型type需要设置为service。
168
169
170   ```
171   "abilities": [{
172       "name": "ServiceAbility",
173       "icon": "res/drawable/phone.png",
174       "label": "test app 2",
175       "launchType": "standard",
176       "type": "service",
177       "visible": true
178   }
179   ]
180   ```
181
1826. 启动Service。
183
184   - Ability为用户提供了StartAbility()方法来启动另外一个Ability,因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。
185
186     开发者可以通过Want的SetWantElement ()来设置目标服务信息。ElementName结构体的两个主要参数:第一个参数为包名称;第二个参数为目标Ability。
187
188      ```
189      {
190          Want want = { nullptr };
191          ElementName element = { nullptr };
192          SetElementBundleName(&element, "com.company.appname");
193          SetElementAbilityName(&element, "ServiceAbility");
194          SetWantElement(&want, element);
195          StartAbility(want);
196          ClearElement(&element);
197          ClearWant(&want);
198      }
199      ```
200
201      StartAbility() 方法会立即执行,如果Service尚未运行,则系统首先会调用OnStart()。
202   - 停止Service。
203
204     Service一旦创建就会一直保持在后台运行,开发者可以通过调用StopAbility()来停止Service。
205
2067. 连接Service。
207
208   - 如果Service需要与Page Ability或其他应用组件中的Service进行交互,则应创建用于连接的Service。
209
210     Service支持其他Ability通过ConnectAbility()与其进行连接,ConnectAbility()需要传入目标Service的Want,以及IAbilityConnection的实例来处理回调。IAbilityConnection提供了两个方法供用户实现,OnAbilityConnectDone()用来处理连接的回调,OnAbilityDisconnectDone()用来处理断开连接的回调。
211
212      ```
213      {
214          // ability创建IAbilityConnection对象和定义IAbilityConnection的两个方法实现
215          IAbilityConnection abilityConnection = new IAbilityConnection();
216          abilityConnection->OnAbilityConnectDone = OnAbilityConnectDone;
217          abilityConnection->OnAbilityDisconnectDone = OnAbilityDisconnectDone;
218
219          void OnAbilityConnectDone(ElementName *elementName, SvcIdentity *serviceSid,
220              int resultCode, void *data)
221          {
222              if (resultCode != 0) {
223                  return;
224              }
225              // push data
226              IpcIo request;
227              char dataBuffer[IPC_IO_DATA_MAX];
228              IpcIoInit(&request, dataBuffer, IPC_IO_DATA_MAX, 0);
229              IpcIoPushInt32(&request, 10);
230              IpcIoPushInt32(&request, 6);
231
232              // send and getReply
233              IpcIo reply;
234              uintptr_t ptr = 0;
235              if (Transact(nullptr, *serviceSid, 0, &request, &reply,
236                  LITEIPC_FLAG_DEFAULT, &ptr) != LITEIPC_OK) {
237                  printf("transact error\n");
238                  return;
239              }
240              int result = IpcIoPopInt32(&reply);
241              printf("execute add method, result is %d\n", result);
242              if (ptr != 0) {
243                  FreeBuffer(nullptr, reinterpret_cast<void *>(ptr));
244              }
245          }
246
247          void OnAbilityDisconnectDone(ElementName *elementName,
248              int resultCode, void *data)
249          {
250              printf("elementName is %s, %s\n",
251                  elementName->bundleName, elementName->abilityName);
252          }
253      }
254      ```
255   - 发起connect和disconnect。
256
257      ```
258      {
259          // ability发起connect
260          Want want = { nullptr };
261          ElementName element = { nullptr };
262          SetElementBundleName(&element, "com.company.appname");
263          SetElementAbilityName(&element, "ServiceAbility");
264          SetWantElement(&want, element);
265          ConnectAbility(want, *abilityConnection, this);
266
267          // ability发起disconnect
268          DisconnectAbility(*abilityConnection);
269      }
270      ```
271
272
273### 包管理接口使用指导
274
275
276**安装应用**
277
278
279  安装接口只能给内置的系统应用使用。根据应用的安装路径,可以在安装应用时进行选择:
280
281- 将应用安装到系统默认的文件目录/storage/app/。
282
283- 将应用安装到系统外挂的存储介质中,例如micro sdcard。
284
285
286这两种选择可以在创建InstallParam实例的时候指定,
287
288- 当InstallParam的成员变量installLocation为 INSTALL_LOCATION_INTERNAL_ONLY时,意味着应用将会被安装到/storage/app/目录下。
289- 当InstallParam的成员变量installLocation为INSTALL_LOCATION_PREFER_EXTERNAL时,意味着应用将被安装到存储介质,其安装目录是/sdcard/app/。
290
291由于安装应用的过程是异步的,所以需要使用类似信号量的机制来确保安装的回调可以被执行。
292
293
294  安装应用的步骤如下(示例代码以安装到系统目录为例):
2951. 将经过安全签名的应用放置于指定的目录下。
296
2972. 创建InstallParam实例和信号量。
298
299   ```
300   InstallParam installParam = {
301   .installLocation = INSTALL_LOCATION_INTERNAL_ONLY, // 安装到系统目录
302   .keepData = false
303   };
304   static sem_t g_sem;
305   ```
306
3073. 定义回调函数。
308
309   ```
310   static void InstallCallback(const uint8_t resultCode, const void *resultMessage)
311   {
312        std::string strMessage = reinterpret_cast<const char *>(resultMessage);
313        if (!strMessage.empty()) {
314           printf("install resultMessage is %s, %d\n", strMessage.c_str(),resultCode);
315        }
316        sem_post(&g_sem);
317   }
318   ```
319
3204. 调用Install接口。
321
322   ```
323   const uint32_t WAIT_TIMEOUT = 30;
324   sem_init(&g_sem, 0, 0);
325   std::string installPath = “/storage/bundle/demo.hap”; // Hap包的存储路径
326   bool result = Install(installPath.c_str(), &installParam, InstallCallback);
327   struct timespec ts = {};
328   clock_gettime(CLOCK_REALTIME, &ts);
329   ts.tv_sec += WAIT_TIMEOUT; // 超时即释放信号量
330   sem_timedwait(&g_sem, &ts);
331   ```
332
333
334**卸载应用**
335
336
337  卸载应用的时候可以选择是否保留应用的数据,开发者可以通过创建的InstallParam实例的成员变量keepData来确定。当keepData为true, 卸载应用之后将保留应用的数据,当keepData为false时,卸载应用之后将不会保留应用的数据。
3381. 创建InstallParam实例和信号量。
339
340   ```
341   static sem_t g_sem;
342   InstallParam installParam = {
343   .installLocation = 1,
344        .keepData = false // 不保留应用数据
345   };
346   ```
347
3482. 定义回调函数。
349
350   ```
351   static void UninstallCallback(const uint8_t resultCode, const void *resultMessage)
352   {
353       std::string strMessage = reinterpret_cast<const char *>(resultMessage);
354       if (!strMessage.empty()) {
355           printf("uninstall resultMessage is %s\n", strMessage.c_str());
356           g_resultMessage = strMessage;
357       }
358       g_resultCode = resultCode;
359       sem_post(&g_sem);
360   }
361   ```
362
3633. 调用Uninstall接口。
364
365   ```
366   sem_init(&g_sem, 0, 0);
367   const uint32_t WAIT_TIMEOUT = 30;
368   std::string BUNDLE_NAME = “com.example.demo”; // 卸载应用的包名
369   Uninstall(BUNDLE_NAME.c_str(), &installParam, UninstallCallback);
370   struct timespec ts = {};
371   clock_gettime(CLOCK_REALTIME, &ts);
372   ts.tv_sec += WAIT_TIMEOUT;
373   sem_timedwait(&g_sem, &ts);
374   ```
375
376
377**查询已安装应用的包信息**
378
379
380  开发者可以利用BundleManager提供的接口GetBundleInfo来查询系统内已安装应用的包信息。
3811. 创建以及初始化BundleInfo。
382
383   ```
384   BundleInfo bundleInfo;
385   (void) memset_s(&bundleInfo, sizeof(BundleInfo), 0, sizeof(BundleInfo));
386   ```
387
3882. 调用GetBundleInfo接口,指定查询应用的包名,同时指定flag来确定获取的BundleInfo中是否含有元能力信息(实例代码以含有元能力信息为例)。
389
390   ```
391   std::string BUNDLE_NAME = "com.example.demo";
392   uint8_t ret = GetBundleInfo(BUNDLE_NAME.c_str(), 1, &bundleInfo); // flags = 1,获取包信息中含有元能力信息
393   ```
394
3953. 使用完获取的BundleInfo之后,要及时清理掉其内部所占用的内存空间避免内存泄漏。
396
397   ```
398   ClearBundleInfo(&bundleInfo);
399   ```
400
401
402### Hap包打包
403
404
405  打包工具一般集成到开发工具或者IDE中,开发者一般不涉及直接使用该工具,下面的介绍开发者可以作为了解。打包工具的jar包在开源代码中的位置:developtools/packing_tool/jar406
407- 打包命令行参数
408
409   **表2** 打包所需要的资源文件描述
410
411  | 命令参数 | 对应的资源文件 | 说明 | 是否可缺省 |
412  | -------- | -------- | -------- | -------- |
413  | --mode | - | 为“hap”字段,打包生成Hap | 否 |
414  | --json-path | 清单文件config.json | - | 否 |
415  | --resources-path | 资源文件resources | - | 是 |
416  | --assets-path | 资源文件assets | - | 是 |
417  | --lib-path | 依赖库TONGGUO&nbsp;文件 | - | 是 |
418  | --shared-libs-path | 共享库文件 | 针对系统应用的共享库,特殊情况下使用 | 是 |
419  | --ability-so-path | 主功能so文件 | - | 是 |
420  | --index-path | 资源索引 | 资源索引文件由资源生成工具生成,由资源流水线会集成该工具 | 是 |
421  | --out-path | - | 生成的Hap包输出路径,默认为当前目录 | 是 |
422  | --force | - | 是否覆盖原有同名文件,默认为false | 是 |
423
424- 打包示例
425
426    **图1** 开发视图
427
428    ![zh-cn_image_0000001154005784](figures/zh-cn_image_0000001154005784.png)
429
430
431    **图2** 编译视图
432
433    ![zh-cn_image_0000001154167492](figures/zh-cn_image_0000001154167492.png)
434
435
436    **图3** 使用打包工具执行以下命令打包:
437
438    ![zh-cn_image_0000001200127655](figures/zh-cn_image_0000001200127655.png)
439
440
441  ```
442  $ 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
443  ```
444