热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要介绍了如何用HMSNearbyService给自己的App添加近距离数据传输功能,本文通过图文示例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

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

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

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

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

1. 业务流程

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

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

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

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

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

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

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

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

  • 主动断开连接的一端调用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()以获取全部数据。

2.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();
)

3.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 进度更新

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

2.6.3 取消传输

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

2.7 断开连接

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

结后语

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

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

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

到此这篇关于如何用HMS Nearby Service给自己的App添加近距离数据传输功能的文章就介绍到这了,更多相关HMS Nearby Service App数据传输内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 打开文件管理器_【教程】模组管理器3.1食用指南
    文编:byakko最近有部分小伙伴反应还不会使用unity模组管理器,现在我就给大家讲一下unity模组管理器——从下载到使用。完整视频版以下是无WiF ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 【Windows】实现微信双开或多开的方法及步骤详解
    本文介绍了在Windows系统下实现微信双开或多开的方法,通过安装微信电脑版、复制微信程序启动路径、修改文本文件为bat文件等步骤,实现同时登录两个或多个微信的效果。相比于使用虚拟机的方法,本方法更简单易行,适用于任何电脑,并且不会消耗过多系统资源。详细步骤和原理解释请参考本文内容。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 【MicroServices】【Arduino】装修甲醛检测,ArduinoDart甲醛、PM2.5、温湿度、光照传感器等,数据记录于SD卡,Python数据显示,UI5前台,微服务后台……
    这篇文章介绍了一个基于Arduino的装修甲醛检测项目,使用了ArduinoDart甲醛、PM2.5、温湿度、光照传感器等硬件,并将数据记录于SD卡,使用Python进行数据显示,使用UI5进行前台设计,使用微服务进行后台开发。该项目还在不断更新中,有兴趣的可以关注作者的博客和GitHub。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
author-avatar
鬼鬼太子_157
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有