如何用HMS Nearby Service給自己的App添加近距離數據傳輸功能
當你給朋友發(fā)送手機資料時,過了很久進度條卻動也不動;當你想發(fā)送大文件給同事時,僅一個文件就用光了你所有流量;當你跟朋友乘坐飛機時想一起玩游戲時,卻因沒有網絡無奈放棄。
們生活中似乎經常能遇到這種尷尬的場景,近距離數據傳輸功能是用戶的一個痛點?,F在,只需要接入華為近距離通信服務,通過Nearby Connection便可以輕松實現設備間的數據傳輸,傳輸類型支持短文本、流數據和文件數據等類型,可幫助app實現本地多人游戲、實時協(xié)作、多屏游戲和離線文件傳輸等功能。下圖是功能演示:
如果你對實現方式感興趣,可以在Github上下載源碼:
https://github.com/HMS-Core/hms-nearby-demo/tree/master/NearbyConnection
首先需要了解Nearby Connection 開發(fā)流程
1. 業(yè)務流程
整體流程可以劃分為4個階段。
廣播掃描階段:廣播端啟動廣播,發(fā)現端啟動掃描以發(fā)現廣播端。
- 廣播端調用startBroadcasting()啟動廣播。
- 發(fā)現端調用startScan()啟動掃描以發(fā)現附近的設備。
- 由onFound()方法通知掃描結果。
建立連接階段:發(fā)現端發(fā)起連接并啟動對稱的身份驗證流程,雙端獨立接受或拒絕連接請求。
- 發(fā)現端調用requestConnect()向廣播端發(fā)起連接請求。
- 兩端由onEstablish()通知連接啟動后,均可以調用acceptConnect()接受連接或調用rejectConnect()拒絕連接。
- 兩端由onResult()通知連接結果。僅當兩端都接受連接時,連接才能建立。
傳輸數據階段:建立連接后,雙端進行數據交換。
- 連接建立后,雙端均可以調用sendData()發(fā)送數據給對端。
- 接收數據的一端由onReceived()通知接收到數據;兩端由onTransferUpdate()通知當前的傳輸狀態(tài)。
斷開連接階段:雙端任意一端發(fā)起斷開連接,通知對端連接斷開。
- 主動斷開連接的一端調用disconnect()斷開連接,對端由onDisconnected()通知連接斷開。
2. 開發(fā)步驟
2.1 開發(fā)準備
如果你以前沒有集成華為移動服務的經驗,那么需要先配置AppGallery Connect,開通近距離通信服務并集成HMS SDK。相關步驟請參考官方文檔。
2.2 聲明系統(tǒng)權限
Nearby Connection開發(fā)場景需要使用Nearby Discovery API和Nearby Transfer API,你的應用必須根據所使用的策略聲明適當的權限。例如:使用POLICY_STAR策略開發(fā)文件傳輸的應用,需要添加特定的權限到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 是危險的系統(tǒng)權限,因此,必須動態(tài)的申請這些權限。如果權限不足,近距離通信服務(Nearby Service)將會拒絕應用開啟廣播或者開啟發(fā)現。
2.3 選擇策略
Nearby Discovery支持3種不同的連接策略:POLICY_MESH,POLICY_STAR和POLICY_P2P??梢愿鶕脠鼍皟?yōu)選策略。
策略選擇并創(chuàng)建BroadcastOption對象的示例代碼如下:
Policy policy = Policy.POLICY_STAR; BroadcastOption broadcastOption = new BroadcastOption.Builder().setPolicy (policy).build();
2.4 廣播和掃描
一旦授予應用所需的權限,并為應用選擇一個策略,就可以開始廣播和掃描以發(fā)現附近的設備。
2.4.1 啟動廣播
廣播端以選定的policy和serviceId為參數,調用startBroadcasting()啟動廣播。其中serviceId應該唯一標識的應用。建議使用應用的包名作為serviceId(例如:com.huawei.example.myapp)。示例代碼如下:
private void doStartBroadcasting() { Policy policy = Policy.POLICY_STAR; BroadcastOption broadcastOption = new BroadcastOption.Builder().setPolicy(policy).build(); Nearby.getDiscoveryEngine(getApplicationContext()) .startBroadcasting(name, serviceId, connectCallback, broadcastOption) .addOnSuccessListener( new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { /* We are broadcasting. */ } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(Exception e) { /* Fail to start broadcasting. */ } }); }
參數connectCallback是一個連接監(jiān)聽回調類實例,用于通知連接狀態(tài)信息。有關ConnectCallback類的詳細信息及示例代碼,參見確認連接章節(jié)。
2.4.2 啟動掃描
發(fā)現端以選定的policy和serviceId為參數,調用startScan()啟動掃描以發(fā)現附近的設備。示例代碼如下:
private void doStartScan() { Policy policy = Policy.POLICY_STAR; ScanOption scanOption = new ScanOption.Builder().setPolicy(policy).build(); Nearby.getDiscoveryEngine(getApplicationContext()) .startScan(serviceId, scanEndpointCallback, scanOption) .addOnSuccessListener( new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { /* Start scan success. */ } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(Exception e) { /* Fail to start scan. */ } }); }
參數scanEndpointCallback是一個掃描監(jiān)聽回調類實例,通知發(fā)現端掃描結果,發(fā)現設備或者已發(fā)現設備丟失。
private ScanEndpointCallback scanEndpointCallback = 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.4.3 停止廣播
當需要停止廣播時,調用stopBroadcasting()。停止廣播后,廣播端不可以接收來自發(fā)現端的連接請求。
2.4.4 停止掃描
當需要停止掃描時,調用stopScan()。停止掃描后,發(fā)現端仍可以向已發(fā)現的設備請求連接。一種常見的做法是:一旦發(fā)現需要連接的設備,就調用stopScan()停止掃描。
2.5 建立連接
2.5.1 請求連接
當附近的設備被發(fā)現,發(fā)現端可以調用requestConnect()發(fā)起連接。示例代碼如下:
private void doStartConnect(String name, String endpointId) throws RemoteException { Nearby.getDiscoveryEngine(getApplicationContext()) .requestConnect(name, endpointId, connectCallback) .addOnSuccessListener( new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { /* Request success. */ } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(Exception e) { /* Fail to request connect. */ } }); }
當然,根據需要,可以向用戶展示發(fā)現的設備列表,并允許他們選擇連接哪些設備。
2.5.2 確認連接
發(fā)現端發(fā)起連接后,通過回調connectCallback的onEstablish()方法將連接建立事件通知給雙方。雙方必須通過調用acceptConnect()接受連接或者通過調用rejectConnect()拒絕連接。僅當雙方都接受連接時,連接才會建立成功。如果一方或雙方都選擇拒絕,則連接失敗。無論哪種方式,連接結果都會通過onResult()方法通知。示例代碼如下:
private final ConnectCallback connectCallback = new ConnectCallback() { @Override public void onEstablish(String endpointId, ConnectInfo connectInfo) { /* Accept the connection request without notifying user. */ Nearby.getDiscoveryEngine(getApplicationContext()) .acceptConnect(endpointId, dataCallback); } @Override public void onResult(String endpointId, ConnectResult result) { switch (result.getStatus().getStatusCode()) { case StatusCode.STATUS_SUCCESS: /* The connection was established successfully, we can exchange data. */ break; case StatusCode.STATUS_CONNECT_REJECTED: /* The Connection was rejected. */ break; default: /* other unknown status code. */ } } @Override public void onDisconnected(String endpointId) { /* The connection was disconneted. */ } };
此示例顯示了一種雙方自動接受連接的確認連接方式。根據需要,可以使用其他的確認連接方式。
2.5.3 驗證連接
應用程序可以提供一種讓用戶確認連接到指定設備的方法,例如:通過驗證token(token可以是一個短隨機字符串或者數字)。通常這涉及在兩個設備上顯示token并要求用戶手動輸入或者確認,類似于藍牙配對對話框。
下面演示一種通過彈窗確認配對碼的方式驗證連接。示例代碼如下:
@Override public void onEstablish(String endpointId, ConnectInfo connectInfo) { AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext()); builder.setTitle(connectInfo.getEndpointName() + " request connection") .setMessage("Please confirm the match code is: " + connectInfo.getAuthCode()) .setPositiveButton( "Accept", (DialogInterface dialog, int which) -> /* Accept the connection. */ Nearby.getDiscoveryEngine(getApplicationContext()) .acceptConnect(endpointId, dataCallback)) .setNegativeButton( "Reject", (DialogInterface dialog, int which) -> /* Reject the connection. */ Nearby.getDiscoveryEngine(getApplicationContext()) .rejectConnect(endpointId)) .setIcon(android.R.drawable.ic_dialog_alert); AlertDialog alert = builder.create(); alert.show(); }
2.6 傳輸數據
設備間建立連接后,可以使用該連接傳輸Data對象。Data對象的類型包括字節(jié)序列、文件和流。通過調用sendData()方法發(fā)送數據,通過DataCallback類實例的onReceived()方法接收數據。
2.6.1 數據類型
1.BYTES
通過調用Data.fromBytes()創(chuàng)建Data.Type.BYTES類型的Data對象。
發(fā)送BYTES類型的數據,示例代碼如下:
Data bytesData = Data.fromBytes(new byte[] {0xA, 0xA, 0xA, 0xA, 0xA}); Nearby.getTransferEngine(getApplicationContext()).sendData(toEndpointId, bytesData);
注意:BYTES類型數據的長度大小不能超過32KB。
接收BYTES類型的數據,示例代碼如下:
static class BytesDataReceiver extends DataCallback { @Override public void onReceived(String endpointId, Data data) { /* BYTES data is sent as a single block, so we can get complete data. */ if (data.getType() == Data.Type.BYTES) { byte[] receivedBytes = data.asBytes(); } } @Override public void onTransferUpdate(String endpointId, TransferStateUpdate update) { /* We will receive TRANSFER_STATE_SUCCESS update after onReceived() called. */ } }
注意:BYTES與FILE和STREAM類型不同,BYTES是以單個數據塊發(fā)送的,因此接收端不用等待BYTES類型的狀態(tài)更新為TRANSFER_STATE_SUCCESS,當onReceived()被調用時候,你就可以調用data.asBytes()以獲取全部數據。
2.FILE
通過調用Data.fromFile()創(chuàng)建Data.Type.FILE類型的Data對象。
發(fā)送FILE類型數據的示例代碼如下:
File fileToSend = new File(getApplicationContext().getFilesDir(), "fileSample.txt"); try { Data fileData = Data.fromFile(fileToSend); Nearby.getTransferEngine(getApplicationContext()) .sendData(endpointList, fileData); } catch (FileNotFoundException e) { /* Exception handle. */ }
一種更高效方法是使用ParcelFileDescriptor創(chuàng)建FILE類型,可以最大程度地減少文件的復制。示例代碼如下:
ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r"); Data fileData = Data.fromFile(pfd);
接收設備收到文件后,文件保存在Download目錄,并且將以fileData.getId()轉化后的字符串命名。傳輸完成后,可以獲取FILE對象。示例代碼如下:
/* We can get the received file in the Download folder. */ File payloadFile = fileData.asFile().asJavaFile(); )
3.STREAM
通過調用Data.fromStream()創(chuàng)建Data.Type.STREAM類型的Data對象。發(fā)送流的示例代碼如下:
URL url = new URL("https://developers.huawei.com"); Data streamData = Data.fromStream(url.openStream()); Nearby.getTransferEngine(getApplicationContext()).sendData(toEndpointId, streamData);
接收端,當onTransferUpdate()回調成功時,可以調用streamData.asStream().asInputStream()或者 streamData.asStream().asParcelFileDescriptor()獲取流對象。示例代碼如下:
static class StreamDataReceiver extends DataCallback { private final HashMap<Long, Data> incomingData = new HashMap<>(); @Override public void onTransferUpdate(String endpointId, TransferStateUpdate update) { if (update.getStatus() == TransferStateUpdate.Status.TRANSFER_STATE_SUCCESS) { Data data = incomingData.get(update.getDataId()); InputStream inputStream = data.asStream().asInputStream(); /* Further processing... */ } } @Override public void onReceived(String endpointId, Data data) { incomingData.put(data.getId(), data); } }
2.6.2 進度更新
DataCallBack回調類onTransferUpdate()方法提供數據發(fā)送或接收的進度更新,基于此可以向用戶顯示傳輸進度,例如:進度條。
2.6.3 取消傳輸
如果需要在接收或發(fā)送過程中取消傳輸,調用TransferEngine類實例方法cancelDataTransfer()。
2.7 斷開連接
如果需要斷開與對端的連接,調用DiscoveryEngine類實例方法disconnect()。一旦調用此接口,將不能從此endpoint收發(fā)數據。
結后語
基于Nearby Connection, 可以幫助你的APP實現如下相關功能:
本地多人游戲:自組網,提供低延時、穩(wěn)定可靠的傳輸體驗。離線文件傳輸:無需流量,可達80MB/S的傳輸速度。
更詳細的開發(fā)指南參考華為開發(fā)者聯(lián)盟官網:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/introduction-0000001050040566
到此這篇關于如何用HMS Nearby Service給自己的App添加近距離數據傳輸功能的文章就介紹到這了,更多相關HMS Nearby Service App數據傳輸內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android Service中使用Toast無法正常顯示問題的解決方法
這篇文章主要介紹了Android Service中使用Toast無法正常顯示問題的解決方法,分析了Service中Toast無法正常顯示的原因與相關的解決方法,具有一定參考借鑒價值,需要的朋友可以參考下2016-10-10詳解android使用ItemDecoration 懸浮導航欄效果
本篇文章主要介紹了Android 最流行的吸頂效果的實現及代碼,非常具有實用價值,需要的朋友可以參考下。2017-01-01Android巧用Fragment解耦onActivityResult詳解
這篇文章主要給大家介紹了關于Android巧用Fragment解耦onActivityResult的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-08-08Android自定義多節(jié)點進度條顯示的實現代碼(附源碼)
這篇文章主要介紹了Android自定義多節(jié)點進度條顯示的實現代碼,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-03-03詳解 Kotlin Reference Basic Types, String, Array and Imports
這篇文章主要介紹了詳解 Kotlin Reference Basic Types, String, Array and Imports的相關資料,需要的朋友可以參考下2017-06-06