天天看點

如何用HMS Nearby Service在應用中添加近距離裝置間資料傳輸功能

前言

想象一下,當你回到家中,客廳裡自動播放起你今天在某家咖啡廳聽到并Mark到手機備忘錄裡的歌曲;智能配電表通過手機提醒你該交電費了,而你隻需要說一句“充五毛錢的”就完成了繳費;晚上,浴室裡的熱水器檢測到你在房間裡的活動,根據你的作息習慣開始自動加熱;而你又一次在車庫裡因為忘了停車位置而迷茫時,不遠處的愛車正閃爍着卡姿蘭大眼睛呼喚着你……所有這一切,通過接入HMS Nearby Service的近距離裝置間資料傳輸服務,你都可以輕松擷取。

應用場景

HMS Nearby Service的近距離裝置間資料傳輸服務,無需連接配接Internet,通過藍牙、WIFI等方式,發現并建立與其他裝置的直接通信通道,支援無縫的近距離互動,例如本地多人遊戲、實時協作、多屏遊戲和離線檔案傳輸等。開發者不需要關心底層藍牙、wifi的通信原理,隻需要接入HMS Nearby Service便可以輕松實作裝置間的資料傳輸,傳輸類型支援短文本、流資料和檔案資料等類型。

下文将為您介紹Nearby Connection 開發流程。

1. 開發準備

如果您已經是華為的開發者,可以省略此步驟。如果您以前沒有內建華為移動服務的經驗,那麼需要先配置AppGallery Connect,開通近距離通信服務并內建HMS SDK。相關步驟請參考參考華為開發者聯盟:https://developer.huawei.com/consumer/cn/doc/development/HMS-Guides/ml-process-4

2. 代碼開發

2.1 聲明系統權限

Nearby Connection開發場景需要使用Nearby Discovery API和Nearby Transfer API,您的應用必須根據所使用的政策聲明适當的權限。例如:使用POLICY_STAR政策開發檔案傳輸的應用,需要添加特定的權限到AndroidManifest.xml:

<!-- Required for Nearby Discovery and Nearby Transfer -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />  
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />   
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Required for FILE payloads -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
           

由于ACCESS_FINE_LOCATION,WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE 是危險的系統權限,是以,您必須動态的申請這些權限。如果權限不足,近距離通信服務(Nearby Service)将會拒絕您的應用開啟廣播或者開啟發現。

2.2 擷取Connection用戶端執行個體

Nearby Service Connection通過DiscoveryEngine和TransferEngine這兩個類的執行個體提供廣播、掃描、連接配接、傳輸等功能,是以首先需要擷取這兩個用戶端執行個體。

DiscoveryEngine mDiscoveryEngine = Nearby.getDiscoveryEngine(context);
TransferEngine mTransferEngine = Nearby.getTransferEngine(context);
           

2.3 開始廣播

DiscoveryEngine用戶端執行個體提供了startBroadcasting接口用于提供廣播功能。在調用該接口時,需要指定裝置名稱、應用的serviceId、連接配接回調類執行個體、廣播政策作為該接口參數。

public void doStartBroadcast(View view) throws RemoteException {
        BroadcastOption.Builder advBuilder = new BroadcastOption.Builder();
        advBuilder.setPolicy(Policy.POLICY_STAR);
        mDiscoveryEngine.startBroadcasting(myNameStr, myServiceId, mConnCb, advBuilder.build());
 }
           

其中,連接配接回調類執行個體mConnCb中的方法會在建立連接配接的過程中被回調執行。示例代碼中,onEstablish方法訓示廣播端在接收到掃描端發起連接配接請求時,通過調用acceptConnect接口接受連接配接請求。同時示例代碼中,通過調用stopBroadcasting/stopScan接口停止廣播/掃描,以提高連接配接效率。onResult方法中,可以擷取連接配接結果,并根據業務需要對連接配接結果進行處理。示例代碼中對已連接配接裝置的EndpointId進行了儲存。onDisconnected方法中,可以添加斷開連接配接時的業務處理邏輯。

private ConnectCallback mConnCb =
            new ConnectCallback() {
                @Override
                public void onEstablish(String endpointId, ConnectInfo connectionInfo) {
                    mTransferEngine = Nearby.getTransferEngine(getApplicationContext());
                    mEndpointId = endpointId;
                    mDiscoveryEngine.acceptConnect(endpointId, mDataCb);
                    ToastUtil.showShortToastTop("Let's chat!");
                    sendBtn.setEnabled(true);
                    msgEt.setEnabled(true);
                    connectBtn.setEnabled(false);
                    connectTaskResult = StatusCode.STATUS_SUCCESS;
                    if (myNameStr.compareTo(friendNameStr) > 0) {
                        mDiscoveryEngine.stopScan();
                    } else {
                        mDiscoveryEngine.stopBroadcasting();
                    }
                }
 
                @Override
                public void onResult(String endpointId, ConnectResult resolution) {
                    mEndpointId = endpointId;
                }

                @Override
                public void onDisconnected(String endpointId) {
                    ToastUtil.showShortToastTop("Disconnect.");
                    connectTaskResult = StatusCode.STATUS_NOT_CONNECTED;
                    sendBtn.setEnabled(false);
                    connectBtn.setEnabled(true);
                    msgEt.setEnabled(false);
                    myNameEt.setEnabled(true);
                    friendNameEt.setEnabled(true);
                }
            };
           

2.4 開始掃描

DiscoveryEngine用戶端執行個體提供了startScan接口用于提供掃描功能。在調用該接口時,需要指定應用的serviceId、掃描回調類執行個體、掃描政策作為該接口參數。其中serviceId和掃描政策需要與廣播端的serviceId和廣播政策保持一緻。

public void doStartScan(View view) throws RemoteException {
        ScanOption.Builder discBuilder = new ScanOption.Builder();
        discBuilder.setPolicy(Policy.POLICY_STAR);
        mDiscoveryEngine.startScan(myServiceId, mDiscCb, discBuilder.build());
    }
           

其中,掃描回調類執行個體mDiscCb中的方法會在掃描過程中被回調執行。示例代碼中,onFound方法訓示掃描端在掃描到廣播端裝置時,通過調用requestConnect接口請求與廣播端建立連接配接。onLost方法中,可以添加裝置Lost後的業務處理邏輯。

private ScanEndpointCallback mDiscCb =
            new ScanEndpointCallback() {
                @Override
                public void onFound(String endpointId, ScanEndpointInfo discoveryEndpointInfo) {
                    mEndpointId = endpointId;
                    mDiscoveryEngine.requestConnect(myNameStr, mEndpointId, mConnCb);
                }

                @Override
                public void onLost(String endpointId) {
                    Log.d(TAG, "Nearby Connection Demo app: Lost endpoint: " + endpointId);
                }
            };
           

2.5 請求連接配接

前文2.3中已經提到了請求連接配接接口的使用。請求連接配接接口requestConnect在調用時,需要指定本地裝置名稱、對端裝置的EndpointId以及連接配接回調類執行個體mConnCb作為參數。執行個體代碼中,掃描端和廣播端共用已同一個連接配接回調類執行個體。開發者也可以根據業務需要,分别定義掃描端和廣播端的處理邏輯。

2.6 接受連接配接

前文2.2中已經提到了接受連接配接接口的使用。接受連接配接接口acceptConnect在調用時,需要指定對端裝置的EndpointId、資料傳輸回調類執行個體作為參數。

其中,資料傳輸回調類執行個體mDataCb會在資料傳輸過程中被執行。開發者可以根據業務需要,在onReceived方法中對接收到的資料進行處理。在onTransferUpdate方法中,開發者可以從TransferStateUpdate執行個體中擷取已傳輸資料量、資料傳輸總量等資訊,并添加相關業務處理邏輯。

private DataCallback mDataCb =
            new DataCallback() {
                @Override
                public void onReceived(String endpointId, Data data) {
                    receiveMessage(data);
                }

                @Override
                public void onTransferUpdate(String endpointId, TransferStateUpdate update) {
                }
            };
           

2.7 發送資料

TransferEngine用戶端執行個體提供了sendData接口以提供資料傳輸能力。調用該接口時,需要指定對端裝置的EndpointId、待傳輸資料作為參數。示例代碼中的待傳輸資料為通過Data. fromFile接口擷取到的檔案類型資料。開發者也可以通過Data. fromStream、Data. fromBytes接口擷取其他類型的資料進行傳輸。

File fileToSend = new File(getApplicationContext().getFilesDir(), "fileSample.txt");
try {
    Data data = Data.fromFile(fileToSend);
    mTransferEngine.sendData(mEndpointId, data);
} catch (FileNotFoundException e) { 
    /* Exception handle. */
}
           

您可以在Github上擷取更詳細的源碼:https://github.com/HMS-Core/hms-nearby-demo/tree/master/NearbyConnection

更詳細的開發指南參考華為開發者聯盟官網

https://developer.huawei.com/consumer/cn/hms/huawei-nearbyservice