
https://developer.huawei.com/consumer/cn/forum/communityHome
近期(4.12 ~ 4.25)鴻蒙OS正在舉行開發者榷訓動,趁機參加并了解一下鴻蒙OS的現狀和當前的應用開發體驗,
1. 開發環境搭建
下載安裝IDE(當前版本:2.1 Beta3)
華為為Harmony應用開發提供了配套的IDE:DevEco Studio(內心比較排斥這種帶Eco字眼兒的命名,PPT怎么吹無所謂,開發工具咱能不能務實一點兒?)
下載IDE需要登錄Huawei賬號,我安裝的是Mac版,下載后的安裝程序還是比較順暢的

啟動界面顯示DevEco Studio仍然是基于IntelliJ的定制IDE
下載SDK
跟Android一樣,IDE啟動第一件事情是下載Harmony SDK

每個版本的SDK中都提供了三套API用來開發Java、Js、C++代碼,版本上需要保持一致,
不同的華為設備對SDK版本有不同要求,比如在測驗中發現,我的API4的代碼無法運行在P40上,改為API5就OK了
關于SDK原始碼
需要注意,目前無法通過SDKManager打包下載原始碼,原始碼需要通過gitee單獨下載
https://gitee.com/openharmony
這為代碼除錯帶來障礙,不知道后期是否可以像Andoird那樣與SDK一起打包下載原始碼
創建專案
Harmony主打多端協同,所以很重視設備多樣性,可面向不同設備創建模板專案

相比AndroidStudio,Harmony提供了更加豐富的專案模板,模板中除了UI以外還提供了部分資料層代碼,基本上是一個可以二次開發的APP,

2. 鴻蒙專案結構
IDE界面
試著創建了一個News Feature Ability(新聞流)的模板專案,成功在IDE中打開:

IDE視窗與AndroidStudio類似,值得一提的Harmony右邊提供的Preview視窗,可以對xml或者Ablitiy檔案進行預覽,有點Compose的Preview的感覺,但是只能靜態預覽,無法互動
工程檔案

工程檔案和Android類似,甚至可以找到一一對應的關系
| Harmony | Android | 說明 |
|---|---|---|
| entry | app | 默認啟動模塊(主模塊),相當于app_module |
| MyApplication | XXXApplication | 鴻蒙的MyApplication是AbilityPackage的子類 |
| MainAbility | MainActivity | 入口頁,鴻蒙中將四大組件的概念統一成Ability |
| MainAbilityListSlice | XXXFragment | Slice類似Fragment,UI的基本組成單元 |
| Component | View | Component類相當于View,后文介紹 |
| config.json | AndroidManifest.xml | 鴻蒙使用json替代xml進行Manifest配置,配置專案差不多 |
| resources/base/… | res/… | 包括Layout檔案在內的各種資源檔案依舊使用xml |
| resources/rawfile/ | assets/ | rawfile存盤任意格式原始資源,相當于assets |
| build.gradle | build.gradle | 編譯腳本,兩者一樣 |
| build/outpus/…/*.hap | build/outputs/…/*.apk | 鴻蒙的產物是hap(harmony application package) 解壓后里面有一個同名的.apk檔案, 這后續是因為鴻蒙需要同時支持apk安裝的兼容方案 |
Ability
Ability是應用所具備能力的抽象,Harmony支持應用以Ability為單位進行部署,一個應用由一個或多個FA(Feature Ability)或PA(Particle Ability)組成,FA有UI界面,提供與用戶互動的能力;而PA無UI界面,提供后臺運行任務的能力以及統一的資料訪問抽象
- FA支持Page Ability:
- Page Ability用于提供與用戶互動的能力,一個Page可以由一個或多個AbilitySlice構成,AbilitySlice之間可以進行頁面導航

- PA支持Service Ability和Data Ability:
- Service Ability:用于提供后臺運行任務的能力,
- Data Ability:用于對外部提供統一的資料訪問抽象,
可以感覺到,各種Ability可以對照Android的四大組件來理解
| Harmony | Android |
|---|---|
| Page Ability (FA) | Activity |
| Service Ability (PA) | Service |
| Data Ability(PA) | ContentProvider |
| AbilitySlice | Fragment |
代碼一覽
MainAbility
以預置的News Feature Ability為例子,這是一個擁有兩個Slice的Page Ability,通過Router注冊兩個Slice
public class MainAbility extends Ability {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setMainRoute(MainAbilityListSlice.class.getName()); //添加路由:ListSlice
addActionRoute("action.detail", MainAbilityDetailSlice.class.getName());//DetailSlice
...
}
}
以下是在模擬器中運行兩個Slice的頁面效果
| MainAbilityListSlice | MainAbilityDetailSlice |
|---|---|
![]() | ![]() |
MainAbilityListSlice
主要看一下串列的顯示邏輯
public class MainAbilityListSlice extends AbilitySlice {
...
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_news_list_layout);
initView();
initData(); //加載資料
initListener();
newsListContainer.setItemProvider(newsListAdapter); //Adatper設定到View
newsListAdapter.notifyDataChanged(); //重繪資料
}
private void initListener() {
newsListContainer.setItemClickedListener((listContainer, component, i, l) -> {
//路由跳轉"action.detail"
LogUtil.info(TAG, "onItemClicked is called");
Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
.withBundleName(getBundleName())
.withAbilityName("com.example.myapplication.MainAbility")
.withAction("action.detail")
.build();
intent.setOperation(operation);
startAbility(intent);
});
}
private void initData() {
...
totalNewsDatas = new ArrayList<>();
newsDatas = new ArrayList<>();
initNewsData();//填充newsDatas
newsListAdapter = new NewsListAdapter(newsDatas, this);//設定到Adapter
}
...
}
類似ListView的用法,通過Adatper加載資料; setItemClickedListener中通過路由跳轉MainAbilityDetailSlice,
Layout_news_list_layout布局檔案定義如下,ListContainer即ListView,是Comopnent的一個子類,Component就是HarmonyOS中的View
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<ListContainer
ohos:id="$+id:selector_list"
ohos:height="40vp"
ohos:width="match_parent"
ohos:orientation="horizontal"
/>
<Component
ohos:height="0.5vp"
ohos:width="match_parent"
ohos:background_element="#EAEAEC"
/>
<ListContainer
ohos:id="$+id:news_container"
ohos:height="match_parent"
ohos:width="match_parent"/>
</DirectionalLayout>
看一下Adapter的實作, 繼承自BaseItemProvider
/**
* News list adapter
*/
public class NewsListAdapter extends BaseItemProvider {
private List<NewsInfo> newsInfoList;
private Context context;
public NewsListAdapter(List<NewsInfo> listBasicInfo, Context context) {
this.newsInfoList = listBasicInfo;
this.context = context;
}
@Override
public int getCount() {
return newsInfoList == null ? 0 : newsInfoList.size();
}
@Override
public Object getItem(int position) {
return Optional.of(this.newsInfoList.get(position));
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public Component getComponent(int position, Component componentP, ComponentContainer componentContainer) {
ViewHolder viewHolder = null;
Component component = componentP;
if (component == null) {
component = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_news_layout, null, false);
viewHolder = new ViewHolder();
Component componentTitle = component.findComponentById(ResourceTable.Id_item_news_title);
Component componentImage = component.findComponentById(ResourceTable.Id_item_news_image);
if (componentTitle instanceof Text) {
viewHolder.title = (Text) componentTitle;
}
if (componentImage instanceof Image) {
viewHolder.image = (Image) componentImage;
}
component.setTag(viewHolder);
} else {
if (component.getTag() instanceof ViewHolder) {
viewHolder = (ViewHolder) component.getTag();
}
}
if (null != viewHolder) {
viewHolder.title.setText(newsInfoList.get(position).getTitle());
viewHolder.image.setScaleMode(Image.ScaleMode.STRETCH);
}
return component;
}
/**
* ViewHolder which has title and image
*/
private static class ViewHolder {
Text title;
Image image;
}
}
基本上就是標準的ListAdatper,把View替換成Component而已,
關于模擬器
代碼完成后可以再模擬器中運行,關于模擬器有幾點想說的:
-
Harmony的模擬器啟動非常快,無需下載鏡像,因為這個模擬器并非本地運行,而只是一個遠端設備的VNC,因此必須在線使用,而且不夠流暢時有丟幀現象,雖然真機除錯效果更好,但不是人人都買得起P40的
-
模擬器嵌入到IDE視窗顯示(像Preview視窗一樣),非獨立視窗,這會帶來一個問題,當同時打開多個IDE時,模擬器可能會顯示在另一個IDE中(就像Logcat跑偏一樣),
-
想使用模擬器必須進過開發者認證,官方推薦使用銀行卡認證,模擬器遠端鏈接的是一臺真實設備,難道是為未來租用設備要計費??
記得以前看過一篇文章,如果是來自國外地區的注冊賬號可以免認證使用模擬器,但是懶得折騰了
3. 開發JS應用
除了Java,鴻蒙還支持基于JS開發應用,借助前端技術完善其跨平臺能力,
鴻蒙為JS工程提供了多種常用UI組件,但是沒有采用當下主流的react、vue那樣JS組件,仍然是基于CSS3/HTML5/JS這種傳統方式進行開發,JS工程結構如下

| 目錄 | 說明 |
|---|---|
| common | 可選,用于存放公共資源檔案,如媒體資源、自定義組件和JS檔案等 |
| i18n | 可選,用于存放多語言的json檔案 |
| pages/index/index.hml | hml檔案定義了頁面的布局結構,使用到的組件,以及這些組件的層級關系 |
| pages/index/index.css | css檔案定義了頁面的樣式與布局,包含樣式選擇器和各種樣式屬性等 |
| pages/index/index.js | js檔案描述了頁面的行為邏輯,此檔案里定義了頁面里所用到的所有的邏輯關系,比如資料、事件等 |
| resources | 可選,用于存放資源組態檔,比如:全域樣式、多解析度加載等組態檔 |
| app.js | 全域的JavaScript邏輯檔案和應用的生命周期管理, |
4. 跨設備遷移
通過前面的介紹,可能感覺和Android大同小異,但是HarmonyOS最牛逼之處是多端協作能力,例如可以將Page在同一用戶的不同設備間遷移,實作無縫切換,
以Page從設備A遷移到設備B為例,遷移動作主要步驟如下:
- 設備A上的Page請求遷移,
- HarmonyOS回呼設備A上Page的保存資料方法,用于保存遷移必須的資料,
- HarmonyOS在設備B上啟動同一個Page,并回呼其恢復資料方法,
通過呼叫continueAbility()請求遷移,如下,獲取設備串列,配對成功后請求遷移
doConnectImg.setClickedListener(
clickedView -> {
// 通過FLAG_GET_ONLINE_DEVICE標記獲得在線設備串列
List deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
if (deviceInfoList.size() < 1) {
WidgetHelper.showTips(this, "無在網設備");
} else {
DeviceSelectDialog dialog = new DeviceSelectDialog(this);
// 點擊后遷移到指定設備
dialog.setListener(
deviceInfo -> {
LogUtil.debug(TAG, deviceInfo.getDeviceName());
LogUtil.info(TAG, "continue button click");
try {
// 開始任務遷移
continueAbility();
LogUtil.info(TAG, "continue button click end");
} catch (IllegalStateException | UnsupportedOperationException e) {
WidgetHelper.showTips(this, ResourceTable.String_tips_mail_continue_failed);
}
dialog.hide();
});
dialog.show();
}
});
Page遷移涉及到資料傳遞,此時需要借助IAbilityContinuation進行通信,
跨設備通信 IAbilityContinuation
跨設備遷移的Page需要實作IAbilityContinuation介面,
Note: 一個應用可能包含多個Page,僅需要在支持遷移的Page中通過以下方法實作IAbilityContinuation介面,同時,此Page所包含的所有AbilitySlice也需要實作此介面,
public class MainAbility extends Ability implements IAbilityContinuation {
...
@Override
public void onCompleteContinuation(int code) {}
@Override
public boolean onRestoreData(IntentParams params) {
return true;
}
@Override
public boolean onSaveData(IntentParams params) {
return true;
}
@Override
public boolean onStartContinuation() {
return true;
}
}
public class MailEditSlice extends AbilitySlice implements IAbilityContinuation {
...
@Override
public boolean onStartContinuation() {
LogUtil.info(TAG, "is start continue");
return true;
}
@Override
public boolean onSaveData(IntentParams params) {
...
LogUtil.info(TAG, "begin onSaveData:" + mailData);
...
LogUtil.info(TAG, "end onSaveData");
return true;
}
@Override
public boolean onRestoreData(IntentParams params) {
LogUtil.info(TAG, "begin onRestoreData");
...
LogUtil.info(TAG, "end onRestoreData, mail data: " + cachedMailData);
return true;
}
@Override
public void onCompleteContinuation(int i) {
LogUtil.info(TAG, "onCompleteContinuation");
terminateAbility();
}
}
-
onStartContinuation(): Page請求遷移后,系統首先回呼此方法,開發者可以在此回呼中決策當前是否可以執行遷移,比如,彈框讓用戶確認是否開始遷移,
-
onSaveData(): 如果onStartContinuation()回傳true,則系統回呼此方法,開發者在此回呼中保存必須傳遞到另外設備上以便恢復Page狀態的資料,
-
onRestoreData(): 源側設備上Page完成保存資料后,系統在目標側設備上回呼此方法,開發者在此回呼中接受用于恢復Page狀態的資料,注意,在目標側設備上的Page會重新啟動其生命周期,無論其啟動模式如何配置,且系統回呼此方法的時機在onStart()之前,
-
onCompleteContinuation(): 目標側設備上恢復資料一旦完成,系統就會在源側設備上回呼Page的此方法,以便通知應用遷移流程已結束,開發者可以在此檢查遷移結果是否成功,并在此處理遷移結束的動作,例如,應用可以在遷移完成后終止自身生命周期,
以Page從設備A遷移到設備B為例,詳細的流程如下:
- 設備A上的Page請求遷移,
- 系統回呼設備A上Page及其AbilitySlice堆疊中所有AbilitySlice實體的
IAbilityContinuation.onStartContinuation()方法,以確認當前是否可以立即遷移, - 如果可以立即遷移,則系統回呼設備A上Page及其AbilitySlice堆疊中所有AbilitySlice實體的
IAbilityContinuation.onSaveData()方法,以便保存遷移后恢復狀態必須的資料, - 如果保存資料成功,則系統在設備B上啟動同一個Page,并恢復AbilitySlice堆疊,然后回呼
IAbilityContinuation.onRestoreData()方法,傳遞此前保存的資料;此后設備B上此Page從onStart()開始其生命周期回呼, - 系統回呼設備A上Page及其AbilitySlice堆疊中所有AbilitySlice實體的
IAbilityContinuation.onCompleteContinuation()方法,通知資料恢復成功與否,
5. 總結和感想
- 從SDK到IDE與Android都高度相似,任何Android開發者基本上就是一個準鴻蒙程式員
- AndroidStudio的功能迭代很快,DevEco Studio在功能上還存在較大差距
- 需要實名認證開發者之后才能使用IDE的各種完整功能,內心抗拒
- 原始碼需要另外下載,對除錯不友好
- 當前還不支持Kotlin,大勢所趨,所以未來一定會支持,而且Kotlin是開源的問題不大
- JS UI框架的技術架構同樣有些過時
- 殺手锏是對多端協作的支持,但這可能需要更多的廠商加入才能真正發揮威力
感想: 目前人們對于鴻蒙的態度呈現兩極化:有的人追捧有的人貶低,我覺得都大可不必,讓我們多給鴻蒙一些空間和耐心,靜觀其變、樂見其成,當然這首選需要華為做到自己不主動炒作,真正靜下心來打磨鴻蒙,只要華為有決心有耐心,作為開發者的我們為什么不支持呢?
Harmony線上挑戰賽
伴隨開發者活動日,鴻蒙還舉辦了多輪線上挑戰賽活動(目前還在進行中),難度不高參與即能完成,且中獎率很高(親測),有興趣可以參與一下,希望我的好運傳遞給你
活動地址

相關鏈接
- HarmonyOS官方 https://www.harmonyos.com/cn/home
- HarmonyOS開發者檔案 https://developer.harmonyos.com/cn/documentation
- HarmonyOS原始碼 https://gitee.com/openharmony
- HarmonyOS課程 https://developer.huawei.com/consumer/cn/training/paths/c1a74183-8245-4208-9c45-fa79367f06b2
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/278512.html
標籤:其他
上一篇:TCP ,你丫的終于來了!!!


