千家信息网

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

发表于:2025-11-07 作者:千家信息网编辑
千家信息网最后更新 2025年11月07日,这篇文章将为大家详细讲解有关如何用HMS Nearby Service给自己的App添加近距离数据传输功能,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解
千家信息网最后更新 2025年11月07日如何用HMS Nearby Service给自己的App添加近距离数据传输功能

这篇文章将为大家详细讲解有关如何用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:

               

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

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

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()。停止广播后,广播端不可以接收来自发现端的连接请求。

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() {                        @Override                        public void onSuccess(Void aVoid) {                            /* Request success. */                        }                    })            .addOnFailureListener(                    new OnFailureListener() {                        @Override                        public void onFailure(Exception e) {                            /* Fail to request connect. */                        }                    });}

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

2.5.2 确认连接

  发现端发起连接后,通过回调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对象的类型包括字节序列、文件和流。通过调用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 {      @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是以单个数据块发送的,因此接收端不用等待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 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()方法提供数据发送或接收的进度更新,基于此可以向用户显示传输进度,例如:进度条。

2.6.3 取消传输

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

2.7 断开连接

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

关于如何用HMS Nearby Service给自己的App添加近距离数据传输功能就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

数据 广播 传输 示例 设备 代码 类型 方法 文件 应用 对象 权限 策略 近距 功能 双方 实例 方式 用户 进度 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 怎么用数据库学习算法 金山区特定网络技术转让比较 怎么提升服务器的速度 删除数据库表s的 网络安全法要求建设关键 信息软件开发公开招标公告 数据库消息队列取任务 旅业管理系统数据库失效 软件开发模型怎么画 软件开发集成电路股 数据库拆分业务 数据库mdf目录在哪 非关系数据库相同字段 公安机关对网络安全的职责 华为服务器开机进入pe按哪个键 吃鸡游戏服务器怎么分配 挖耳朵视频软件开发 学习通网络技术的答案 中职计算机网络技术老师笔试 支付宝里的易参保无法连接服务器 redis列数据库 数据库中检索和索引的区别 联通软件开发待遇怎么样青岛 一周年网络安全图解 网络安全等保三级标准哪里办 保密软件开发合同范本 我的世界服务器菜单物品的mod 企业网络安全硬件方案 观点互联网金融科技有限公司 科技互联网络反垄断法征求意见
0