近期(4.12 ~ 4.25)鴻蒙OS正在舉行開發者日活動,趁機參加並瞭解一下鴻蒙OS的現狀和應用開發體驗。 developer.huawei.com/consumer/cn…css
華爲爲Harmony應用開發提供了配套的IDE:DevEco Studio(心裏比較排斥這種帶Eco字眼兒的命名,PPT怎麼吹無所謂,開發工具咱能不能務實一點兒?)前端
下載IDE須要登陸Huawei帳號,我安裝的是Mac版,下載後的安裝過程仍是比較順暢的vue
啓動界面顯示DevEco Studio仍然是基於IntelliJ的定製IDEjava
跟Android同樣,IDE啓動第一件事情是下載Harmony SDKreact
每一個版本的SDK中都提供了三套API用來開發Java、Js、C++代碼,版本上須要保持一致。 不一樣的華爲設備對SDK版本有不一樣要求,好比在測試中發現,個人API4的代碼沒法運行在P40上,改成API5就OK了git
須要注意,目前沒法經過SDKManager打包下載源碼,源碼須要經過gitee單獨下載程序員
這爲代碼調試帶來障礙,不知道後期是否能夠像Andoird那樣與SDK一塊兒打包下載源碼json
Harmony主打多端協同,因此很重視設備多樣性,可面向不一樣設備建立模板項目markdown
相比AndroidStudio,Harmony提供了更加豐富的項目模板,模板中除了UI之外還提供了部分數據層代碼,基本上是一個能夠二次開發的APP。
試着建立了一個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是應用所具有能力的抽象,Harmony支持應用以Ability爲單位進行部署。一個應用由一個或多個FA(Feature Ability)或PA(Particle Ability)組成。FA有UI界面,提供與用戶交互的能力;而PA無UI界面,提供後臺運行任務的能力以及統一的數據訪問抽象
能夠感受到,各類Ability能夠對照Android的四大組件來理解
Harmony | Android |
---|---|
Page Ability (FA) | Activity |
Service Ability (PA) | Service |
Data Ability(PA) | ContentProvider |
AbilitySlice | Fragment |
以預置的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 |
---|---|
主要看一下列表的顯示邏輯
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跑偏同樣)。
想使用模擬器必須進過開發者認證,官方推薦使用銀行卡認證。模擬器遠端連接的是一臺真實設備,難道是爲將來租用設備要計費??記得之前看過一篇文章,若是是來自國外地區的註冊帳號能夠免認證使用模擬器,可是懶得折騰了
除了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邏輯文件和應用的生命週期管理。 |
經過前面的介紹,可能感受和Android大同小異,可是HarmonyOS最牛逼之處是多端協做能力,例如能夠將Page在同一用戶的不一樣設備間遷移,實現無縫切換。
以Page從設備A遷移到設備B爲例,遷移動做主要步驟以下:
經過調用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
進行通訊。
跨設備遷移的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爲例,詳細的流程以下:
IAbilityContinuation.onStartContinuation()
方法,以確認當前是否能夠當即遷移。IAbilityContinuation.onSaveData()
方法,以便保存遷移後恢復狀態必須的數據。IAbilityContinuation.onRestoreData()
方法,傳遞此前保存的數據;此後設備B上此Page從onStart()
開始其生命週期回調。IAbilityContinuation.onCompleteContinuation()
方法,通知數據恢復成功與否。目前人們對於鴻蒙的態度呈現兩極化,有的人追捧有的人貶低,我以爲都大可沒必要,多給鴻蒙一些空間和耐心,靜觀其變、樂見其成。固然首選須要華爲作到本身不主動炒做,真正靜下心來打磨鴻蒙,只要華爲有決心有耐心,做爲開發者的咱們爲何不支持呢?
伴隨開發者活動日,鴻蒙還舉辦了多輪線上挑戰賽活動(目前還在進行中),難度不高參與即能完成,且中獎率很高(親測),有興趣能夠參與一下,但願個人好運傳遞給你
活動詳情:mp.weixin.qq.com/s/3IrZGZkm1…