文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

HarmonyOS服务卡片开发知识总结

2024-12-14 01:06

关注

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

前言

服务卡片的征文活动也已经接近尾声,在这段时间里,论坛里有许多优秀的服务卡片作品和相关的文章涌现。我拜读了专栏中几乎所有的服务卡片的开发分享文章,从每一篇文章中提取并汲取精华,整合到本文中。一些较为显然的介绍性的内容在本文中将会被省略(如卡片有多少种尺寸之类),但是一些即使在很多文章都已经说明过的,而我又认为比较重要的内容,我仍然会再次记录下来(例如卡片的框架)。另外,作为学习笔记,我会记录下一些我在学习种遇到的新的名词的概念,这些概念可能是大家已经熟知的,这部分大家可以直接忽略;文章的内容整合自论坛种的众多文章,所以大家可能会在本文中看到自己文章的影子。

服务卡片的框架

服务卡片整体框架主要包含三部分:卡片使用方、卡片管理服务和卡片提供方。其概念分别如下:

● 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置,如桌面、服务中心、搜索等。

● 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。

● 卡片提供方:提供卡片显示内容的HarmonyOS应用或原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。

卡片的运行框架有如下示意图:


简明版示意图


详细版示意图

卡片的常用功能

Java卡片与JS卡片功能的对比图


MainAbility的自动生成函数解析

在新建服务卡片的时候,在MainAbility类中将会生成一些回调方法,具体方法及其回调条件如下图,在后面的具体的卡片操作中,也会再次声明所调用的回调方法。


在onCreatForm(Intent)方法中,卡片提供方被拉起后intent会携带卡片相关信息,具体如下:


服务卡片生命周期回调函数时序如下:


配置卡片编辑功能

有些服务卡片需要具备可编辑能力,如天气App需要编辑所在城市。具体实现方法如下:在config.json中,对某一个form的配置增加formConfigAbility的属性配置,可实现编辑功能。formConfigAbility的值是一个url格式的Ability名称。若不配置formConfigAbility,则不显示编辑菜单。示例代码如下:(摘抄自[一文看懂HarmonyOS服务卡片运行原理和开发方法])(https://harmonyos.oss-cn-beijing.aliyuncs.com/images/202106/830a107135e5fa06fa1714ebaa9fa523833da2.jpg?x-oss-process=image/resize,w_1080,h_478)


卡片的定点/定时刷新:将回调updateForm()方法

运作机制示意图如下:


注意:定时刷新和定点刷新都配置的情况下,定时刷新优先。

JS卡片的定点更新步骤:

关闭定时更新:updateDuration”修改为0,以关闭定时刷新(config.json文件中)

设定“scheduledUpdateTime”的时刻

在具体的xxxxlmpl中重写updateFormData()方法

把需要刷新的数据存入一个ZSONObject实例中

将这个实例封装在一个FormBindingData的实例bindingData中

调用MainAbility的方法updateForm(),并将bindingData作为第二个实参。

JS卡片重写updateFormData()方法的代码如下:


JAVA卡片的定点更新步骤:

前三步与JS卡片相同

构造一个ComponentProvider的实例,用于表示一个Java卡片实例,传入的第一个实参是根据卡片尺寸得到的布局文件。然后,调用方法setText()修改卡片的;最后,调用MainAbility的方法updateForm(),并将componentProvider作为第二个实参。

JAVA卡片重写updateFormData()方法的代码如下:


卡片的定时刷新

卡片的定时刷新的最小单位时间是三十分钟,这就带来了两个问题:

创建卡片后的最初的三十分钟是不会进行卡片刷新的,所以需要进行手动的刷新。

如果是编写刷新间隔小于三十分的卡片(如时钟卡片每一秒刷新一次),那么需要自己重新创建一个timer实例来设定刷新时间。

添加手动刷新:用onCreateForm()调用onUpdateForm(formId)即可,代码摘抄自亮子力的哔哩哔哩卡片

  1. @Override 
  2. protected ProviderFormInfo onCreateForm(Intent intent) { 
  3.     ... ... 
  4.  
  5.     //初始化时先在线更新一下卡片 
  6.     onUpdateForm(formId); 
  7.  
  8.     return formController.bindFormData(); 

 重新编写刷新时间的代码为代码摘抄自Codelabs 之 时钟FA卡片开发样例

  1. private void startTimer() { 
  2.         Timer timer = new Timer(); 
  3.         timer.schedule( 
  4.                 new TimerTask() { 
  5.                     @Override 
  6.                     public void run() { 
  7.                         updateForms(); 
  8.                     } 
  9.                 }, 
  10.                 0, 
  11.                 SEND_PERIOD); 
  12.     } 
  1. private void updateForms() { 
  2.         // 从数据库中获取卡片信息 
  3.         HiLog.info(LABEL, "从数据库中获取卡片"); 
  4.         OrmPredicates ormPredicates = new OrmPredicates(Form.class); 
  5.         List
     formList = connect.query(ormPredicates); 
  6.         int layoutId=0; 
  7.         // 更新时分秒 
  8.         if (formList.size() <= 0) { 
  9.             return
  10.         } 
  11.         HiLog.info(LABEL, "遍历卡片列表,逐个更新"); 
  12.         for (Form form : formList) { 
  13.             // 遍历卡片列表更新卡片 
  14.             ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(form, this); 
  15.             try { 
  16.                 Long updateFormId = form.getFormId(); 
  17.                 HiLog.info(LABEL, "updateForm,更新卡片[:"+updateFormId+"] 的数据,并非数据库操作" ); 
  18.                 //更新卡片数据 
  19.                 boolean isSucc=updateForm(updateFormId, componentProvider); 
  20.                 HiLog.info(LABEL, "更新卡片数据完成,"+isSucc); 
  21.             } catch (FormException e) { 
  22.                 // 删除不存在的卡片 
  23.                 DatabaseUtils.deleteFormData(form.getFormId(), connect); 
  24.                 HiLog.error(LABEL, "更新卡片异常,删除数据库中不存在的卡片"); 
  25.             } 
  26.         } 
  27.     }

JAVA与JS卡片定点定时更新的对比总结

由上可见,两者的大体思路是相同的:即先在config.json文件中修改配置,然后对具体的xxxxlmpl中的updateFormData()进行重写。不同的是,JS卡片式通过创建一个ZSONOject实例,然后封装在FormBindingData的实例中的办法更新数据;而JAVA卡片则是通过创建ComponentProvider的实例,通过它来更新数据。因而,在调用MainAbility的方法updateForm()的时候,JS卡片的第二个实参是bingding Data,而JAVA卡片是componentProvider。

卡片的跳转事件:

运作机制示意图如下:


JS卡片的跳转事件步骤

在对应卡片的index.json文件中添加跳转配置


相关参数意义具体如下:


在index.hml中,在标签image中添加一个属性onclick,并将值设置为刚刚在index.json中定义的action的名称“startSecondAbility”

根据key的值“params”获得一个字符串格式的JSON数据;然后,调用ZSONObject.stringToZSON()将其转换为一个ZSONObject的实例data;最后,从data中分别获得”param1”和”param2”这两个key对应的value。


JAVA卡片的跳转事件步骤

打开MainWidget3Impl,在方法bindFormData()中,当卡片尺寸为2*4时,调用componentProvider的方法setIntentAgent(),传入的第一个实参是在布局文件中的id,第二个实参是用于页面跳转的IntentAgent实例。代码如下所示:


定义方法getStartAbilityIntentAgent()的具体实现,代码如下:


总结

Java卡片要通过IntentAgent来设置事件,相比之下JS似乎显得要方便一些。

卡片的消息事件

JS卡片的消息事件步骤

在对应卡片的index.json文件中添加信息事件配置


相关参数意义具体如下:


重写方法onTriggerFormEvent(),代码如下:


message是一个字符串格式的JSON数据;然后,调用ZSONObject.stringToZSON()将message转换为一个ZSONObject的实例data;最后,从data中分别获得”p1”和”p2”这两个key对应的value。

服务卡片常用到的数据库

数据持久化

概念:数据持久化就是将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中的数据模型的统称

好处:

程序代码重用性强,即使更换数据库,只需要更改配置文件,不必重写程序代码。

业务逻辑代码可读性强,在代码中不会有大量的SQL语言,提高程序的可读性。

持久化技术可以自动优化,以减少对数据库的访问量,提高程序运行效率。

数据持久化对象的基本操作有:保存、更新、删除、查询等。

轻量级偏好数据库

轻量级数据库官方文档链接

轻量级偏好数据库在服务卡片开发中的使用场景

根据轻量级偏好数据库的特点,在服务卡片的开发中,涉及到保存网络上的内容时,可以使用轻量级偏好数据库来存储cookie信息。

轻量级偏好数据库的创建(获取preference实例)

  1. context = getContext(); 
  2. databaseHelper = new DatabaseHelper(context); 
  3. filename = "preference_db";//文件名,不能为空,也不能包含路径 
  4. preferences = databaseHelper.getPreferences(filename); 

轻量级偏好数据库的插入,删除,修改,查询

插入与修改:

首先获取指定文件对应的Preferences实例,然后借助Preferences API将数据写入Preferences实例,通过flush或者flushSync将Preferences实例持久化。其中flush为异步地写入,flushSync为同步写入。flush()会立即更改内存中的Preferences对象,但会将更新异步写入磁盘。flushSync()更改内存中的数据的同时会将数据同步写入磁盘。由于flushSync()是同步的,建议不要从主线程调用它,以避免界面卡顿。

异步写入代码:

  1. preferences.putInt("number",number); 
  2. preferences.putString("fruit","apple"); 
  3. preferences.flush(); 

同步写入的代码:

  1. preferences.putInt("intKey", 3); 
  2. preferences.putString("StringKey""String value"); 
  3. bool result = preferences.flushSync(); 

查询:

  1. int value = preferences.getInt("intKey", 0); 

删除:

删除preferen单实例

  1. DatabaseHelper databaseHelper = new DatabaseHelper(context); 
  2. String fileName = "name"; // fileName表示文件名,其取值不能为空,也不能包含路径。 
  3. databaseHelper.removePreferencesFromCache(fileName); 

删除指定文件

  1. DatabaseHelper databaseHelper = new DatabaseHelper(context); 
  2. String fileName = "name"; // fileName表示文件名,其取值不能为空,也不能包含路径。 
  3. boolean result = databaseHelper.deletePreferences(fileName); 

注册观察者

  1. private class PreferencesObserverImpl implements Preferences.PreferencesObserver { 
  2.     
  3.     @Override     
  4.     public void onChange(Preferences preferences, String key) { 
  5.         if ("intKey".equals(key)) { 
  6.            HiLog.info(LABLE, "Change Received:[key=value]");         
  7.         }     
  8.     } 
  9.  
  10. // 向preferences实例注册观察者 
  11. PreferencesObserverImpl observer = new PreferencesObserverImpl(); 
  12. preferences.registerObserver(observer); 
  13. // 修改数据 
  14. preferences.putInt("intKey", 3); 
  15. preferences.flush(); 
  16. // 修改数据后,observer的onChange方法会被回调 
  17. // 向preferences实例注销观察者 
  18. preferences.unRegisterObserver(observer); 

轻量级数据库在服务卡片中的使用例子:(摘抄自亮子力的哔哩哔哩卡片

  1.  
  2.   public static void SaveMap(Preferences preferences,Map map){ 
  3.       // 遍历map 
  4.       for (Map.Entry entry : map.entrySet()) { 
  5.           HiLog.info(TAG,entry.getKey() + "=" + entry.getValue()); 
  6.           preferences.putString(entry.getKey(),entry.getValue());//3.将数据写入Preferences实例, 
  7.       } 
  8.       preferences.flushSync();//4.通过flush()或者flushSync()将Preferences实例持久化。 
  9.   } 
  10.  
  11.    
  12.   public static Map GetCookieMap(Preferences preferences){ 
  13.       Map map = new HashMap<>(); 
  14.       map = preferences.getAll();//3.读取数据 
  15.       return map; 
  16.   } 

对象关系映射数据库

对象关系映射数据库官方文档

对象关系数据库在服务卡片中的应用

对象关系数据库在服务卡片中主要用作存储卡片的信息,如卡片id,卡片名字,卡片大小等。

对象关系数据库的创建

配置“build.gradle”文件,这里仅展示使用注解处理器的模块为“com.huawei.ohos.hap”模块,则需要在模块的“build.gradle”文件的ohos节点中添加以下配置:

  1. compileOptions{         
  2.     annotationEnabled true     
  3. }  

构造对象关系映射数据库:创建数据库类并配置对应的属性

  1. //数据库有三个表,User,Book, AllDataType三个表,版本号为1 
  2. @Database(entities = {User.class, Book.class, AllDataType.class}, version = 1)  
  3. public abstract class BookStore extends OrmDatabase {  

构造数据表,即创建数据库实体类并配置对应的属性(如对应表的主键,外键等)。数据表必须与其所在的数据库在同一个模块中。

  1. @Entity(tableName = "user", ignoredColumns = {"ignoredColumn1""ignoredColumn2"}, 
  2.     indices = {@Index(value = {"firstName""lastName"}, name = "name_index"unique = true)})  
  3. public class User extends OrmObject {  
  4.     // 此处将userId设为了自增的主键。注意只有在数据类型为包装类型时,自增主键才能生效。 
  5.     @PrimaryKey(autoGenerate = true)  
  6.     private Integer userId;    
  7.     private String firstName;    
  8.     private String lastName;    
  9.     private int age;    
  10.     private double balance;    
  11.     private int ignoredColumn1;  
  12.     private int ignoredColumn2;  
  13.   
  14.     // 需添加各字段的getter和setter方法。  

使用对象数据操作接口OrmContext创建数据库。

  1. // context入参类型为ohos.app.Context,注意不要使用slice.getContext()来获取context,请直接传入slice,否则会出现找不到类的报错。 
  2. DatabaseHelper helper = new DatabaseHelper(this);  
  3.  
  4. OrmContext context = helper.getOrmContext("BookStore""BookStore.db", BookStore.class);  

对象关系映射数据库的增删改查

查询数据:

  1. OrmPredicates query = context.where(User.class).equalTo("lastName""San");  
  2. List<User> users = context.query(query); 

更新数据:更新数据有两种方法,这里仅贴出其中一种

  1. // 更新数据 
  2. OrmPredicates predicates = context.where(User.class); 
  3. predicates.equalTo("age", 29); 
  4. List<User> users = context.query(predicates); 
  5. User user = users.get(0); 
  6. user.setFirstName("Li"); 
  7. context.update(user); 
  8. context.flush(); 

删除数据:删除数据也有两种方法,这里也仅贴出其中一种,另一种方法可以到官方文档查看

  1. // 删除数据 
  2. OrmPredicates predicates = context.where(User.class); 
  3. predicates.equalTo("age", 29); 
  4. List<User> users = context.query(predicates); 
  5. User user = users.get(0); 
  6. context.delete(user); 
  7. context.flush(); 

注册观察者

  1. // 定义一个观察者类。 
  2. private class CustomedOrmObjectObserver implements OrmObjectObserver { 
  3.     @Override     
  4.     public void onChange(OrmContext changeContext, AllChangeToTarget subAllChange) { 
  5.         // 用户可以在此处定义观察者行为 
  6.     } 
  7.  
  8. // 调用registerEntityObserver方法注册一个观察者observer。 
  9. CustomedOrmObjectObserver observer = new CustomedOrmObjectObserver(); 
  10. context.registerEntityObserver("user", observer); 
  11.  
  12. // 当以下方法被调用,并flush成功时,观察者observer的onChange方法会被触发。其中,方法的入参必须为User类的对象。 
  13. public  boolean insert(T object) 
  14. public  boolean update(T object) 
  15. public  boolean delete(T object) 

对象关系映射数据库在服务卡片中的应用实例

  1. @Entity(tableName = "form"
  2. public class Form extends OrmObject { 
  3.     @PrimaryKey() 
  4.     // 卡片id 
  5.     private Long formId; 
  6.  
  7.     // 卡片名称 
  8.     private String formName; 
  9.  
  10.     // 卡片名称 
  11.     private int dimension; 
  12.  
  13.      
  14.     public Form(Long formId, String formName, int dimension) { 
  15.         this.formId = formId; 
  16.         this.formName = formName; 
  17.         this.dimension = dimension; 
  18.     } 
  1. protected ProviderFormInfo onCreateForm(Intent intent) { 
  2.         HiLog.info(TAG, "onCreateForm"); 
  3.         long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID); 
  4.         String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY); 
  5.         int dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2); 
  6.         HiLog.info(TAG, "onCreateForm: formId=" + formId + ",formName=" + formName); 
  7.         FormControllerManager formControllerManager = FormControllerManager.getInstance(this); 
  8.         FormController formController = formControllerManager.getController(formId); 
  9.         formController = (formController == null) ? formControllerManager.createFormController(formId, 
  10.                 formName, dimension) : formController; 
  11.         if (formController == null) { 
  12.             HiLog.error(TAG, "Get null controller. formId: " + formId + ", formName: " + formName); 
  13.             return null
  14.         } 
  15.  
  16.         //初始化时先在线更新一下卡片 
  17.         onUpdateForm(formId); 
  18.  
  19.         // TODO 获取添加到桌面的服务卡片ID 
  20.  
  21.         // 存储卡片信息。对象关系映射数据库2.通过对象数据操作接口OrmContext,创建一个别名为“FormDatabase”,数据库文件名为“FormDatabase.db”的数据库 
  22.         ormContext = databaseHelper.getOrmContext("FormDatabase""FormDatabase.db", MyOrmDatabase.class); 
  23.         // 对象关系映射数据库3.实例化数据表 
  24.         HiLog.info(TAG, "存储卡片信息: formId=" + formId + ",formName=" + formName + ",dimension=" + dimension); 
  25.         Form form = new Form(formId, formName, dimension); 
  26.         // 对象关系映射数据库4.添加方法 
  27.         ormContext.insert(form); 
  28.         ormContext.flush(); 
  29.  
  30.  
  31.         return formController.bindFormData(); 
  32.     } 

网络编程的相关知识

这部分在我平时的学习未曾接触过,在大家眼中很平常的概念,在我这里都是全新的知识,所以会有较多的概念记录。

WebView组件的使用

WebView组件官方文档

WebView提供在应用中集成Web页面的能力。

服务卡片的开发中一般只需要加载Web页面,则常用操作如下:

配置应用的网络权限(如未配置网络权限的话)

  1.   ... 
  2.   "module": { 
  3.     ... 
  4.     "reqPermissions": [ 
  5.       { 
  6.         "name""ohos.permission.INTERNET" 
  7.       } 
  8.     ], 
  9.     ... 
  10.   } 

使用load方法加载Web页面

这里仅贴出使用xml方式布局的代码,使用代码方式布局可参看官方文档

  1. WebView webView = (WebView) findComponentById(ResourceTable.Id_webview); 
  2. webView.getWebConfig().setJavaScriptPermit(true);  // 如果网页需要使用JavaScript,增加此行;如何使用JavaScript下文有详细介绍   
  3. final String url = EXAMPLE_URL; // EXAMPLE_URL由开发者自定义 
  4. webView.load(url); 

什么是JSON文件

JSON的全称为JavaScript Object Notation,是轻量级的文本数据交换格式,JSON实际上是JavaScript的一个子集。重要的是,许多API请求返回值都是josn格式的结果。

JSON生成Java实体类

网上有很多相关的工具,如何自己编写我暂且还未学会,就决定先应用现有的工具了

什么是Cookie

由于HTTP是一种无状态协议,服务器没有办法单单从网络连接上面知道访问者的身份,因此就使用Cookie作为辨识访问者的一种标志。

Cookie是一段不超过4KB的小型文本数据,由一个名称(Name)、一个值(Value)和其它几个用于控制Cookie有效期、安全性、使用范围的可选属性组成。

运作机制如下图:


客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie,客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,

以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

实际就是颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。

cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量。

Cookie相关方法

CookieStore官方文档



卡片开发的基本步骤总结

选择已有的卡片模板,或者自行绘制卡片样式

对卡片进行初始化的设置

建立数据库(对象关系映射数据库存储卡片信息,涉及网络的可以用轻量级数据库存储cookie

给卡片上面的组件添加响应的事件

实现卡片的功能

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

来源:鸿蒙社区内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯