如何用Nearby Service开发针对附近人群的精准广告推送功能

  当你想找一家餐厅吃饭,却不知道去哪家,这时候手机跳出一条通知,为你自动推送附近优质餐厅的信息,你会点击查看吗?当你还在店内纠结于是否买下一双球鞋时,手机应用给了你发放了老顾客5折优惠券,这样的广告你有拒绝的理由吗?

  这样的广告不仅不会引起用户的厌烦,还满足了用户的需求。更准确的广告推送,减少对用户不必要的打扰,提高用户对应用的满意度。那如何才能给自己的APP增加一个针对附近人群的精准广告推送功能呢?

在这里插入图片描述

  可以接入华为近距离通信服务,通过蓝牙信标消息订阅功能(Nearby Beacon Message)来实现. 在商场中部署Beacon,Beacon消息提供精确的相对位置信息,当用户走近餐厅、商店时,就会收到本店预先配置好的促销消息,例如优惠券、打折信息等等,从而可以很便捷的对本店进行推广,下图是功能演示。

在这里插入图片描述

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

1.开发准备

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

1.1 在项目级gradle里添加华为maven仓和agcp配置

  增量添加如下maven地址和agcp配置,以下不用修改,拷贝即可。

buildscript {
     repositories {
         google()
         jcenter()
         maven { url 'http://developer.huawei.com/repo/' }
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:3.4.1'
         classpath 'com.huawei.agconnect:agcp:1.2.1.301'
     }
 }
  
 allprojects {
     repositories {
         google()
         jcenter()
         maven { url 'http://developer.huawei.com/repo/' }
     }
 }

1.2 在应用级的build.gradle里面加上SDK依赖

  把近距离通信服务SDK引入,最重要的是以下com.huawei开头的SDK。

dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation 'androidx.appcompat:appcompat:1.1.0'
     implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
     testImplementation 'junit:junit:4.12'
     androidTestImplementation 'androidx.test.ext:junit:1.1.1'
     androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
     implementation "com.huawei.hmf:tasks:1.3.1.301"
     implementation "com.huawei.hms:network-grs:1.0.9.302"
     implementation 'com.huawei.agconnect:agconnect-core:1.2.1.301'
     implementation 'com.huawei.hms:nearby:4.0.4.300'
     api 'com.google.code.gson:gson:2.8.5'
 }

1.3 在AndroidManifest.xml文件里面申请网络权限、蓝牙权限和位置权限

  以下权限见名知义,比如android.permission.INTERNET就是需要网络权限,android.permission.BLUETOOTH就是需要蓝牙权限。以下权限都是必须的。

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.BLUETOOTH" />
 <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.FOREGROUND_SERVICE" />
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

2. 代码开发

2.1 初始化以及动态权限申请

  onCreate是当前activity创建时会调用的方法,在这个方法里头,我们可以做一些准备动作,比如必要权限的申请,以及检查手机网络、蓝牙、GPS是否开启等。

@RequiresApi(api = Build.VERSION_CODES.P)
 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     Log.i(TAG, "onCreate");
     setContentView(R.layout.activity_canteen);
     boolean isSuccess = requestPermissions(this, this);
     if (!isSuccess) {
         return;
     }
     Log.i(TAG, "requestPermissions success");
     if (!NetCheckUtil.isNetworkAvailable(this)) {
         showWarnDialog(Constant.NETWORK_ERROR);
         return;
     }
     if (!BluetoothCheckUtil.isBlueEnabled()) {
         showWarnDialog(Constant.BLUETOOTH_ERROR);
         return;
     }
     if (!GpsCheckUtil.isGpsEnabled(this)) {
         showWarnDialog(Constant.GPS_ERROR);
         return;
     }
     intView();
     init();
 }

  注册监听,当检测到手机蓝牙、GPS、网络未连接时,给出提示: 以下使用的是Android中的AlertDialog组件来做提示。

private void showWarnDialog(String content) {
     DialogInterface.OnClickListener onClickListener =
         new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 android.os.Process.killProcess(android.os.Process.myPid());
             }
         };
     AlertDialog.Builder builder = new AlertDialog.Builder(this);
     builder.setTitle(R.string.warn);
     builder.setIcon(R.mipmap.warn);
     builder.setMessage(content);
     builder.setNegativeButton(getText(R.string.btn_confirm), onClickListener);
     builder.show();
 }

2.2 接收Beacon消息的过程

  以下这个startScanning方法,是在onStart方法中被调用的,表示开始启动蓝牙扫描,并且以下的MessageHandler对象中,封装了4个回调方法(onFound表示获取到beacon消息,OnLost表示消息丢失,onDistanceChanged表示beacon与本手机的距离变化,onBleSignalChanged表示监测到beacon的信号变化)。

  最重要的就是以下的doOnFound方法,表示接收到beacon消息时,客户端的处理方式,可以做个性化展示。

private void doOnFound(Message message) {
     if (message == null) {
         return;
     }
     String type = message.getType();
     if (type == null) {
         return;
     }
     String messageContent = new String(message.getContent());
     Log.d(TAG, "New Message:" + messageContent + " type:" + type);
     if (type.equalsIgnoreCase(Constant.CANTEEN)) {
         operateOnFoundCanteen(messageContent);
     } else if (type.equalsIgnoreCase(Constant.NOTICE)) {
         operateOnFoundNotice(messageContent);
     }
 }

自定义展示:
  下面的方法只是简单的演示其中一种消息处理方式,包括客户端上的通知栏横幅通知、页面字体滑动展示等操作。

private void operateOnFoundCanteen(String messageContent) {
     CanteenAdapterInfo canteenAdapterInfo =
             (CanteenAdapterInfo) JsonUtils.json2Object(messageContent, CanteenAdapterInfo.class);
     if (canteenAdapterInfo == null) {
         return;
     }
     String canteenName = canteenAdapterInfo.getCanteenName();
     if (canteenName == null) {
         return;
     }
     Log.d(TAG, "canteenName:" + canteenName);
     if (!canteenNameList.contains(canteenName)) {
         return;
     }
  
     String notice = "";
     if (receivedNoticeMap.containsKey(canteenName)) {
         notice = receivedNoticeMap.get(canteenName);
     }
     int canteenImage = getCanteenImage(canteenName);
     int requestCode = getRequestCode(canteenName);
     canteenAdapterInfo.setNotice(notice);
     canteenAdapterInfo.setCanteenImage(canteenImage);
     canteenAdapterInfo.setShowNotice(true);
     canteenAdapterInfo.setRequestCode(requestCode);
  
     canteenAdapterInfoMap.put(canteenName, canteenAdapterInfo);
     canteenAdapterInfoList.add(canteenAdapterInfo);
  
     sendNotification(Constant.NOTIFICATION_TITLE, Constant.NOTIFICATION_SUBTITLE, canteenName, requestCode);
     runOnUiThread(
         new Runnable() {
             @Override
             public void run() {
                 searchTipTv.setText(R.string.found_tip);
                 loadingLayout.setVisibility(View.GONE);
                 canteenAdapter.setDatas(canteenAdapterInfoList);
             }
         });
 }

结后语

  本次给大家演示的demo用到了华为Nearby Service的蓝牙信标消息订阅功能

  基于Nearby Beacon Message能力不仅仅可以用来做广告推送,还可以实现如下相关功能:

  1. 汽车生活类应用集成Nearby Beacon Message功能后,可以识别用户靠近汽车,通知App启用无钥匙进入,记录行驶轨迹等。

  2. 办公类应用集成Nearby Beacon Message功能后,可快速准确记录员工打卡位置。

  3. 旅游、展览类应用集成Nearby Beacon Message功能后,当用户靠近某个展品或文物时候,能够获取相应的介绍信息。

  4. 游戏类应用使用Nearby Beacon Message,可以实现游戏和现实场景互动。通过现实物体触发游戏关卡,或者对参与线下活动的游戏玩家给予游戏道具奖励。

  更详细的开发指南可参考华为开发者联盟官网


往期链接:超简单集成ML kit 实现听写单词播报
原文链接:https://developer.huawei.com/consumer/cn/forum/topicview?tid=0201283747724140328&fid=18
原作者:赵照

热门文章

暂无图片
编程学习 ·

Python之OpenCV的学习(二)

图像 1.OpenCV读取图像的格式是BGR 2.shape属性 # 返回一个[h,w,c]元组,即height,width,通道数 img_shape = img.shape img_shape1 = img_shape[0] img_shape2 = img_shape[1] img_shape3 = img_shape[2]3.彩色图像与灰度图像 # cv2.IMREAD_COLOR; 彩色图像 # cv2.IMREAD_GRAY…
暂无图片
编程学习 ·

算法复杂度评价指标(大o表示法)

大O表示法(1)常见的大o数量级函数(2)其他算法复杂度表示法 基本操作数量函数T(n)的精确值并不是特别重要,重要的是Tn(n)中起决定性因素的主导部分。用动态的眼光看,就是当问题规模增大的时候,T(n)中的一些部分会盖过其他部分的贡献。 数量级函数描述了T(n)中随着n增加而…
暂无图片
编程学习 ·

GPU与深度学习

GPU与深度学习一.为什么深度学习要使用CPU 深度学习:深度学习是模拟人脑神经系统而建立的数学网络模型,最大特点是需要大数据来训练,也就需要大量的并行的重复计算。 GPU:CPU的全称是Central Processing Unit,GPU的全称是Graphics Processing Unit。在命名上,这两种器件…
暂无图片
编程学习 ·

centos下安装nginx

1,环境介绍 本文安装nginx版本为1.12.2 系统版本为centos6.9 2,软件下载 链接:https://pan.baidu.com/s/1RVhCS1-WcXXaGlnAVQPH-g 提取码:od5x 3,安装nginx 1,编译安装需要安装一些兼容包 yum install pcre-devel openssl-devel gcc-c++ htop iotop iftop nmap nc telnet…
暂无图片
编程学习 ·

SQL函数

SQL拥有很多可用于计数和计算的内建函数。SQL Aggregat函数计算从列中取得的值,返回一个单一的值。有用的Aggregate函数如下:AVG() - 返回平均值COUNT() - 返回行数FIRST() - 返回第一个记录的值LAST() - 返回最后一个记录的值MAX() - 返回最大值MIN() - 返回最小值SUM() - 返…
暂无图片
编程学习 ·

Maven工程配置(build/run委托,skipTests)

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是…
暂无图片
编程学习 ·

vue前端代码优化-1

也许有人会感觉CR没有什么卵用,只要我代码实现了功能,我完成了开发任务,我就OK了,为啥还要CR??但是CR真的是有必要的,你不仅可以发现自己代码中的不足之处,待优化点,简洁明了的代码易读别人接手也会很快。1. 比如在vue项目中只有某一个组件用某一个特别长的常量对象,…
暂无图片
编程学习 ·

【漏洞通告】Treck TCP/IP协议库“ Ripple20”漏洞通告

【漏洞通告】Treck TCP/IP协议库“ Ripple20”漏洞通告 威胁对抗能力部 [绿盟科技安全情报](javascript:void(0)😉 昨天 通告编号:NS-2020-0039 2020-06-30TA****G: Treck、TCP/IP协议库、Ripple20漏洞危害: 攻击者利用此类漏洞,可造成拒绝服务、远程代码执行等。版本: 1…
暂无图片
编程学习 ·

【牛客网】写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

题目 写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。 需要掌握 1、异或运算 两个数不相同,结果为1。两个数相同,结果为0。 2、与运算 两位同时为“1”,结果才为“1”,否则为0 3、左移 将一个二进制操作数对象按指定的移动位数向左移,左边溢…
暂无图片
编程学习 ·

导入spring源码到idea的完整步骤

导入spring源码到idea的完整步骤1.到github上找到spring-framework代码,然后将代码fork到码云上,步骤地址如下: https://cloud.tencent.com/developer/article/1589675 2.下载gradle,安装gradle(注意idea和gradle对应的版本,楼主使用的是2019.2版本的idea和5.2.1版本的gr…
暂无图片
编程学习 ·

试题 算法提高 树的直径

资源限制 时间限制:100ms 内存限制:8.0MB 问题描述树的直径 输入格式输入的第一行包含一个整数n,表示树中的点数。接下来n-1行,每行3个正整数,表示连同的两点及边的权值。 输出格式输出1行,包含一个整数,表示树的直径。 样例输入 7 1 2 1 1 3 1 2 4 1 3 5 1 4 7 1 4 6…
暂无图片
编程学习 ·

GitHub Git bash 常用命令

这里写自定义目录标题1. 下载安装2. 设置GitHub用户信息3. 进入文件夹4. 仓库连接4.1 初始化本地仓库4.2 GitHub上新建一个仓库4.3 配置SSH Key4.3.1 没有的话4.3.2 有的话4.3.3 测试一下4.3 建立连接4.4 文件上传4.5 查看提交记录4.6 pull操作 1. 下载安装 点击下载windows版本…
暂无图片
编程学习 ·

如何创建MySQL存储过程,这是一个问题!且看大佬如何整理剖析

简单地说,存储过程就是一条或者多条SQL语句的集合,可视为批文件,但是其作用不仅限于批处理。本篇主要介绍如何创建存储过程和存储函数以及变量的使用,如何调用、查看、修改、删除存储过程和存储函数等。存储程序可以分为存储过程和函数,MySQL 中创建存储过程和函数使用的语…
暂无图片
编程学习 ·

Java环境安装和配置

**Java环境安装和配置 1.下载 进入下载页面:https://www.oracle.com/java/technologies/javase-downloads.html 2.点击对应版本JDK.Download进入下载界面并选择对应操作系统 3.下载后安装 已经安装过此版本的提示: 点击下一步: 更改都存储路径,不要出现特殊符号和汉字: 2.…
暂无图片
编程学习 ·

从永远到永远-SpringCloud项目实战(七)-前端框架NUXT

1、什么是服务端渲染 服务端渲染又称SSR (Server Side Render)是在服务端完成页面的内容,而不是在客户端通过AJAX获取数据。 服务器端渲染(SSR)的优势主要在于:更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。 如果你的应用程序初始展示 loading 菊花图,…
暂无图片
编程学习 ·

ROS成长-wiki-ros教程整理 二 环境搭建

此处学习是通过catkin 编译方式来学习的。 1、创建个人的工作文件夹,此文件夹是之后学习的,存放程序的根目录 mkdir -p ~/catkin_ws/src 2、创建环境 cd ~/catkin_ws/ catkin_make catkin_make 命令是创建工作区的快捷命令,第一次在工作空间中运行它,它将在工作文件夹中的“…
暂无图片
编程学习 ·

Efficient Large-Scale Multi-Modal Classification 多模态学习论文阅读

引言 多模态问题我们应该不会陌生。 Efficient Large-Scale Multi-Modal Classification(AAAI2018) 摘要 早期的互联网主要是基于文本的,但现代数字世界正变得越来越多模式。这里,我们研究多模态分类,其中一个模态是离散的,例如文本;而另一个是连续的,例如从卷积神经网…
暂无图片
编程学习 ·

C++ 宏的几个基本用法

变量定义#define PI 3.1415926 直接展开作为变量条件编译#if, #elif, #else, #endif用于条件编译: 在编译时期执行 #if 常量表达式1 语句… #elif 常量表达式2 语句… #elif 常量表达式3 语句… … #else 语句… #endif预定义#ifdef, #ifndef, defined. 它们经常用于避免头文件…