【一步到位】Android Studio 实现安卓连接阿里云物联网云平台
软件下载下载 Android Studio 和应用工具 - Android 开发者 | Android Developers (google.cn),最新版本目前是Android Studio Iguana | 2024.1.2,你可以到官网上去找之前的版本,手上有工作项目的不要去轻易尝试,说不定你就编译失败了,失败了也不要担心,办理就可以了。
https://i-blog.csdnimg.cn/direct/572dcfedef8345f49452dd273cc0535a.png
本次利用Android Studio 4.0这里提供了一下版本的下载地址,可直接点击下:https://redirector.gvt1.com/edgedl/android/studio/install/4.0.0.16/android-studio-ide-193.6514223-windows.exe
软件安装
在非系统盘(C盘之外)的盘符下面新建一个文件夹定名为Android,作为软件安装目次(注意:目次不要出现中文,否则会堕落),并在文件夹下新建空文件夹定名为SDK,用于下载SDK。
https://i-blog.csdnimg.cn/direct/9bec4da7eef643c39d6906218532281a.png
我们双击下载好的Android studio
https://i-blog.csdnimg.cn/direct/97aee46343ba4ee7ba9b7beb7b1bcc1c.png安装路径选择我们刚才新建的文件下,安装完成后,会进入Android Studio的启动页面,我们点击cancel(取消)。
接下来我们选择next,进入SDK下载界面,修改安装路径选择上面新建文件夹下的SDK路径,然后我们选择SDK安装位置并点击next。
等候Finish, 到这里Android studio安装就完成了!
创建项目
安装完成后桌面生成新的图标,双击图标打开。
https://i-blog.csdnimg.cn/direct/819050a7f507481bae110723eec4fd8f.png
第一次进入,并没有工程,所以我们选择新建项目“Start a new Android Studio project”。
https://i-blog.csdnimg.cn/direct/910fb09d72a646ccb8b52b08b8144d84.png
选择空缺活动“Empty Activity”并点击“next”。
https://i-blog.csdnimg.cn/direct/c7cd703413d0433c85f42f52a45cb9fd.png
创建项目,SDA建议选择API21,自界说项目名称、项目存放路径(不能利用带中文的),其他默认,语言选择Java。
https://i-blog.csdnimg.cn/direct/dbf8aaf159844c02b89409f5bfb9d0a8.png
项目配置
按照如下路径,右键新建Java类,定名为AliyunIoTSignUtil,添加AliyunIoTSignUtil工具类。
https://i-blog.csdnimg.cn/direct/4cfc6956db8642c6bde80c61a0445df1.png
复制代码到新建的类中,并保存。
//第一行保留 自己的pack.com.example.
import java.util.Arrays;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class AliyunIoTSignUtil {
public static String sign(Map<String, String> params, String deviceSecret, String signMethod) {
//将参数Key按字典顺序排序
String[] sortedKeys = params.keySet().toArray(new String[]{});
Arrays.sort(sortedKeys);
//生成规范化请求字符串
StringBuilder canonicalizedQueryString = new StringBuilder();
for (String key : sortedKeys) {
if ("sign".equalsIgnoreCase(key)) {
continue;
}
canonicalizedQueryString.append(key).append(params.get(key));
}
try {
String key = deviceSecret;
return encryptHMAC(signMethod, canonicalizedQueryString.toString(), key);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* HMACSHA1加密
*/
public static String encryptHMAC(String signMethod, String content, String key) throws Exception {
SecretKey secretKey = new SecretKeySpec(key.getBytes("utf-8"), signMethod);
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
byte[] data = mac.doFinal(content.getBytes("utf-8"));
return bytesToHexString(data);
}
public static final String bytesToHexString(byte[] bArray) {
StringBuffer sb = new StringBuffer(bArray.length);
String sTemp;
for (int i = 0; i < bArray.length; i++) {
sTemp = Integer.toHexString(0xFF & bArray);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
右上角,展示形式改为Project。
https://i-blog.csdnimg.cn/direct/cfce0a2dce4c46e9b3295d2659fcc8dd.png
按照如下路径,寻找文件并打开。
https://i-blog.csdnimg.cn/direct/1376a8c323c146bf9ef5da4d4552aa1b.png
添加下面代码,打开网络权限。
https://i-blog.csdnimg.cn/direct/29ce6c6fb3684e9c8ae7bf95fd8e42cc.png
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 按照如下路径,打开对应文件。
https://i-blog.csdnimg.cn/direct/7408a416655e465483aa67ede8a05dec.png
对比下图,添加缺少的依靠。
https://i-blog.csdnimg.cn/direct/5367ff5efa00487f9f859e3efbe0463a.png
implementation 'com.google.android.material:material:1.0.0'
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0' 页面计划
项目新建后,会自动打开项目界面,如下图。
https://i-blog.csdnimg.cn/direct/e72d0d69f0304e5ba898479e422fc698.png
此时打开了两个文件,分别后缀是.java和.xml。可以理解为.xml是计划项目页面的部分,.java是计划项目逻辑关系的部分。
起首我们举行页面计划。
https://i-blog.csdnimg.cn/direct/a27446cf4fd9439b8b95789e87014c21.png
Android Studio支撑图形编辑,右上角有三种展示模式。分别为代码,代码与展示,图形计划。
起首我们通过图形界面放置四个文本框和二个按钮。
https://i-blog.csdnimg.cn/direct/ceaf637eb91147f58eb2cf148fa0d608.png
放置元素后,须要对其举行布局,Androi Stduio共有6种布局方式,分别为:线性布局“LinearLayout”、相对布局“RelativeLayout”、表格布局“TableLayout”、层布局“FrameLayout”
、绝对布局“AbsoluteLayout”、网格布局“GridLayout”,具体部分可网上自行了解。
https://i-blog.csdnimg.cn/direct/1e8493ed23614b2b87eca015ddd2da36.png
我们这次利用的是线性布局,及元素在同一条水平或垂直的线上。
实例代码为:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearLayoutTemperature"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginTop="96sp">
<TextView
android:id="@+id/textView1"
android:layout_width="65dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="温度"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/Temp"
android:layout_width="71dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="NuLL"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayoutHumidity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginTop="46sp">
<TextView
android:id="@+id/textView2"
android:layout_width="65dp"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="湿度"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/Humi"
android:layout_width="71dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="NuLL"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayoutButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:layout_marginTop="96sp">
<Button
android:id="@+id/open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开" />
<Button
android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="关" /> <!-- 修改为“关”以与按钮功能匹配 -->
</LinearLayout>
</LinearLayout> 将新创建的四个文本元素和两个按钮,举行线性布局,对应代码的功能可通过英语翻译大概了解,或自行查阅资料,这里不做解释。
https://i-blog.csdnimg.cn/direct/1581cdd60a7e4c5fb05e62fff1d82da3.png
我们设置了两个文本用于现实温度湿度数值,用两个按钮用于控制设备开关。
逻辑计划
实例代码:
//package ..... 这一行保留自己的
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.Toast;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
private MqttClient client;
private MqttConnectOptions options;
private Handler handler;
private ScheduledExecutorService scheduler;
//阿里云三元组
private String productKey = "k0796zJ6ms6";
private String deviceName = "QtDev";
private String deviceSecret = "04c37a6b23ca9a936e8c760d56eca377";
//Topic
private final String pub_topic = "/sys/k0796zJ6ms6/QtDev/thing/event/property/post";
private final String sub_topic = "/sys/k0796zJ6ms6/QtDev/thing/service/property/set";
private int temp = 0;
private int humi = 0;
private TextView Temp;
private TextView Humi;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Temp = findViewById(R.id.Temp);
Humi = findViewById(R.id.Humi);
Button btn_open = findViewById(R.id.open);
Button btn_close = findViewById(R.id.close);
btn_open.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开
publish_message("{\"params\":{\"Wind\":1},\"version\":\"1.0.0\"}");
}
});
btn_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//关
publish_message("{\"params\":{\"Wind\":0},\"version\":\"1.0.0\"}");
}
});
mqtt_init();
start_reconnect();
handler = new Handler() {
@SuppressLint("SetTextI18n")
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1: //开机校验更新回传
break;
case 2:// 反馈回传
break;
case 3://MQTT 收到消息回传 UTF8Buffer msg=new UTF8Buffer(object.toString());
String message = msg.obj.toString();
Log.d("nicecode", "handleMessage: "+ message);
try {
//JSON解析获取数据
JSONObject jsonObjectALL = null;
jsonObjectALL = new JSONObject(message);
JSONObject items = jsonObjectALL.getJSONObject("items");
JSONObject obj_temp = items.getJSONObject("Temp");
JSONObject obj_humi = items.getJSONObject("Humi");
temp = obj_temp.getInt("value");
humi = obj_humi.getInt("value");
Temp.setText(temp + "");
Humi.setText(humi + "");
Log.d("nicecode", "temp: "+ temp);
Log.d("nicecode", "humi: "+ humi);
} catch (JSONException e) {
e.printStackTrace();
break;
}
break;
case 30://连接失败
Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
break;
case 31: //连接成功
Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
try {
client.subscribe(sub_topic, 1);
} catch (MqttException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
};
}
private void mqtt_init() {
try {
String clientId = "a1MoTKOqkVK.test_device1";
Map<String, String> params = new HashMap<String, String>(16);
params.put("productKey", productKey);
params.put("deviceName", deviceName);
params.put("clientId", clientId);
String timestamp = String.valueOf(System.currentTimeMillis());
params.put("timestamp", timestamp);
// cn-shanghai
String host_url ="tcp://"+ productKey + ".iot-as-mqtt.cn-shanghai.aliyuncs.com:1883";
String client_id = clientId + "|securemode=2,signmethod=hmacsha1,timestamp=" + timestamp + "|";
String user_name = deviceName + "&" + productKey;
//example.自己的名字 参考package com.example.mqqttest;
String password = com.example.mqqttest.AliyunIoTSignUtil.sign(params, deviceSecret, "hmacsha1");
//host为主机名,test为clientid即连接MQTT的客户端ID,一般以客户端唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
System.out.println(">>>" + host_url);
System.out.println(">>>" + client_id);
//connectMqtt(targetServer, mqttclientId, mqttUsername, mqttPassword);
client = new MqttClient(host_url, client_id, new MemoryPersistence());
//MQTT的连接设置
options = new MqttConnectOptions();
//设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
options.setCleanSession(false);
//设置连接的用户名
options.setUserName(user_name);
//设置连接的密码
options.setPassword(password.toCharArray());
// 设置超时时间 单位为秒
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
options.setKeepAliveInterval(60);
//设置回调
client.setCallback(new MqttCallback() {
@Override
public void connectionLost(Throwable cause) {
//连接丢失后,一般在这里面进行重连
System.out.println("connectionLost----------");
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
//publish后会执行到这里
System.out.println("deliveryComplete---------" + token.isComplete());
}
@Override
public void messageArrived(String topicName, MqttMessage message)
throws Exception {
//subscribe后得到的消息会执行到这里面
System.out.println("messageArrived----------");
Message msg = new Message();
//封装message包
msg.what = 3; //收到消息标志位
msg.obj =message.toString();
//发送messge到handler
handler.sendMessage(msg); // hander 回传
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void mqtt_connect() {
new Thread(new Runnable() {
@Override
public void run() {
try {
if (!(client.isConnected()))//如果还未连接
{
client.connect(options);
Message msg = new Message();
msg.what = 31;
// 没有用到obj字段
handler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
Message msg = new Message();
msg.what = 30;
// 没有用到obj字段
handler.sendMessage(msg);
}
}
}).start();
}
private void start_reconnect() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
if (!client.isConnected()) {
mqtt_connect();
}
}
}, 0 * 1000, 10 * 1000, TimeUnit.MILLISECONDS);
}
private void publish_message(String message) {
if (client == null || !client.isConnected()) {
return;
}
MqttMessage mqtt_message = new MqttMessage();
mqtt_message.setPayload(message.getBytes());
try {
client.publish(pub_topic, mqtt_message);
} catch (MqttException e) {
e.printStackTrace();
}
}
自行修改阿里云三元组
https://i-blog.csdnimg.cn/direct/58b6d8c5d86b43bbabb2d780c3149d84.png
注意:如下两行种的R.id.Temp与Humi,与你在.xml定名的id相同。
Temp = findViewById(R.id.Temp);
Humi = findViewById(R.id.Humi); 注意:com.example.mqqttest.利用自己的名字 参考package com.example.mqqttest;
String password = com.example.mqqttest.AliyunIoTSignUtil.sign(params, deviceSecret, "hmacsha1"); 可再次代码上举行二次加工。
项目运行
安装虚拟机
https://i-blog.csdnimg.cn/direct/64acb0942cf144bca25f6c0c7f0d9222.png
https://i-blog.csdnimg.cn/direct/3c363915a0194c38986a0a5df770e2e2.png
安装完成后启动虚拟机,并运行项目程序。
https://i-blog.csdnimg.cn/direct/02a2e046793e4ccb8463ff57487145ba.png
利用阿里云设备模拟器大概MQTT.fx举行硬件设备模拟,结果展示。
https://i-blog.csdnimg.cn/direct/54cdcbbd7a284f5f88c4fc5fc73240bd.png
最后,大家就完成了本次计划。请求留下名贵的点赞收藏。
我是一个励志成为嵌入式全栈工程师的大三门生,求实习!!!
B站/CSDN:此乃刘同学
liustu.com.cn
项目展示链接:【Android Studio 实现安卓连接阿里云物联网云平台】https://www.bilibili.com/video/BV1rCpneCEfY?vd_source=4a3d8c11d455291dbb5507669d40a8b8
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]