HarmonyOS - 實(shí)現(xiàn)多設(shè)備協(xié)同開發(fā)實(shí)戰(zhàn)教程~

前言

現(xiàn)在隨著個(gè)人設(shè)備越來越多侍芝,越來越需要多個(gè)設(shè)備之間相互感知和連接凉泄,設(shè)備和設(shè)備之間可以相互聯(lián)動(dòng),形成互聯(lián)互通的場景清钥,而搭載HarmonyOS的設(shè)備恰好可以滿足這一點(diǎn) 琼锋。下面通過開發(fā)一個(gè)HarmonyOS的多端分布式表白應(yīng)用來實(shí)現(xiàn)設(shè)備之間的相互聯(lián)動(dòng)。

項(xiàng)目介紹

H5頁面可以實(shí)現(xiàn)一些比較特殊的頁面效果祟昭,所以選擇在應(yīng)用中集成H5頁面缕坎。應(yīng)用可以將頁面直接投放到附近其他HarmonyOS設(shè)備上,實(shí)現(xiàn)多端設(shè)備分布式顯示篡悟,同時(shí)應(yīng)用可以跨端控制谜叹,更新應(yīng)用頁面,形成多設(shè)備協(xié)同的效果搬葬。

下面是效果展示:

多設(shè)備協(xié)同原理

HarmonyOS 給應(yīng)用開發(fā)者提供了一套在多個(gè)設(shè)備不同應(yīng)用之間進(jìn)行任務(wù)流轉(zhuǎn)的API接口荷腊,實(shí)現(xiàn)設(shè)備協(xié)同需要關(guān)注 流轉(zhuǎn)任務(wù)管理服務(wù)分布式任務(wù)調(diào)度

? 流轉(zhuǎn)任務(wù)管理服務(wù):在流轉(zhuǎn)發(fā)起端急凰,接受用戶應(yīng)用程序注冊(cè)女仰,提供流轉(zhuǎn)入口、狀態(tài)顯示抡锈、退出流轉(zhuǎn)等管理能力疾忍。

? 分布式任務(wù)調(diào)度:提供遠(yuǎn)程服務(wù)啟動(dòng)、遠(yuǎn)程服務(wù)連接企孩、遠(yuǎn)程遷移等能力锭碳,并通過不同能力組合,支撐用戶應(yīng)用程序完成跨端遷移或多端協(xié)同的業(yè)務(wù)體驗(yàn)勿璃。

? 分布式安全:提供E2E的加密通道擒抛,為用戶應(yīng)用程序提供安全的跨端傳輸機(jī)制推汽,保證“正確的人,通過正確的設(shè)備歧沪,正確地使用數(shù)據(jù)”歹撒。

? 分布式軟總線:使用基于手機(jī)、平板诊胞、智能穿戴暖夭、智慧屏等分布式設(shè)備的統(tǒng)一通信基座,為設(shè)備之間的互聯(lián)互通提供統(tǒng)一的分布式通信能力撵孤。

? 任務(wù)流轉(zhuǎn)流程:設(shè)備A和設(shè)備B登錄相同的華為賬號(hào)迈着,在同一網(wǎng)絡(luò)下。設(shè)備A向設(shè)備B發(fā)起協(xié)同邪码,應(yīng)用程序需先向系統(tǒng)的流轉(zhuǎn)任務(wù)管理服務(wù) 注冊(cè)回調(diào)裕菠,獲取到設(shè)備B的 DeviceId 等設(shè)備信息,設(shè)備A初始化分布式任務(wù)調(diào)度闭专,通過設(shè)備DeviceId指定設(shè)備發(fā)起協(xié)同奴潘,設(shè)備B接收到協(xié)同請(qǐng)求,初始化分布式任務(wù)調(diào)度影钉,啟動(dòng)對(duì)應(yīng)的應(yīng)用程序画髓,把流轉(zhuǎn)的狀態(tài)上報(bào)給流轉(zhuǎn)任務(wù)管理服務(wù),流轉(zhuǎn)任務(wù)管理服務(wù)返回流轉(zhuǎn)結(jié)果平委,完成一次設(shè)備A到設(shè)備B的任務(wù)流轉(zhuǎn)奈虾。

理解分布式軟總線

? 分布式:指的是一種運(yùn)行方式,簡單來說就是任務(wù)可以在一個(gè)設(shè)備上運(yùn)行肆汹,也可以多個(gè)設(shè)備連接起來一起運(yùn)行愚墓,在多個(gè)設(shè)備中沒有一個(gè)絕對(duì)的中心。

? 總線:簡單了解一下“總線”的概念昂勉,在計(jì)算機(jī)系統(tǒng)中浪册,各個(gè)部件之間傳送信息的通道叫總線,外部設(shè)備通過相應(yīng)的接口與總線相連接岗照,組成了整個(gè)計(jì)算機(jī)系統(tǒng)村象。

? 分布式軟總線:所以不難理解分布式軟總線其實(shí)就是實(shí)現(xiàn)多個(gè)設(shè)備之間的連接,傳遞消息攒至,實(shí)現(xiàn)任務(wù)多端運(yùn)行的一種技術(shù)厚者。在HarmonyOS中,底層已經(jīng)幫我們實(shí)現(xiàn)了設(shè)備之間的組網(wǎng)迫吐、發(fā)現(xiàn)和連接库菲,所以并不需要關(guān)心設(shè)備怎么通信,只需要調(diào)用底層封裝好的接口志膀,實(shí)現(xiàn)多設(shè)備協(xié)同就可以了熙宇。

實(shí)現(xiàn)步驟

實(shí)現(xiàn)分布式多設(shè)備協(xié)同鳖擒,需要實(shí)現(xiàn)跨端啟動(dòng)應(yīng)用、后臺(tái)PA服務(wù)烫止、分布式數(shù)據(jù)同步的功能蒋荚,具體實(shí)現(xiàn)流程如下

一、跨設(shè)備啟動(dòng)應(yīng)用

多設(shè)備協(xié)同實(shí)現(xiàn)的前提馆蠕,需要在多端安裝相同的應(yīng)用期升,而在現(xiàn)實(shí)使用環(huán)境中,在多個(gè)設(shè)備中安裝一個(gè)相同的應(yīng)用還是一個(gè)比較麻煩的事互躬。而HarmonyOS的原子化服務(wù)則不需要用戶手動(dòng)安裝播赁,由系統(tǒng)程序框架后臺(tái)安裝后即可使用,在HarmonyOS的服務(wù)中心以服務(wù)卡片的形式展示吼渡。

應(yīng)用由原子化服務(wù)平臺(tái)(Huawei Ability Gallery)管理和分發(fā)行拢,只需要上傳到原子化服務(wù)平臺(tái)(Huawei Ability Gallery)即可,在多設(shè)備協(xié)同中诞吱,當(dāng)設(shè)備A的應(yīng)用向設(shè)備B的應(yīng)用發(fā)起多端協(xié)同,如果設(shè)備B上沒有安裝對(duì)應(yīng)服務(wù)竭缝,HarmonyOS會(huì)自動(dòng)下載相關(guān)原子化服務(wù)房维,和A端的應(yīng)用一起進(jìn)行多端協(xié)同。

跨設(shè)備啟動(dòng)應(yīng)用抬纸,也就是設(shè)備A上的應(yīng)用可以拉起設(shè)備B上的應(yīng)用咙俩。因?yàn)樵踊?wù)應(yīng)用免安裝的特性,所以不用關(guān)心應(yīng)用在多設(shè)備上的安裝湿故,只需實(shí)現(xiàn)跨設(shè)備啟動(dòng)應(yīng)用即可阿趁。

1. 創(chuàng)建原子化服務(wù)

以原子化服務(wù)的形式創(chuàng)建項(xiàng)目, 原子化服務(wù)的特點(diǎn)是支持免安裝坛猪,沒有應(yīng)用圖標(biāo)脖阵,只在 HarmanoyOS 服務(wù)中心以卡片的形式展現(xiàn),支持跨端遷移和多端協(xié)同墅茉。
在新建項(xiàng)目時(shí)選擇Atomic Service命黔,創(chuàng)建一個(gè)原子化服務(wù),同時(shí)打開Show in service center 開關(guān)就斤,自動(dòng)創(chuàng)建服務(wù)卡片悍募,去掉TV的勾選狀態(tài),目前服務(wù)卡片不支持TV設(shè)備洋机。

2. 創(chuàng)建HarmonyOS IDL接口

選擇項(xiàng)目的module目錄坠宴,點(diǎn)擊鼠標(biāo)右鍵,選擇New>Idl File绷旗,如下圖:

IDL是HarmonyOS的接口描述語言喜鼓,可以實(shí)現(xiàn)IPC跨進(jìn)程間通信副砍,接口的提供方是服務(wù)端,客戶端綁定應(yīng)用的服務(wù)來進(jìn)行交互颠通。

在IDL中定義服務(wù)端接口址晕,代碼如下。

登錄后復(fù)制

**interface** com.wealchen.multipoint.IMultiPointIdl {

  *//啟動(dòng)服務(wù)*
  void serviceStart([in] int code);

  *//發(fā)送消息*
  void sendMsg([in] int code,[in]int extras);

  *//停止服務(wù)*
  int serviceStop();
}

創(chuàng)建的IDL接口通過編譯會(huì)在build > generated > source > Idl> 目錄 debugrelease 下自動(dòng)生成對(duì)應(yīng)的接口類顿锰、樁類和代理類谨垃,如下圖。

3. 實(shí)現(xiàn)后臺(tái)接口服務(wù)

在HarmonyOS中硼控,客戶端綁定服務(wù)端后刘陶,獲取到序列化的IRemoteObject對(duì)象,通過IRemoteObject對(duì)象實(shí)現(xiàn)客戶端與服務(wù)端的通信牢撼,IRemoteObject在編譯生成的樁類和代理類中已經(jīng)完成了對(duì)象的創(chuàng)建和消息發(fā)送的實(shí)現(xiàn)匙隔,具體可查看上圖中自動(dòng)生成的代碼。

創(chuàng)建一個(gè)Service后臺(tái)服務(wù)MultiPointService熏版,提供給客戶端連接纷责,并實(shí)現(xiàn)IDL中定義的接口,在接口實(shí)現(xiàn)中啟動(dòng)FA頁面撼短,接收客戶端發(fā)送的消息再膳,具體代碼如下:

登錄后復(fù)制

**public** **class** MultiPointService **extends** Ability {
  *// DESCRIPTOR 保持與 MultiPointIdlStub 和 MultiPointIdlProxy中的一致*
  **private** static final String DESCRIPTOR = "com.wealchen.multipoint.IMultiPointIdl";
  **private** static final String TAG = MultiPointService.class.getName();

  @Override
  **protected** void onStart(Intent intent) {
    **super**.onStart(intent);
  }

  @Override
  **protected** IRemoteObject onConnect(Intent intent) {
    **return** **new** MultiPointRemoteObject(DESCRIPTOR);
  }


  **private** **class** MultiPointRemoteObject **extends** MultiPointIdlStub {
    **public** MultiPointRemoteObject(String descriptor) {
      **super**(descriptor);
    }

    @Override
    **public** void serviceStart(int _code) **throws** RemoteException {
      *//啟動(dòng)WebViewAbility頁面*
      Intent intent = **new** Intent();
      Operation operation = **new** Intent.OperationBuilder().withBundleName(getBundleName())
          .withAbilityName(WebViewAbility.class.getName()).build();
      intent.setOperation(operation);
      intent.setParam(Constants.INTENT_STAR_PARAM, _code);
      startAbility(intent);
      LogUtil.debug(TAG, "serviceStart : start WebViewAbility " + _code);
    }

    @Override
    **public** void sendMsg(int _code, int _extras) **throws** RemoteException {
      LogUtil.debug(TAG, "sendMsg  code: " + _code + " extras: " + _extras);
    }

    @Override
    **public** int serviceStop() **throws** RemoteException {
      **return** 0;
    }
  }
}

4. 實(shí)現(xiàn)跨端啟動(dòng)應(yīng)用

啟動(dòng)跨端應(yīng)用需指定設(shè)備的DeviceId,通過設(shè)備管理器DeviceManager曲横,可獲取到當(dāng)前同一網(wǎng)絡(luò)下所有不是待機(jī)狀態(tài)的設(shè)備喂柒,拿到DeviceId,DeviceName等設(shè)備信息禾嫉,具體代碼如下:

登錄后復(fù)制

 *//獲取同一網(wǎng)絡(luò)下的設(shè)備*
    List<DeviceInfo> deviceInfos = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
    **if** (deviceInfos == **null** && deviceInfos.size() == 0) {
      **return**;
    }
    **for** (int i = 0; i < deviceInfos.size(); i++) {
      String devId = deviceInfos.get(i).getDeviceId();
      String devName = deviceInfos.get(i).getDeviceName();
    }

通過DeviceId可以與指定設(shè)備的后臺(tái)服務(wù)MultiPointService連接灾杰,實(shí)現(xiàn)長期交互,系統(tǒng)提供了connectAbility方法熙参,實(shí)現(xiàn)跨設(shè)備PA連接與斷開連接的能力艳吠,通過AbilitySlice的connectAbility接口跨設(shè)備連接到后臺(tái)服務(wù)MultiPointService,發(fā)送消息到指定設(shè)備孽椰,設(shè)備在接收到消息之后可以執(zhí)行相應(yīng)的任務(wù)讲竿,從而實(shí)現(xiàn)跨設(shè)備應(yīng)用任務(wù)的調(diào)度,連接服務(wù)實(shí)現(xiàn)代碼如下:

登錄后復(fù)制

 *//啟動(dòng)遠(yuǎn)端設(shè)備的FA*
  **private** void startAbilityFa(String devicesId, String event, int localExtras) {
    Intent intent = **new** Intent();
    Operation operation =
        **new** Intent.OperationBuilder()
            .withDeviceId(devicesId)
            .withBundleName(getBundleName())
            .withAbilityName(MultiPointService.class.getName())
            .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
            .build();
    intent.setOperation(operation);
    boolean connectFlag = connectAbility(intent, **new** IAbilityConnection() {
      @Override
      **public** void onAbilityConnectDone(ElementName elementName, IRemoteObject remoteObject, int extra) {
        LogUtil.debug(TAG, "onAbilityConnectDone extra:" + extra);
        multiPointIdl = MultiPointIdlStub.asInterface(remoteObject);
        **try** {
          **if** (multiPointIdl != **null**) {
            **switch** (event) {
              **case** "serviceStart":
                multiPointIdl.serviceStart(0);
                **break**;
              **case** "sendMsg":
                multiPointIdl.sendMsg(1001, localExtras);
                **break**;
            }
          }
        } **catch** (RemoteException e) {
          LogUtil.error(TAG, "connect successful,but have remote exception");
        }
      }

      @Override
      **public** void onAbilityDisconnectDone(ElementName elementName, int extra) {
        LogUtil.debug(TAG, "extra " + extra + " elementName " + elementName.getAbilityName());
        disconnectAbility(**this**);
      }
    });
    **if** (connectFlag) {
      Toast.toast(**this**, "transmit successful!", TOAST_DURATION);
    } **else** {
      Toast.toast(**this**, "transmit failed!Please try again later.", TOAST_DURATION);
    }
  }

二弄屡、多端設(shè)備協(xié)同

多設(shè)備協(xié)同可以實(shí)現(xiàn)對(duì)跨端設(shè)備的控制题禀,使用HarmonyOS的分布式數(shù)據(jù)服務(wù),不同設(shè)備之間的數(shù)據(jù)可以實(shí)時(shí)更新并顯示在界面上膀捷。

1. 分布式數(shù)據(jù)服務(wù)介紹

分布式數(shù)據(jù)服務(wù)是HarmonyOS為應(yīng)用程序提供不同設(shè)備間同步數(shù)據(jù)的能力迈嘹,通過使用分布式數(shù)據(jù)接口,應(yīng)用程序?qū)?shù)據(jù)保存到分布式數(shù)據(jù)庫中,不同設(shè)備之間可以通過分布式數(shù)據(jù)服務(wù)互相訪問秀仲,支持?jǐn)?shù)據(jù)在相同帳號(hào)的多端設(shè)備之間相互同步融痛。

HarmonyOS的分布式數(shù)據(jù)庫是一種NoSQL類型數(shù)據(jù)庫,其數(shù)據(jù)以鍵值對(duì)key-value的形式進(jìn)行組織神僵、索引和存儲(chǔ)雁刷,分布式數(shù)據(jù)服務(wù)包含五部分:

? 服務(wù)接口:提供專門的數(shù)據(jù)庫創(chuàng)建、數(shù)據(jù)訪問保礼、數(shù)據(jù)訂閱等接口給應(yīng)用程序調(diào)用沛励,接口支持KV數(shù)據(jù)模型,支持常用的數(shù)據(jù)類型炮障,同時(shí)確保接口的兼容性目派、易用性和可發(fā)布性。

? 服務(wù)組件:負(fù)責(zé)服務(wù)內(nèi)元數(shù)據(jù)管理胁赢、權(quán)限管理企蹭、加密管理、備份和恢復(fù)管理以及多用戶管理等智末、同時(shí)負(fù)責(zé)初始化底層分布式DB的存儲(chǔ)組件谅摄、同步組件和通信適配層。

? 存儲(chǔ)組件:負(fù)責(zé)數(shù)據(jù)的訪問系馆、數(shù)據(jù)的縮減螟凭、事務(wù)、快照它呀、數(shù)據(jù)庫加密,以及數(shù)據(jù)合并和沖突解決等特性棒厘。

? 同步組件:連結(jié)了存儲(chǔ)組件與通信組件纵穿,其目標(biāo)是保持在線設(shè)備間的數(shù)據(jù)庫數(shù)據(jù)一致性,包括將本地產(chǎn)生的未同步數(shù)據(jù)同步給其他設(shè)備奢人,接收來自其他設(shè)備發(fā)送過來的數(shù)據(jù)谓媒,并合并到本地設(shè)備中。

? 通信適配層:負(fù)責(zé)調(diào)用底層公共通信層的接口完成通信管道的創(chuàng)建何乎、連接句惯,接收設(shè)備上下線消息,維護(hù)已連接和斷開設(shè)備列表的元數(shù)據(jù)支救,同時(shí)將設(shè)備上下線信息發(fā)送給上層同步組件抢野,同步組件維護(hù)連接的設(shè)備列表,同步數(shù)據(jù)時(shí)根據(jù)該列表歼指,調(diào)用通信適配層的接口將數(shù)據(jù)封裝并發(fā)送給連接的設(shè)備伴箩。

2. 創(chuàng)建分布式數(shù)據(jù)庫

創(chuàng)建分布式數(shù)據(jù)庫管理器列牺,設(shè)置是否開啟加密带族,自動(dòng)同步功能恃轩,定義數(shù)據(jù)存儲(chǔ)和查詢方法结洼,使用手動(dòng)同步數(shù)據(jù)的方法實(shí)現(xiàn)數(shù)據(jù)的同步,詳細(xì)代碼代碼如下:

登錄后復(fù)制

**public** **class** MyKvStoreManager {
  **private** static final String TAG = MyKvStoreManager.class.getName();
  **private** static MyKvStoreManager instance = **null**;
  **private** static SingleKvStore singleKvStore = **null**;
  **private** static KvManager kvManager;

  **private** MyKvStoreManager(Context context) {
    createKVManager(context);
  }
  */**
  ** 創(chuàng)建分布式數(shù)據(jù)管理器*
  ** \*/*
  **public** static void createKVManager(Context context) {
    KvManagerConfig config = **new** KvManagerConfig(context);
    kvManager = KvManagerFactory.getInstance().createKvManager(config);
    **try** {
      Options options = **new** Options();
      options.setCreateIfMissing(**true**)
          .setAutoSync(**false**)
          .setEncrypt(**false**)
          .setKvStoreType(KvStoreType.SINGLE_VERSION);
      String storeId = "remoteData";
      singleKvStore = kvManager.getKvStore(options, storeId);
    } **catch** (KvStoreException e) {
      LogUtil.error(TAG, "getKvStore:" + e.getKvStoreErrorCode());
    }
  }

  */**
  ** 存儲(chǔ)數(shù)據(jù)*
  ** \*/*
  **public** static void putKvStore(String key, String value) {
    **if** (singleKvStore == **null**) {
      **return**;
    }
    **try** {
      singleKvStore.putString(key, value);
    } **catch** (KvStoreException e) {
      LogUtil.debug(TAG, "putString:" + e.getKvStoreErrorCode());
    }
  }

  */**
  ** 查詢數(shù)據(jù)*
  ** \*/*
  **public** static String getKvStore(String key) {
    String valueString = "";
    **try** {
      valueString = singleKvStore.getString(key);
    } **catch** (KvStoreException e) {
      LogUtil.debug(TAG, "getString:" + e.getKvStoreErrorCode());
    }
    **return** valueString;
  }

  */**
  ** 手動(dòng)同步數(shù)據(jù)*
  ** \*/*
  **public** static void syncData() {
    List<DeviceInfo> deviceInfoList = kvManager.getConnectedDevicesInfo(DeviceFilterStrategy.NO_FILTER);
    List<String> deviceIdList = **new** ArrayList<>();
    **for** (DeviceInfo deviceInfo : deviceInfoList) {
      deviceIdList.add(deviceInfo.getId());
      LogUtil.debug(TAG,"syncData getId: "+deviceInfo.getId());
    }
    **if** (deviceIdList.isEmpty()){
      **return**;
    }
    singleKvStore.sync(deviceIdList, SyncMode.PUSH_PULL);
  }
   */**
  ** 訂閱數(shù)據(jù)同步結(jié)果*
  ** \*/*
  **public** static void dataChangeObserver(KvStoreObserver storeObserver) {
    singleKvStore.subscribe(SubscribeType.SUBSCRIBE_TYPE_REMOTE, storeObserver);
  }
}

3. 訂閱分布式數(shù)據(jù)變化

訂閱分布式數(shù)據(jù)變化客戶端應(yīng)用需要實(shí)現(xiàn)KvStoreObserver接口叉跛,KvStoreObserver接口回調(diào)中返回?cái)?shù)據(jù)更新的結(jié)果松忍,根據(jù)返回結(jié)果應(yīng)用執(zhí)行相應(yīng)的任務(wù),或者更新界面筷厘,從而實(shí)現(xiàn)多端設(shè)備的同步鸣峭,注意:回調(diào)方法中不允許更新UI組件等阻塞動(dòng)作。

更新UI可以通過getMainTaskDispatcher().asyncDispatch()切換到UI主線程更新UI的敞掘,詳細(xì)代碼如下:

登錄后復(fù)制

MyKvStoreManager.dataChangeObserver(**new** KvStoreObserver() {
  @Override
  **public** void onChange(ChangeNotification changeNotification) {
    LogUtil.debug(TAG, "dataChangeObserver");
    List<Entry> updateEntries = changeNotification.getUpdateEntries();
    **for** (int i = 0; i < updateEntries.size(); i++) {
      String key = updateEntries.get(i).getKey();
      String value = updateEntries.get(i).getValue().getString();
      LogUtil.debug(TAG, "dataChangeObserver key:" + key + " value:" + value);
      getMainTaskDispatcher().asyncDispatch(**new** Runnable() {
        @Override
        **public** void run() {
          **switch** (key) {
            **case** JS_ADD_NAME:
              *//添加名字*
              setJsAddName(value);
              **break**;
            **case** JS_DEL_NAME:
              *//刪除名字*
              setJsDelName(value);
              **break**;
            **default**:
              updateListView(key, value);
              **break**;
          }
        }
      });

    }
  }
});

三叽掘、WebView與JavaScript的交互

1. 在WebView調(diào)用H5頁面的JavaScript方法

更新H5頁面時(shí),WebView需要調(diào)用JavaScript玖雁,通過WebView.executeJs()傳入H5頁面中對(duì)應(yīng)的方法名稱更扁,實(shí)現(xiàn)應(yīng)用調(diào)用頁面內(nèi)的JavaScript方法,實(shí)現(xiàn)的代碼如下:

登錄后復(fù)制

 **private** void setJsAddName(String addName) {
    LogUtil.debug(TAG, "addName:" + addName);
    String add_js = String.format("javascript:addName('%s')", addName);
    webView.executeJs(add_js, **new** AsyncCallback<String>() {
      @Override
      **public** void onReceive(String msg) {
        *// 在此確認(rèn)返回結(jié)果*
        LogUtil.debug(TAG, "executeJs onReceive:" + msg);
      }
    });
  }

H5頁面定義的方法:

**function** addName(name){
    **var** tpl = document.getElementById('name').innerText
    **var** str = name +'\n'
    document.getElementById('name').innerText = tpl.concat(str)
    **return** name
}

2. H5頁面調(diào)用應(yīng)用中的方法

WebView組件通過注入回調(diào)對(duì)象到頁面內(nèi)容赫冬,實(shí)現(xiàn)在H5頁面中調(diào)用應(yīng)用中的方法浓镜。

H5頁面調(diào)用應(yīng)用中的方法:

登錄后復(fù)制

**function** callToApp() {
  **if** (window.showDeviceList && window.showDeviceList.call) {
    **var** result = showDeviceList.call("showDeviceList");
  }
}

應(yīng)用中定義的方法:

webView.addJsCallback("showDeviceList", **new** JsCallback() {
  @Override
  **public** String onCallback(String s) {
    getMainTaskDispatcher().asyncDispatch(**new** Runnable() {
      @Override
      **public** void run() {
        **switch** (s) {
          **case** "showDeviceList":
            **if** (!bottomDialog.isShowing()) {
              bottomDialog.show();
            }
            **break**;
        }
      }
    });
    **return** method;
  }
});

總結(jié)

應(yīng)用實(shí)現(xiàn)了包括原子化服務(wù)、任務(wù)流轉(zhuǎn)劲厌、跨端啟動(dòng)應(yīng)用膛薛、分布式數(shù)據(jù)服務(wù)、多端設(shè)備協(xié)同补鼻、WebView組件與JavaScript交互的功能哄啄,其中包含了HarmonyOS系統(tǒng)才具有的功能。以上只是簡單介紹了HarmonyOS特有功能的實(shí)現(xiàn)风范,當(dāng)然HarmonyOS的特性不止這些咨跌,更多的功能和實(shí)現(xiàn)還需要開發(fā)者去探索。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末硼婿,一起剝皮案震驚了整個(gè)濱河市锌半,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寇漫,老刑警劉巖刊殉,帶你破解...
    沈念sama閱讀 212,294評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異州胳,居然都是意外死亡记焊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門栓撞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來亚亲,“玉大人,你說我怎么就攤上這事“乒椋” “怎么了肛响?”我有些...
    開封第一講書人閱讀 157,790評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惜索。 經(jīng)常有香客問我特笋,道長,這世上最難降的妖魔是什么巾兆? 我笑而不...
    開封第一講書人閱讀 56,595評(píng)論 1 284
  • 正文 為了忘掉前任猎物,我火速辦了婚禮,結(jié)果婚禮上角塑,老公的妹妹穿的比我還像新娘蔫磨。我一直安慰自己,他們只是感情好圃伶,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,718評(píng)論 6 386
  • 文/花漫 我一把揭開白布堤如。 她就那樣靜靜地躺著,像睡著了一般窒朋。 火紅的嫁衣襯著肌膚如雪搀罢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,906評(píng)論 1 290
  • 那天侥猩,我揣著相機(jī)與錄音榔至,去河邊找鬼。 笑死欺劳,一個(gè)胖子當(dāng)著我的面吹牛唧取,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播划提,決...
    沈念sama閱讀 39,053評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼枫弟,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了腔剂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,797評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤驼仪,失蹤者是張志新(化名)和其女友劉穎掸犬,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绪爸,經(jīng)...
    沈念sama閱讀 44,250評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡湾碎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,570評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奠货。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片介褥。...
    茶點(diǎn)故事閱讀 38,711評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柔滔,到底是詐尸還是另有隱情溢陪,我是刑警寧澤,帶...
    沈念sama閱讀 34,388評(píng)論 4 332
  • 正文 年R本政府宣布睛廊,位于F島的核電站形真,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏超全。R本人自食惡果不足惜咆霜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,018評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嘶朱。 院中可真熱鬧蛾坯,春花似錦、人聲如沸疏遏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽改览。三九已至下翎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宝当,已是汗流浹背视事。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庆揩,地道東北人俐东。 一個(gè)月前我還...
    沈念sama閱讀 46,461評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像订晌,于是被迫代替她去往敵國和親虏辫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,595評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容