HarmonyOS實戰(zhàn)原子化服務(wù)初嘗試(ClockFACardDemo學(xué)習(xí))

      網(wǎng)友投稿 693 2022-05-30

      【本文正在參與“有獎?wù)魑?| HarmonyOS征文大賽”活動】

      寫在前面

      看到有一個HarmonyOS征文大賽”的活動,所以準(zhǔn)備在學(xué)習(xí)下,擁抱國產(chǎn)操作系統(tǒng), 之前學(xué)HarmonyOS 照著官網(wǎng)寫了一個Holle World(關(guān)于HarmonyOS的環(huán)境搭建,基本目錄結(jié)構(gòu),簡單Holle World實現(xiàn))

      博文主題是關(guān)于服務(wù)卡片,原子化服務(wù)的,關(guān)于這個,在HarmonyOS 2.0發(fā)布會上有看到有講。

      博文由兩部分內(nèi)容構(gòu)成: 服務(wù)卡片(原子化服務(wù))的一些基本概念,和官網(wǎng)Demo的代碼學(xué)習(xí)。

      依舊,把HarmonyOS Developer的地址留在這里,博文好多都是文檔里的東西,建議小伙伴看文檔學(xué)習(xí),對于英語很垃圾的我來講,這回不用翻譯啦,有中文版,沒障礙。

      嗯,博文有理解不對的地方請小伙伴積極留言

      時光不能倒流,如果人可以從80歲開始倒過來活的話,人生一定會更加精彩。--------任正非

      在最新發(fā)布的Harmonyos 2版本的新系統(tǒng)中, "服務(wù)卡片"服務(wù)成為一大亮點:^1

      全新的Harmonyos桌面簡潔有序,上滑App生成服務(wù)卡片,在桌面即可呈現(xiàn)更豐富的信息。

      卡片內(nèi)容實時更新,只需一管即可獲取所需信息,省去了打開App的時間。

      HarmonyOS實戰(zhàn)一原子化服務(wù)初嘗試(ClockFACardDemo學(xué)習(xí))

      卡片可大可小、可藏可顯,還能夠個性化定制,讓每個桌面獨一無二。

      同時,卡片也是原子化服務(wù)的載體,在服務(wù)中心可以輕松獲取、隨時分享,無需下載、安裝,一步到位獲取各種服務(wù)。

      嗯,下面開始我們愉快的HarmonyOS----服務(wù)卡片之旅吧!

      什么是原子化服務(wù)

      在學(xué)習(xí)服務(wù)卡片之前,我們先來了解一下什么是原子化服務(wù)

      原子化服務(wù)是 HarmonyOS 提供的一種面向未來的服務(wù)提供方式,是有獨立入口的(用戶可通過點擊、碰一碰、掃一掃等方式直接觸發(fā))、免安裝的(無需顯式安裝,由系統(tǒng)程序框架后臺安裝后即可使用)、可為用戶提供一個或多個便捷服務(wù)的用戶程序形態(tài)。原子化服務(wù)基于 HarmonyOS API 開發(fā),支持運行在1+8+N設(shè)備上,供用戶在合適的場景、合適的設(shè)備上便捷使用。^2

      例如:某傳統(tǒng)方式的需要安裝的“購物應(yīng)用A”,在按照原子化服務(wù)理念調(diào)整設(shè)計后,成為由“商品瀏覽”“購物車”“支付”等多個便捷服務(wù)組成的、可以免安裝的“購物原子化服務(wù)A”。

      文檔里說的有些官方,

      個人理解,原子化服務(wù)本質(zhì)還是終端應(yīng)用

      (我們先這樣理解它,后面在具體描述),一個代替終端應(yīng)用提供新服務(wù)提供方式的存在。拋去服務(wù)流轉(zhuǎn)/分享、設(shè)備控制之類的分布式能力不說,在設(shè)計上類似于微信小程序和流應(yīng)用的優(yōu)點的結(jié)合體,在入口設(shè)計等方面感覺像微信小程序一樣便捷,但是不需要載體(微信,瀏覽器等),在整體體驗方面又像流應(yīng)用一樣,不需要顯示安裝。但是(體驗應(yīng)該要好于流應(yīng)用),個人理解不談分布式能力,像是用瘦客戶的方式有了胖客戶的體驗。

      所謂原子化服務(wù),個人理解,即將原來的終端應(yīng)用以功能為粒度細(xì)化(原子化),分離成一個個服務(wù),多個服務(wù)之間通信完成需求,不在依托于具體的終端應(yīng)用。類似于一種極限的思想,細(xì)化功能粒度,逐漸減少對終端應(yīng)用總體的依賴。

      關(guān)于原子化服務(wù)的更多內(nèi)容,小伙伴移步HarmonyOS Developer:什么是原子化服務(wù)

      原子化服務(wù)具有如下基本要素

      必須設(shè)計和開發(fā):

      基礎(chǔ)信息

      服務(wù)卡片

      簡單了解一下基礎(chǔ)信息

      :即每個原子化服務(wù)有獨立的圖標(biāo)、名稱、描述、快照。基礎(chǔ)信息應(yīng)能夠準(zhǔn)確反映服務(wù)提供方的特征及服務(wù)的核心體驗,并與其他關(guān)聯(lián)的應(yīng)用和服務(wù)保持同步最新。

      其他的沒問題,我們重點看一下快照這里

      :快照為與原子化服務(wù)關(guān)聯(lián)的小尺寸服務(wù)卡片的截圖。截圖應(yīng)為理想的服務(wù)狀態(tài),讓用戶一眼可知服務(wù)內(nèi)容。需提供直角圖片,由展示快照的應(yīng)用進(jìn)行圓角裁切.,

      快照為與原子化服務(wù)關(guān)聯(lián)的小尺寸服務(wù)卡片的截圖.下面我們看看服務(wù)卡片是什么

      什么是服務(wù)卡片,存在意義。

      關(guān)于服務(wù)卡片,我們看看HarmonyOS Developer中設(shè)計文檔是怎么講的。https://developer.harmonyos.com/cn/docs/design/des-guides/service-widget-about-0000001144696239

      服務(wù)卡片介紹

      將原子化服務(wù)/應(yīng)用的重要信息以卡片的形式展示在桌面,用戶可通過快捷手勢使用卡片,通過輕量交互行為實現(xiàn)服務(wù)直達(dá)、減少層級跳轉(zhuǎn)的目的。

      這是我的手機(jī)的時鐘應(yīng)用的類似的服務(wù)卡片的這樣一個東西,嗯,這個不算是服務(wù)卡片的。和實際的卡片還是有很大差距的.

      這是P40模擬器上的時鐘,帶有服務(wù)卡片選項的,真正的服務(wù)卡片

      服務(wù)卡片的核心理念在于提供用戶容易使用且一目了然的信息內(nèi)容,將智慧化能力融入到服務(wù)卡片的體驗中供用戶選擇使用,同時滿足在不同終端設(shè)備上的展示和自適應(yīng)。

      服務(wù)卡片的構(gòu)成

      服務(wù)卡片的顯示主要由內(nèi)容主體、歸屬的 App 名稱構(gòu)成,在臨時態(tài)下會出現(xiàn)Pin鈕的操作特征,點擊按鈕用戶可快捷將·卡片固定·在桌面顯示。開發(fā)者應(yīng)該借助卡片內(nèi)容和卡片名稱清晰地向用戶傳遞所要提供的服務(wù)信息。

      嗯,關(guān)于服務(wù)卡片的其他這里不多介紹,小伙伴了解更多移步到

      官方文檔:https://developer.harmonyos.com/cn/docs/design/des-guides/service-widget-about-0000001144696239

      關(guān)于服務(wù)卡片的

      交互設(shè)計,內(nèi)容設(shè)計,視覺風(fēng)格 等都有詳細(xì)講解。

      簡單實踐-原子化服務(wù)之服務(wù)卡片初嘗試

      檢驗真理的唯一標(biāo)準(zhǔn)是實踐 ,說了這么多,編碼試一下。先做出點東西來,然后我們在慢慢研究原理。嘻嘻。

      官方給出一個java開發(fā)時鐘卡片的例子。

      官方教程文檔見Java卡片開發(fā)指導(dǎo)

      代碼見https://gitee.com/openharmony/codelabs/tree/master/ClockFACardDemo

      這里,官方的Demo很簡單,文檔很完整小伙伴一看就明白。感興趣的可以拉下來跑跑,我們看看具體的實現(xiàn)成果

      主體結(jié)構(gòu)分析

      HarmonyOS支持應(yīng)用以Ability為單位進(jìn)行部署。Ability可以分為FA(Feature Ability)和PA(Particle Ability)兩種類型

      FA:Page Ability:由上次的Holle Word我們可以知道,在HarmonyOS中,提供了Ability和·AbilitySlice·兩個基礎(chǔ)類, 一個有界面的Ability可以由一個或多個AbilitySlice構(gòu)成,AbilitySlice主要用于承載單個頁面的具體邏輯實現(xiàn)和界面UI,是應(yīng)用顯示、運行和跳轉(zhuǎn)的最小單元。所以ClockCardSlice為應(yīng)用的主界面UI.,即這個Demo中只有一個Page AbilitySlice(ClockCardSlice)。ClockCardSlice是通過setMainRoute()方法來指定,指定當(dāng)前Ability的默認(rèn)頁面。

      PA:Service Ability: 由于時鐘是需要實時更新的,所以需要Service Ability來實時運行后臺任務(wù),即TimerAbility為時鐘更新的Service Ability

      layout文件夾:頁面布局文件夾,由于時鐘卡片Codelab涉及兩個尺寸:22和24,因此需要新建兩個.xml文件用于頁面布局。

      config.json:配置文件,用于卡片和Service Ability的聲明。

      關(guān)于 Ability 的更多介紹PageAbility和ServiceAbility的生命周期回調(diào)等知識,小伙伴移步HarmonyOS Developer:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-service-lifecycle-0000000000044472

      時鐘FA卡片應(yīng)用主要設(shè)計到創(chuàng)建、更新和刪除卡片,對象關(guān)系映射型數(shù)據(jù)庫的使用以及如何啟動計時器服務(wù),卡片布局等。下面我們就這幾部分代碼分析,學(xué)習(xí)。

      HarmonyOS對象關(guān)系映射(Object Relational Mapping,ORM)數(shù)據(jù)庫是一款基于SQLite(一款輕型的數(shù)據(jù)庫,是遵守ACID的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)。)的數(shù)據(jù)庫框架,屏蔽了底層SQLite數(shù)據(jù)庫的SQL操作,針對實體和關(guān)系提供了增刪改查等一系列的面向?qū)ο蠼涌凇?yīng)用開發(fā)者不必再去編寫復(fù)雜的SQL語句, 以操作對象的形式來操作數(shù)據(jù)庫,提升效率的同時也能聚焦于業(yè)務(wù)開發(fā)。

      個人感覺和使用JPA有相同的地方,如果有使用過JPA的,那這個很容易理解。

      對象關(guān)系映射數(shù)據(jù)庫的三個主要組件:

      數(shù)據(jù)庫:被開發(fā)者用@Database注解,且繼承了OrmDatabase的類,對應(yīng)關(guān)系型數(shù)據(jù)庫。

      實體對象:被開發(fā)者用@Entity注解,且繼承了OrmObject的類,對應(yīng)關(guān)系型數(shù)據(jù)庫中的表。

      對象數(shù)據(jù)操作接口:包括數(shù)據(jù)庫操作的入口OrmContext類和謂詞接口(OrmPredicate)等。

      package com.huawei.cookbooks.database; import ohos.data.orm.OrmObject; import ohos.data.orm.annotation.Entity; import ohos.data.orm.annotation.PrimaryKey; /** * Card Table 存放對象關(guān)系映射數(shù)據(jù)庫相關(guān)對象的目錄。 */ // TODO 定義一個對象關(guān)系映射的數(shù)據(jù)表 @Entity(tableName = "form") public class Form extends OrmObject { // TODO 聲明主鍵 @PrimaryKey() private Long formId; private String formName; private Integer dimension; public Form(Long formId, String formName, Integer dimension) { this.formId = formId; this.formName = formName; this.dimension = dimension; } public Form() { } public Integer getDimension() { return dimension; } public void setDimension(Integer dimension) { this.dimension = dimension; } public Long getFormId() { return formId; } public void setFormId(Long formId) { this.formId = formId; } public String getFormName() { return formName; } public void setFormName(String formName) { this.formName = formName; } }

      package com.huawei.cookbooks.database; import ohos.data.orm.OrmDatabase; import ohos.data.orm.annotation.Database; /** * Card Database 卡片數(shù)據(jù)庫對象,用于創(chuàng)建卡片數(shù)據(jù)庫。 */ @Database( entities = {Form.class}, version = 1) public abstract class FormDatabase extends OrmDatabase { }

      創(chuàng)建數(shù)據(jù)庫:開發(fā)者需要定義一個表示數(shù)據(jù)庫的類,繼承OrmDatabase,再通過@Database注解內(nèi)的entities屬性指定哪些數(shù)據(jù)模型類(表)屬于這個數(shù)據(jù)庫。

      屬性:version:數(shù)據(jù)庫版本號。entities:數(shù)據(jù)庫內(nèi)包含的表。

      創(chuàng)建數(shù)據(jù)表。開發(fā)者可通過創(chuàng)建一個繼承了OrmObject并用@Entity注解的類,獲取數(shù)據(jù)庫實體對象,也就是表的對象。

      tableName:表名。primaryKeys:主鍵名,一個表里只能有一個主鍵,一個主鍵可以由多個字段組成。foreignKeys:外鍵列表。indices:索引列表

      建立數(shù)據(jù)庫連接

      提供對數(shù)據(jù)庫相關(guān)操作的方法

      package com.huawei.cookbooks.utils; import com.huawei.cookbooks.database.Form; import ohos.data.orm.OrmContext; import ohos.data.orm.OrmPredicates; import java.util.List; /** * Card Database Operations 提供對數(shù)據(jù)庫相關(guān)操作的方法 */ public class DatabaseUtils { /** * delete data * * @param formId form id * @param connect data connection */ public static void deleteFormData(long formId, OrmContext connect) { OrmPredicates where = connect.where(Form.class); where.equalTo("formId", formId); List

      query = connect.query(where); if (!query.isEmpty()) { connect.delete(query.get(0)); connect.flush(); } } /** * add card info * * @param form card object * @param connect data connection */ public static void insertForm(Form form, OrmContext connect) { connect.insert(form); connect.flush(); } }

      對于對象關(guān)系映射型數(shù)據(jù)庫的學(xué)習(xí),小伙伴移步HarmonyOS Developer學(xué)習(xí),這個不在多介紹:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/database-orm-guidelines-0000000000030063

      我們想看看官方文檔里怎么說。創(chuàng)建一個FormAbility,覆寫卡片相關(guān)回調(diào)函數(shù)。

      onCreateForm(Intent intent)

      onUpdateForm(long formId)

      onDeleteForm(long formId)

      onCastTempForm(long formId)

      onEventNotify(Map formEvents)

      在onCreateForm(Intent intent)中,當(dāng)卡片使用方請求獲取卡片時,卡片提供方會被拉起并調(diào)用onCreateForm(Intent intent)回調(diào),intent中會帶有卡片ID,卡片名稱,臨時卡片標(biāo)記和卡片外觀規(guī)格信息,分別通過

      AbilitySlice.PARAM_FORM_IDENTITY_KEY、

      AbilitySlice.PARAM_FORM_NAME_KEY、

      AbilitySlice.PARAM_FORM_TEMORARY_KEY和

      AbilitySlice.PARAM_FORM_DIMENSION_KEY按需獲取。

      提供方可以通過AbilitySlice.PARAM_FORM_CUSTOMIZE_KEY獲取卡片使用方設(shè)置的自定義數(shù)據(jù)。

      卡片使用方:顯示卡片內(nèi)容的宿主應(yīng)用,控制卡片在宿主中展示的位置

      卡片提供方:提供卡片顯示內(nèi)容的HarmonyOS Service/HarmonyOS Application,控制卡片的顯示內(nèi)容、控件布局以及控件點擊事件

      public class FormAbility extends Ability { ...... @Override public void onStart(Intent intent) { super.onStart(intent); ...... } @Override protected ProviderFormInfo onCreateForm(Intent intent) { long formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, 0); String formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY); int specificationId = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, 0); boolean tempFlag = intent.getBooleanParam(AbilitySlice.PARAM_FORM_TEMPORARY_KEY, false); // 獲取自定義數(shù)據(jù) IntentParams intentParams = intent.getParam(AbilitySlice.PARAM_FORM_CUSTOMIZE_KEY); HiLog.info(LABEL_LOG, "onCreateForm: " + formId + " " + formName + " " + specificationId); // 開發(fā)者需要根據(jù)卡片的名稱以及外觀規(guī)格獲取對應(yīng)的xml布局并構(gòu)造卡片對象,此處ResourceTable.Layout_form_ability_layout_2_2僅為示例 ProviderFormInfo formInfo = new ProviderFormInfo(ResourceTable.Layout_form_ability_layout_2_2, this); // 獲取卡片信息 String formData = getInitFormData(formName, specificationId); ComponentProvider componentProvider = new ComponentProvider(); componentProvider.setText(ResourceTable.Id_title, "formData-" + formData); formInfo.mergeActions(componentProvider); ...... HiLog.info(LABEL_LOG, "onCreateForm finish......."); return formInfo; } @Override protected void onDeleteForm(long formId) { super.onDeleteForm(formId); // 刪除卡片實例數(shù)據(jù),需要由開發(fā)者實現(xiàn) deleteFormInfo(formId); ...... } @Override // 若卡片支持定時更新/定點更新/卡片使用方主動請求更新功能,則提供方需要覆寫該方法以支持?jǐn)?shù)據(jù)更新 protected void onUpdateForm(long formId) { super.onUpdateForm(formId); // 更新卡片信息,由開發(fā)者實現(xiàn) ...... } @Override protected void onCastTempForm(long formId) { // 使用方將臨時卡片轉(zhuǎn)換為常態(tài)卡片觸發(fā),提供方需要做相應(yīng)的處理,將數(shù)據(jù)持久化。 super.onCastTempForm (formId); ...... } @Override protected void onEventNotify(Map formEvents) { // 使用方發(fā)起可見或者不可見通知觸發(fā),提供方需要做相應(yīng)的處理,比如卡片可見時刷新卡片,僅系統(tǒng)應(yīng)用能收到該回調(diào)。 super.onEventNotify(formEvents); ...... } }

      了解更多見Java卡片開發(fā)指導(dǎo):https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ability-service-widget-provider-java-0000001104082220

      我們看看Demo里怎么寫的:覆蓋了

      onDeleteForm(當(dāng)卡片被刪除時,需要重寫onDeleteForm方法,根據(jù)卡片id刪除卡片實例數(shù)據(jù):)和

      onCreateForm·(當(dāng)卡片使用方請求獲取卡片時,卡片提供方會被拉起并調(diào)用onCreateForm回調(diào)函數(shù),完成卡片信息的初始化)

      package com.huawei.cookbooks; import com.huawei.cookbooks.database.Form; import com.huawei.cookbooks.database.FormDatabase; import com.huawei.cookbooks.slice.ClockCardSlice; import com.huawei.cookbooks.utils.ComponentProviderUtils; import com.huawei.cookbooks.utils.DatabaseUtils; import ohos.aafwk.ability.Ability; import ohos.aafwk.ability.AbilitySlice; import ohos.aafwk.ability.ProviderFormInfo; import ohos.aafwk.content.Intent; import ohos.aafwk.content.Operation; import ohos.agp.components.ComponentProvider; import ohos.data.DatabaseHelper; import ohos.data.orm.OrmContext; import ohos.hiviewdfx.HiLog; import ohos.hiviewdfx.HiLogLabel; /** * Card Main Ability 主程序入口,由DevEco Studio生成,開發(fā)者需要重寫創(chuàng)建、刪除卡片等方法。 */ public class MainAbility extends Ability { private static final HiLogLabel LABEL_LOG = new HiLogLabel(0, 0, "com.huawei.cookbooks.MainAbility"); // TODO 卡片一 private static final int DEFAULT_DIMENSION_2X2 = 2; // TODO 卡片二 private static final int DEFAULT_DIMENSION_2X4 = 3; private static final String EMPTY_STRING = ""; private static final int INVALID_FORM_ID = -1; private long formId; // TODO 卡片對象 private ProviderFormInfo formInfo; // TODO 數(shù)據(jù)庫服務(wù)對象 private DatabaseHelper helper = new DatabaseHelper(this); // TODO 數(shù)據(jù)庫連接對象 private OrmContext connect; /** * TODO 項目啟動方法,啟動定時服務(wù) * @param intent */ @Override public void onStart(Intent intent) { super.onStart(intent); // TODO 建立數(shù)據(jù)庫連接 connect = helper.getOrmContext("FormDatabase", "FormDatabase.db", FormDatabase.class); // TODO 啟動TimerAbility Intent intentService = new Intent(); Operation operation = new Intent.OperationBuilder() .withDeviceId("") .withBundleName(getBundleName()) .withAbilityName(TimerAbility.class.getName()) .build(); intentService.setOperation(operation); startAbility(intentService); super.setMainRoute(ClockCardSlice.class.getName()); } //當(dāng)卡片使用方請求獲取卡片時,卡片提供方會被拉起并調(diào)用onCreateForm回調(diào)函數(shù),完成卡片信息的初始化 @Override protected ProviderFormInfo onCreateForm(Intent intent) { if (intent == null) { return new ProviderFormInfo(); } // 獲取卡片id formId = INVALID_FORM_ID; if (intent.hasParameter(AbilitySlice.PARAM_FORM_IDENTITY_KEY)) { formId = intent.getLongParam(AbilitySlice.PARAM_FORM_IDENTITY_KEY, INVALID_FORM_ID); } else { return new ProviderFormInfo(); } // 獲取卡片名稱 String formName = EMPTY_STRING; if (intent.hasParameter(AbilitySlice.PARAM_FORM_NAME_KEY)) { formName = intent.getStringParam(AbilitySlice.PARAM_FORM_NAME_KEY); } // 獲取卡片規(guī)格 int dimension = DEFAULT_DIMENSION_2X2; if (intent.hasParameter(AbilitySlice.PARAM_FORM_DIMENSION_KEY)) { dimension = intent.getIntParam(AbilitySlice.PARAM_FORM_DIMENSION_KEY, DEFAULT_DIMENSION_2X2); } int layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_2; if (dimension == DEFAULT_DIMENSION_2X4) { layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_4; } formInfo = new ProviderFormInfo(layoutId, this); // 存儲卡片信息 Form form = new Form(formId, formName, dimension); ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(form, this); formInfo.mergeActions(componentProvider); if (connect == null) { connect = helper.getOrmContext("FormDatabase", "FormDatabase.db", FormDatabase.class); } try { DatabaseUtils.insertForm(form, connect); } catch (Exception e) { DatabaseUtils.deleteFormData(form.getFormId(), connect); } return formInfo; } // todo 當(dāng)卡片被刪除時,需要重寫onDeleteForm方法,根據(jù)卡片id刪除卡片實例數(shù)據(jù): @Override protected void onDeleteForm(long formId) { super.onDeleteForm(formId); // 刪除數(shù)據(jù)庫中的卡片信息 DatabaseUtils.deleteFormData(formId, connect); } }

      卡片數(shù)據(jù)服務(wù):為了方便處理·時鐘卡片刷新的定時任務(wù),我們創(chuàng)建了一個Service Ability,定時去更新卡片信息,在TimerAbility.java中

      package com.huawei.cookbooks; import com.huawei.cookbooks.database.Form; import com.huawei.cookbooks.database.FormDatabase; import com.huawei.cookbooks.utils.ComponentProviderUtils; import com.huawei.cookbooks.utils.DatabaseUtils; import ohos.aafwk.ability.Ability; import ohos.aafwk.ability.FormException; import ohos.aafwk.content.Intent; import ohos.agp.components.ComponentProvider; import ohos.data.DatabaseHelper; import ohos.data.orm.OrmContext; import ohos.data.orm.OrmPredicates; import ohos.hiviewdfx.HiLog; import ohos.hiviewdfx.HiLogLabel; import ohos.rpc.IRemoteObject; import java.util.List; import java.util.Timer; import java.util.TimerTask; /** * Time PA 時鐘更新Service Ability。 */ public class TimerAbility extends Ability { private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "Demo"); private static final long SEND_PERIOD = 1000L; private DatabaseHelper helper = new DatabaseHelper(this); private OrmContext connect; @Override public void onStart(Intent intent) { HiLog.info(LABEL_LOG, "TimerAbility::onStart"); connect = helper.getOrmContext("FormDatabase", "FormDatabase.db", FormDatabase.class); startTimer(); super.onStart(intent); } // 卡片更新定時器,每秒更新一次 private void startTimer() { Timer timer = new Timer(); timer.schedule( new TimerTask() { @Override public void run() { updateForms(); } }, 0, SEND_PERIOD); } private void updateForms() { // 從數(shù)據(jù)庫中獲取卡片信息 OrmPredicates ormPredicates = new OrmPredicates(Form.class); List formList = connect.query(ormPredicates); // 更新時分秒 if (formList.size() <= 0) { return; } for (Form form : formList) { // 遍歷卡片列表更新卡片 ComponentProvider componentProvider = ComponentProviderUtils.getComponentProvider(form, this); try { Long updateFormId = form.getFormId(); updateForm(updateFormId, componentProvider); } catch (FormException e) { // 刪除不存在的卡片 DatabaseUtils.deleteFormData(form.getFormId(), connect); HiLog.error(LABEL_LOG, "onUpdateForm updateForm error"); } } } @Override public void onBackground() { super.onBackground(); HiLog.info(LABEL_LOG, "TimerAbility::onBackground"); } @Override public void onStop() { super.onStop(); HiLog.info(LABEL_LOG, "TimerAbility::onStop"); } }

      有關(guān)卡片組件的更新,封裝了ComponentProviderUtils這個類,在卡片更新時候,通過調(diào)用updateForm方法,傳入?yún)?shù)formId和componentProvider.

      package com.huawei.cookbooks.utils; import com.huawei.cookbooks.ResourceTable; import com.huawei.cookbooks.database.Form; import ohos.agp.components.ComponentProvider; import ohos.agp.utils.Color; import ohos.app.Context; import java.util.Calendar; /** * Component ProviderUtils 提供獲取ComponentProvider對象的方法,用于卡片組件的更新。 */ public class ComponentProviderUtils { // 當(dāng)前星期顏色 private static Color nowWeekColor = new Color(Color.rgb(255, 245, 238)); // 原色星期 private static Color primaryWeekColor = new Color(Color.rgb(192, 192, 192)); private static final int WEEK_DAYS = 7; private static final int STRING_LENGTH = 2; private static final int DIM_VERSION = 3; private static final int SUNDAY = 1; private static final int MONDAY = 2; private static final int TUESDAY = 3; private static final int WEDNESDAY = 4; private static final int THURSDAY = 5; private static final int FRIDAY = 6; private static final int SATURDAY = 7; /** * Obtain the day of the week * * @return week */ public static int getWeekDayId() { Calendar calendar = Calendar.getInstance(); int week = calendar.get(Calendar.DAY_OF_WEEK); int result = getWeekIdResult(week); return result; } /** * get week component id * * @param week week * @return component id */ private static int getWeekIdResult(int week) { int result = ResourceTable.Id_mon; switch (week) { case SUNDAY: result = ResourceTable.Id_sun; break; case MONDAY: result = ResourceTable.Id_mon; break; case TUESDAY: result = ResourceTable.Id_tue; break; case WEDNESDAY: result = ResourceTable.Id_wed; break; case THURSDAY: result = ResourceTable.Id_thu; break; case FRIDAY: result = ResourceTable.Id_fri; break; case SATURDAY: result = ResourceTable.Id_sat; break; default: result = ResourceTable.Id_sun; break; } return result; } /** * Obtains the ComponentProvider object * * @param form form info * @param context context * @return component provider */ public static ComponentProvider getComponentProvider(Form form, Context context) { int layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_2; if (form.getDimension() == DIM_VERSION) { layoutId = ResourceTable.Layout_form_image_with_info_date_card_2_4; } ComponentProvider componentProvider = new ComponentProvider(layoutId, context); setComponentProviderValue(componentProvider); return componentProvider; } /** * Time converted to string * * @param time time * @return time string */ private static String int2String(int time) { String timeString; if (String.valueOf(time).length() < STRING_LENGTH) { timeString = "0" + time; } else { timeString = time + ""; } return timeString; } /** * Set the value of componentProvider * * @param componentProvider component provider */ private static void setComponentProviderValue(ComponentProvider componentProvider) { Calendar now = Calendar.getInstance(); int hour = now.get(Calendar.HOUR_OF_DAY); int min = now.get(Calendar.MINUTE); int second = now.get(Calendar.SECOND); String hourString = int2String(hour); String minString = int2String(min); String secondString = int2String(second); componentProvider.setText(ResourceTable.Id_date, DateUtils.getCurrentDate("yyyy-MM-dd")); componentProvider.setText(ResourceTable.Id_hour, hourString); componentProvider.setText(ResourceTable.Id_min, minString); componentProvider.setText(ResourceTable.Id_sec, secondString); // 獲取當(dāng)前星期 int weekDayId = getWeekDayId(); componentProvider.setTextColor(weekDayId, nowWeekColor); // 將前一天的星期改回原色 int lastWeekId = getLastWeekDayId(); componentProvider.setTextColor(lastWeekId, primaryWeekColor); } /** * obtain previous day of the week * * @return previous day of the week */ public static int getLastWeekDayId() { Calendar calendar = Calendar.getInstance(); int week = calendar.get(Calendar.DAY_OF_WEEK); int lastWeek; if (week == 1) { lastWeek = WEEK_DAYS; } else { lastWeek = week - 1; } return getWeekIdResult(lastWeek); } }

      對于服務(wù)卡片我們就先學(xué)的這里,學(xué)完了Demo,小伙伴是不是蠢蠢欲動呢?自己寫一個類似的Demo應(yīng)該是在可能完成的范圍之內(nèi)的,感興趣的小伙伴趕快試試吧。之后我準(zhǔn)備基于官方的一個Demo(一個音頻播放器),搞一個服務(wù)卡片。嗯,時間關(guān)系,今天就先搞到這里。

      【本文正在參與“有獎?wù)魑?| HarmonyOS征文大賽”活動】

      開發(fā)者 數(shù)據(jù)庫

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:公有云基本概念系列——區(qū)域&可用區(qū)
      下一篇:MongoDB 創(chuàng)建數(shù)據(jù)庫
      相關(guān)文章
      亚洲一二成人精品区| 亚洲一区二区三区在线播放| 99亚洲精品卡2卡三卡4卡2卡| 亚洲日韩图片专区第1页| 国产精品亚洲二区在线观看 | 欧洲 亚洲 国产图片综合| 亚洲国产综合第一精品小说| 久久精品国产亚洲AV电影| 亚洲AV色香蕉一区二区| 久久综合九九亚洲一区| 亚洲五月综合缴情在线观看| 亚洲人成人网站色www| 337p日本欧洲亚洲大胆裸体艺术| 亚洲国产精品成人一区| 亚洲AV无码一区二三区| 亚洲成片观看四虎永久| 亚洲性久久久影院| 国产aⅴ无码专区亚洲av麻豆| 亚洲伊人久久综合影院| 亚洲人成无码网站久久99热国产| 伊人久久精品亚洲午夜| 亚洲人成伊人成综合网久久久| 曰韩亚洲av人人夜夜澡人人爽| 亚洲精品无码午夜福利中文字幕| 亚洲gv白嫩小受在线观看| 水蜜桃亚洲一二三四在线| 91亚洲国产在人线播放午夜| 亚洲黄色在线观看网站| 亚洲精品中文字幕无乱码| 亚洲综合伊人制服丝袜美腿| 亚洲成a人无码亚洲成av无码| 蜜臀亚洲AV无码精品国产午夜.| 亚洲精品国产福利一二区| 亚洲精品一品区二品区三品区| 亚洲av无码潮喷在线观看| 亚洲毛片免费视频| 亚洲熟妇av午夜无码不卡| 国产成人不卡亚洲精品91| 中文亚洲AV片不卡在线观看| 亚洲av之男人的天堂网站| 亚洲视频一区在线播放|