Android APP之接入AWS IOT实现发布订阅测试_awsiotmqttmanager(1)

文章目录

 产品需要接入物联网,主要面向欧美市场,所以选择使用了亚马逊物联网平台。研究了一阵子做一些总结。

一、前言

本文中是开发的APP控制端(开发语言是Android),来接入AWS IOT平台并且实现消息的订阅和发布。由于能力有限,若有错误的理解请指正。
AWS IOT的官方文档,不得不说文档写的真“详细”啊,看得云里雾里,只能参考互联网。
由于国内使用AWS物联网平台应用不多,关于Android APP的demo更是几乎没有,网上能搜到与之相关最多的是PubSub的Android项目。AWS的官方博客中有一篇文章介绍了相关内容,还有其他一些博主也有一些探究,但由于时间过于久远很多操作都发生了变化,甚至连官方GitHub上的Android PubSub示例也下架了(猜测早期应该是有的),资料实在是匮乏,只能一步一步尝试。

二、准备

在编写APP之前首先要了解一些基础知识,如mqtt协议、IOT设备、事务、策略等等,本文中还使用了AWS IAM、Cognito,也需要做适当了解。

1.使用Android Studio构建App项目
项目下的build.gradle中导入iot使用包,AWS的开发包很多,这里使用的android版的iot开发包,服务器端应该使用java版

implementation 'com.amazonaws:aws-android-sdk-iot:2.73.0'


2.注册AWS账号,需要一张信用卡,国内的也可以,没有什么难度。审核通过后即可开始测试使用。
登录AWS控制台,主要使用的是IOT Core、IAM以及Cognito
在使用IOT Core以及Cognito控制台时需要注意右上角的区域选择,曾经我就犯过在代码里创建资源后在控制台里始终找不到的错误,原因就是区域错了。另外各区域是否都支持用户池、身份池暂时不明,我就直接选择的us-east-1测试。

3.Cognito、IAM概念
从官方文档阅读可以知道AWS IOT Core允许好几种方式来安全接入,比如X.509证书、Cognito身份等等。
从网上搜索的关于之前的PubSub项目的零散代码可以看出:

 //创建证书
                    CreateKeysAndCertificateRequest createKeysAndCertificateRequest =
                            new CreateKeysAndCertificateRequest();
                    createKeysAndCertificateRequest.setSetAsActive(true);

                    final CreateKeysAndCertificateResult createKeysAndCertificateResult;
                    //调用了refresh方法
                    createKeysAndCertificateResult =
                            mIotAndroidClient.createKeysAndCertificate(createKeysAndCertificateRequest);
                    Log.i(Constants.TAG,
                            "Cert ID: " +
                                    createKeysAndCertificateResult.getCertificateId() +
                                    " created.");

// store in keystore for use in MQTT client
// saved as alias "default" so a new certificate isn't
// generated each run of this application
                    Log.d(Constants.TAG,certificateId);
                    Log.d(Constants.TAG,createKeysAndCertificateResult.getCertificatePem());
                    Log.d(Constants.TAG,createKeysAndCertificateResult.getKeyPair().getPrivateKey());

                    AWSIotKeystoreHelper.saveCertificateAndPrivateKey(certificateId,createKeysAndCertificateResult.getCertificatePem(),
                            createKeysAndCertificateResult.getKeyPair().getPrivateKey(),
                            keystorePath, keystoreName, keystorePassword);

该示例中实际是创建了一个Keystore文件存储在App的私有目录下,并且给该Keystore附加上一个IOT策略(一定要加,不加连接不了),然后调用manager去连接,如下:

mqttManager.connect(pubSubActivity.clientKeyStore ...

在连接成功后即可订阅、发布消息了。
整个过程可以概括为:
1).app内有权限的情况下生成一个iot认可的证书,且该证书上附加了一个已存在的策略
2).iot认可这个附上策略的证书,可连接发布订阅消息

但是这种方案不适用于实际生产环节,且官方也不推荐使用(keystore文件相当于在手机上存放了一个证书),这种方案应该更适用于设备端接入AWS IOT。
在每次生成Keystore文件时在IOT的后台下的证书菜单里,都会对应有一个证书。而手机APP一旦卸载之前生成的Keystore文件也会消失,再次调用会重新生成新的证书,旧的证书依然存在但其实已失效(刚开始测试使用这种方案时App经常卸载重装,iot的后台的证书菜单下就多了很多无用的数据)。

对于移动端APP推荐使用Cognito身份来接入AWS IOT平台。
Cognito没有做细致研究,仅使用了身份池。身份池的概念相当于初始进入APP时是一个游客身份,经过“登录”后变成了另一个经过验证的身份,所拥有的权限也发生了变化。我测试了身份提供商是Login with Amazon和自定义身份,其他如用户池、google等请自行测试。

4.步骤详解
1)配置身份池
在这里插入图片描述
这里经过身份验证的我选择了Amazon和自定义,下一步生成对应的IAM角色,直接生成新的角色,分别命名auth_xx和unauth_xx,具体名称不限只需要记住哪个对应哪个就行。

输入
Amazon身份提供商在该处填写应用程序ID,该应用ID是在Login With Amazon控制台注册生成的ID,还是有点麻烦的,请自行查看官网文档进行注册。
在这里插入图片描述
最终填写的是图示中箭头的这个id。

自定义身份提供商可自己定义一个标识,合法即可,这个标识后续在android和自有服务器端都会使用到。

填写好后其他默认不需要修改,直接创建审核通过即可。

2)补充IAM角色策略

进入IAM控制台后点击角色,找到上面创建身份池时命名的两个角色名。
点击给经过验证的auth_xx的这个角色,点击附加策略。
在这里插入图片描述
测试可以点击附加策略后选择AWSIoTFullAccess全部权限,也可以点击创建内联策略自定义一个,json格式串可以网上搜,自定义主要可以缩小一些权限范围,后面再讲权限策略设置。

至此可以简单理解下:一个用户下载了APP,刚使用时是一个游客,当用户使用了Amazon账户联合登录或者服务器端对他进行了验证合法后用户就获得了auth_xx这个角色的权限,就能和IOT互动了。

3)创建IOT Policy
为什么还需要创建Iot Policy呢,因为官方文档里介绍这种接入AWS IOT的方式是由IAM角色的权限策略和这个identity身份上的权限策略组合而定的,至于两者的权限是如何合并界定参考官方,我也没细致研究。(题外话:如果给unauth_xx身份直接赋予了AWSIoTFullAccess权限是后不用附加IOT策略也是可以连接IOT平台的)
点击安全性->策略,创建一个新策略,内容也可以先设置全部权限,稍后和上面IAM一样再进行细分。
在这里插入图片描述

三 、编码

下面介绍以Login with Amzon为身份提供商接入AWS IOT 
 已准备好的工作:
   1>创建了一个身份池,关联定义了两个IAM角色,一个是未经过认证的角色,权限默认不需要更改,另一个是经过认证的角色,附加上了AWSIoTFullAccess权限。该身份提供商Amazon的ID已填写。
   2>Iot的后台创建了一个策略,策略先定义的全部权限(参照上图)
   3>注意区域选择一致

Android App中接入Login with Amazon不做详解,网上资料齐全没什么难度。
在oncreat方法里先初始化Cognito认证构造方法

credentialsProvider = new CognitoCachingCredentialsProvider(
                getApplicationContext(), // Context
                "XXXXXXXXXXXX", //Cognito控值台创建的身份池ID
                Regions.US\_EAST\_1 // 身份池所属区域
        );

题外话:如果是使用的自定义身份验证的方式,就不能使用这个构造方法了,要使用下面这个

DeveloperAuthenticationProvider developerProvider = new DeveloperAuthenticationProvider
                (null, Cognito\_Pool\_ID, Regions.US\_EAST\_1,baseApplication);
credentialsProvider = new CognitoCachingCredentialsProvider( context, developerProvider, Regions.US\_EAST\_1 );

developerProvider是需要自己实现的一个方法,继承自AWSAbstractCognitoDeveloperIdentityProvider

public class DeveloperAuthenticationProvider extends AWSAbstractCognitoDeveloperIdentityProvider {

    private static final String developerProvider = "xxxxxx";//这是自定义的身份提供商名
    private BaseApplication baseApplication;
	//构造方法
    public DeveloperAuthenticationProvider(String accountId, String identityPoolId, Regions region, BaseApplication baseApplication) {
        super(accountId, identityPoolId, region);
        this.baseApplication = baseApplication;
    }
    .............

这个自定义类中主要是要实现refresh方法

@Override
    public String refresh() {
        String result = "";
        String getIdenttid = "";
        String token="";
        try {
            //这里要调用后台服务器方法来获取权限,这里就简单传了存在sp中的用户ID到后台服务器
            String appToken = (String)baseApplication.get("token","");
            result = AcceletNetWork.synGet(HttpUrls.GetCognitoToken+"&appToken="+appToken);//这里采用的是同步
            Gson gson = new Gson();
            TokenDataBean dataBean = gson.fromJson(result, TokenDataBean.class);
            getIdenttid = dataBean.getIdentityId();
            token = dataBean.getToken();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //服务器返回两个参数,update一下
        update(getIdenttid,token);
        return token;
    }

服务器也是java编写,代码简单提一下:
服务器使用的AWS java版的SDK,且有V1、V2的版本,请注意区别。

try {
			Map<String, String> logins = Collections.singletonMap(developerIdentityProviderName, "single" + ":" + user_sn);//developerIdentityProviderName是在身份池里填写的自定义标识,第二个参数是唯一标识,这里采用的是用户user\_sn
			Builder builder = GetOpenIdTokenForDeveloperIdentityRequest.builder()
					.identityPoolId(identityPoolId)//身份池ID
					.logins(logins)
					.tokenDuration(3600\*24L);//有效期
			GetOpenIdTokenForDeveloperIdentityRequest request = builder.build();
			
			GetOpenIdTokenForDeveloperIdentityResponse response = getClient().getOpenIdTokenForDeveloperIdentity(request);
	//最主要的就是getOpenIdTokenForDeveloperIdentity方法了,官网文档介绍一定要从后台调用
			
			if(response != null) {
				JSONObject identityJson = new JSONObject();
				identityJson.put("identityId", response.identityId());
				identityJson.put("token", response.token());
				return identityJson;
			}			
		} catch (Exception e) {
			e.printStackTrace();
		}

题外话结束,回到LWA版。在使用亚马逊账户登录完成后会返回一个token,更新登录映射

public void onSuccess(AuthorizeResult result) {
                        String token = result.getAccessToken();
                        if (null != token) {
                            /\* The user is signed in \*/
                            Map<String, String> logins = new HashMap<String, String>();
                            logins.put("www.amazon.com", token);//标识名固定为www.amazon.com
                            credentialsProvider.setLogins(logins);//调用setLogins
                       		attachPolicy();
                        } else {
                            /\* The user is not signed in \*/
                        }
                    }

然后附加IOT策略

 private void attachPolicy() {
        try{
            AWSIotClient awsIotClient = new AWSIotClient(credentialsProvider);
            awsIotClient.setRegion(Region.getRegion(Regions.US\_EAST\_1));

            AttachPolicyRequest attachPolicyRequest = new AttachPolicyRequest()
                    .withPolicyName("xxxxx")//IOT控制台里创建的策略名称
                    .withTarget(credentialsProvider.getIdentityId());//身份池的唯一ID
            awsIotClient.attachPolicy(attachPolicyRequest);
        }catch (Exception e){
            e.printStackTrace();
            Log.d(Constants.TAG,"附加策略异常");
        }

    }

附加IOT策略说明:网上搜有不少介绍使用AWS CLI给identityId加上策略,更有甚者说没有API可调用的。
还是得参考官方文档啊,文档里介绍了核心代码但就是不提供完整的PubSub示例,无语。
这里附加IOT策略需要注意:
1>构造的awsIotClient需要有attachPolicy的权限,之前IAM里经过身份验证的角色赋予了AWSIOTFull全部的权限,所以没问题,但是你如果改了那就要加上AttachPolicy。
2>IOT控制台里创建的策略名称也要正确,如果区域不一致会报找不到策略的错误。如果是控制台里复制时注意左右不要有空格。
3>identityId身份,IOT策略附加到这个角色身上,这个身份对应的一个唯一ID,在Cognito管理后台可以查看。
在这里插入图片描述
这个身份ID很有用处,后面要用它连接AWS IOT CORE以及解决缓存问题。
4>由于attachPolicy方法没有回调函数之类的反应是否策略附加成功,各个管理后台也没有查看的地方。但我试了修改策略名称或者随便填一个身份ID,都会报错,所以只要程序没有报错那么策略就附加成功了。

接下来就是准备连接AWS IOT进行MQTT测试了,初始化awsIotMqttManager并进行连接

				String tIdentityId= credentialsProvider.getIdentityId();
                awsIotMqttManager = new AWSIotMqttManager(tIdentityId, "xxxxxxxxxx");
                awsIotMqttManager.setCleanSession(true);//默认为true,类似于一个连接如果断线后服务器不再保留其相关的信息
                awsIotMqttManager.setKeepAlive(10);// 将keepalive的时间设置小于服务端的超时时间,则客户端每隔 keepalive的时间就会给服务端发一个心跳包,默认是300s,单位是s
                awsIotMqttManager.setAutoReconnect(true);//断开后自动重连
                awsIotMqttManager.setMaxAutoReconnectAttempts(1);//设置断网后重新尝试连接的次数,不设的话默认值为-1 ,一直试着连接

                try {
                    awsIotMqttManager.connect(credentialsProvider, (status, throwable) -> {
                        if(throwable!=null){
                            Log.d(Constants.TAG,throwable.toString());
                        }
                        if (status == AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.Connecting) {
                            Log.d(Constants.TAG,"正在连接中");
                            
                        }else if(status == AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.Connected){
                            Log.d(Constants.TAG,"连上了");
                        }else if (status == AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.Reconnecting) {
                            Log.d(Constants.TAG,"重连");
                        }else if (status == AWSIotMqttClientStatusCallback.AWSIotMqttClientStatus.ConnectionLost) {
                            Log.d(Constants.TAG,"失去连接");
                        }else{
## 最后

**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/2c05c11ae6f1097ee848c0bfe43f6a51.png)

![img](https://img-blog.csdnimg.cn/img_convert/8fe53d5eda07e2b4268a0b8523113ccf.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/42055fb4f64f5a6ce87c5a208c3973be.png)

 ![img](https://img-blog.csdnimg.cn/img_convert/303c27320fafbf1397a050fff5cefce5.png)

![img](https://img-blog.csdnimg.cn/img_convert/15070e395f3bf84bb96655537d679c83.png)

![img](https://img-blog.csdnimg.cn/img_convert/9c596c79c8b3ed9ad3d62e6a4ce946e0.png)

![](https://img-blog.csdnimg.cn/img_convert/798862929b4dbc0c03d286d4c893bd27.png)

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!



[外链图片转存中...(img-h4HQ2LRN-1715537383375)]

[外链图片转存中...(img-dPs7Svn5-1715537383376)]

[外链图片转存中...(img-CG0gIs0w-1715537383376)]

 

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!


  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值