前言
想象一下,當你回到家中,客廳裡自動播放起你今天在某家咖啡廳聽到并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