如何用HMS Nearby Service给自己的App添加近距离数据传输功能

2024-02-29 01:59

本文主要是介绍如何用HMS Nearby Service给自己的App添加近距离数据传输功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  当你给朋友发送手机资料时,过了很久进度条却动也不动;当你想发送大文件给同事时,仅一个文件就用光了你所有流量;当你跟朋友乘坐飞机时想一起玩游戏时,却因没有网络无奈放弃。

在这里插入图片描述

  们生活中似乎经常能遇到这种尴尬的场景,近距离数据传输功能是用户的一个痛点。现在,只需要接入华为近距离通信服务,通过Nearby Connection便可以轻松实现设备间的数据传输,传输类型支持短文本、流数据和文件数据等类型,可帮助app实现本地多人游戏、实时协作、多屏游戏和离线文件传输等功能。下图是功能演示:

在这里插入图片描述

  如果你对实现方式感兴趣,可以在Github上下载源码:
  https://github.com/HMS-Core/hms-nearby-demo/tree/master/NearbyConnection

  首先需要了解Nearby Connection 开发流程

在这里插入图片描述

1. 业务流程

  整体流程可以划分为4个阶段。

  广播扫描阶段:广播端启动广播,发现端启动扫描以发现广播端。

  1. 广播端调用startBroadcasting()启动广播。
  2. 发现端调用startScan()启动扫描以发现附近的设备。
  3. 由onFound()方法通知扫描结果。

  建立连接阶段:发现端发起连接并启动对称的身份验证流程,双端独立接受或拒绝连接请求。

  1. 发现端调用requestConnect()向广播端发起连接请求。
  2. 两端由onEstablish()通知连接启动后,均可以调用acceptConnect()接受连接或调用rejectConnect()拒绝连接。
  3. 两端由onResult()通知连接结果。仅当两端都接受连接时,连接才能建立。

  传输数据阶段:建立连接后,双端进行数据交换。

  1. 连接建立后,双端均可以调用sendData()发送数据给对端。
  2. 接收数据的一端由onReceived()通知接收到数据;两端由onTransferUpdate()通知当前的传输状态。

  断开连接阶段:双端任意一端发起断开连接,通知对端连接断开。

  1. 主动断开连接的一端调用disconnect()断开连接,对端由onDisconnected()通知连接断开。

2. 开发步骤

2.1 开发准备

  如果你以前没有集成华为移动服务的经验,那么需要先配置AppGallery Connect,开通近距离通信服务并集成HMS SDK。相关步骤请参考官方文档。

2.2 声明系统权限

  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.3 选择策略

  Nearby Discovery支持3种不同的连接策略:POLICY_MESH,POLICY_STAR和POLICY_P2P。可以根据应用场景优选策略。

  策略选择并创建BroadcastOption对象的示例代码如下:

Policy policy = Policy.POLICY_STAR;  
BroadcastOption broadcastOption = new BroadcastOption.Builder().setPolicy (policy).build();

2.4 广播和扫描

  一旦授予应用所需的权限,并为应用选择一个策略,就可以开始广播和扫描以发现附近的设备。

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>() {@Overridepublic void onSuccess(Void aVoid) {/* We are broadcasting. */}}).addOnFailureListener(new OnFailureListener() {@Overridepublic void onFailure(Exception e) {/* Fail to start broadcasting. */}});
}

  参数connectCallback是一个连接监听回调类实例,用于通知连接状态信息。有关ConnectCallback类的详细信息及示例代码,参见确认连接章节。

2.4.2 启动扫描

  发现端以选定的policy和serviceId为参数,调用startScan()启动扫描以发现附近的设备。示例代码如下:

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>() {@Overridepublic void onSuccess(Void aVoid) {/* Start scan success. */}}).addOnFailureListener(new OnFailureListener() {@Overridepublic void onFailure(Exception e) {/* Fail to start scan. */}}); 
}

  参数scanEndpointCallback是一个扫描监听回调类实例,通知发现端扫描结果,发现设备或者已发现设备丢失。

private ScanEndpointCallback scanEndpointCallback =new ScanEndpointCallback() {@Overridepublic void onFound(String endpointId, ScanEndpointInfo discoveryEndpointInfo) {mEndpointId = endpointId;mDiscoveryEngine.requestConnect(myNameStr, mEndpointId, mConnCb);}@Overridepublic void onLost(String endpointId) {Log.d(TAG, "Nearby Connection Demo app: Lost endpoint: " + endpointId);}};

2.4.3 停止广播

  当需要停止广播时,调用stopBroadcasting()。停止广播后,广播端不可以接收来自发现端的连接请求。

2.4.4 停止扫描

  当需要停止扫描时,调用stopScan()。停止扫描后,发现端仍可以向已发现的设备请求连接。一种常见的做法是:一旦发现需要连接的设备,就调用stopScan()停止扫描。

2.5 建立连接

2.5.1 请求连接

  当附近的设备被发现,发现端可以调用requestConnect()发起连接。示例代码如下:

private void doStartConnect(String name, String endpointId) throws RemoteException {Nearby.getDiscoveryEngine(getApplicationContext()).requestConnect(name, endpointId, connectCallback).addOnSuccessListener(new OnSuccessListener<Void>() {@Overridepublic void onSuccess(Void aVoid) {/* Request success. */}}).addOnFailureListener(new OnFailureListener() {@Overridepublic void onFailure(Exception e) {/* Fail to request connect. */}});
}

  当然,根据需要,可以向用户展示发现的设备列表,并允许他们选择连接哪些设备。

2.5.2 确认连接

  发现端发起连接后,通过回调connectCallback的onEstablish()方法将连接建立事件通知给双方。双方必须通过调用acceptConnect()接受连接或者通过调用rejectConnect()拒绝连接。仅当双方都接受连接时,连接才会建立成功。如果一方或双方都选择拒绝,则连接失败。无论哪种方式,连接结果都会通过onResult()方法通知。示例代码如下:

private final ConnectCallback connectCallback =new ConnectCallback() {@Overridepublic void onEstablish(String endpointId, ConnectInfo connectInfo) {/* Accept the connection request without notifying user. */Nearby.getDiscoveryEngine(getApplicationContext())
.acceptConnect(endpointId, dataCallback);}@Overridepublic 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对象的类型包括字节序列、文件和流。通过调用sendData()方法发送数据,通过DataCallback类实例的onReceived()方法接收数据。

2.6.1 数据类型

  1. BYTES
    通过调用Data.fromBytes()创建Data.Type.BYTES类型的Data对象。
    发送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 {  @Overridepublic 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();}}@Overridepublic void onTransferUpdate(String endpointId, TransferStateUpdate update) {/* We will receive TRANSFER_STATE_SUCCESS update after onReceived() called. */}
}

  注意:BYTES与FILE和STREAM类型不同,BYTES是以单个数据块发送的,因此接收端不用等待BYTES类型的状态更新为TRANSFER_STATE_SUCCESS,当onReceived()被调用时候,你就可以调用data.asBytes()以获取全部数据。

  1. FILE
    通过调用Data.fromFile()创建Data.Type.FILE类型的Data对象。
    发送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创建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();
)
  1. STREAM
      通过调用Data.fromStream()创建Data.Type.STREAM类型的Data对象。发送流的示例代码如下:
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<>();@Overridepublic 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... */}}@Overridepublic void onReceived(String endpointId, Data data) {incomingData.put(data.getId(), data);} 
}

2.6.2 进度更新

  DataCallBack回调类onTransferUpdate()方法提供数据发送或接收的进度更新,基于此可以向用户显示传输进度,例如:进度条。

2.6.3 取消传输

  如果需要在接收或发送过程中取消传输,调用TransferEngine类实例方法cancelDataTransfer()。

2.7 断开连接

  如果需要断开与对端的连接,调用DiscoveryEngine类实例方法disconnect()。一旦调用此接口,将不能从此endpoint收发数据。

结后语

  基于Nearby Connection, 可以帮助你的APP实现如下相关功能:

  1. 本地多人游戏:自组网,提供低延时、稳定可靠的传输体验。
  2. 离线文件传输:无需流量,可达80MB/S的传输速度。

  更详细的开发指南参考华为开发者联盟官网:https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/introduction-0000001050040566


原文链接:https://developer.huawei.com/consumer/cn/forum/topicview?tid=0201296701616220024&fid=18
原作者:赵照

这篇关于如何用HMS Nearby Service给自己的App添加近距离数据传输功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/757256

相关文章

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端,一般来说需要版本检测和自动更新功能,这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤: 读取本地应用版本拉取远程版本并比较两个版本如果需要升级,那么拉取更新历史弹出升级控制窗口用户选择升级时,拉取升级包解压,重启应用用户选择忽略时,本地版本标志为忽略版本用户选择取消时,隐藏升级控制窗口 2.

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

Spring+MyBatis+jeasyui 功能树列表

java代码@EnablePaging@RequestMapping(value = "/queryFunctionList.html")@ResponseBodypublic Map<String, Object> queryFunctionList() {String parentId = "";List<FunctionDisplay> tables = query(parent

PostgreSQL核心功能特性与使用领域及场景分析

PostgreSQL有什么优点? 开源和免费 PostgreSQL是一个开源的数据库管理系统,可以免费使用和修改。这降低了企业的成本,并为开发者提供了一个活跃的社区和丰富的资源。 高度兼容 PostgreSQL支持多种操作系统(如Linux、Windows、macOS等)和编程语言(如C、C++、Java、Python、Ruby等),并提供了多种接口(如JDBC、ODBC、ADO.NET等

寻迹模块TCRT5000的应用原理和功能实现(基于STM32)

目录 概述 1 认识TCRT5000 1.1 模块介绍 1.2 电气特性 2 系统应用 2.1 系统架构 2.2 STM32Cube创建工程 3 功能实现 3.1 代码实现 3.2 源代码文件 4 功能测试 4.1 检测黑线状态 4.2 未检测黑线状态 概述 本文主要介绍TCRT5000模块的使用原理,包括该模块的硬件实现方式,电路实现原理,还使用STM32类

nginx介绍及常用功能

什么是nginx nginx跟Apache一样,是一个web服务器(网站服务器),通过HTTP协议提供各种网络服务。 Apache:重量级的,不支持高并发的服务器。在Apache上运行数以万计的并发访问,会导致服务器消耗大量内存。操作系统对其进行进程或线程间的切换也消耗了大量的CPU资源,导致HTTP请求的平均响应速度降低。这些都决定了Apache不可能成为高性能WEB服务器  nginx:

详解Tomcat 7的七大新特性和新增功能(1)

http://developer.51cto.com/art/201009/228537.htm http://tomcat.apache.org/tomcat-7.0-doc/index.html  Apache发布首个Tomcat 7版本已经发布了有一段时间了,Tomcat 7引入了许多新功能,并对现有功能进行了增强。很多文章列出了Tomcat 7的新功能,但大多数并没有详细解释它们