【一步到位】Android Studio 实现安卓连接阿里云物联网云平台
最新版本目前是Android Studio Iguana | 2024.1.2,你可以到官网上去找之前的版本,手上有工作项目的不要去轻易尝试,说不定你就编译失败了,失败了也不要担心,解决就可以了。
软件下载
下载 Android Studio 和应用工具 - Android 开发者 | Android Developers (google.cn),最新版本目前是Android Studio Iguana | 2024.1.2,你可以到官网上去找之前的版本,手上有工作项目的不要去轻易尝试,说不定你就编译失败了,失败了也不要担心,解决就可以了。
本次使用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。
我们双击下载好的Android studio
安装路径选择我们刚才新建的文件下,安装完成后,会进入Android Studio的启动页面,我们点击cancel(取消)。
接下来我们选择next,进入SDK下载界面,修改安装路径选择上面新建文件夹下的SDK路径,然后我们选择SDK安装位置并点击next。
等待Finish, 到这里Android studio安装就完成了!
创建项目
安装完成后桌面生成新的图标,双击图标打开。
第一次进入,并没有工程,所以我们选择新建项目“Start a new Android Studio project”。
选择空白活动“Empty Activity”并点击“next”。
创建项目,SDA建议选择API21,自定义项目名称、项目存放路径(不能使用带中文的),其他默认,语言选择Java。
项目配置
按照如下路径,右键新建Java类,命名为AliyunIoTSignUtil,添加AliyunIoTSignUtil工具类。
复制代码到新建的类中,并保存。
//第一行保留 自己的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[i]);
if (sTemp.length() < 2) {
sb.append(0);
}
sb.append(sTemp.toUpperCase());
}
return sb.toString();
}
}
右上角,展示形式改为Project。
按照如下路径,寻找文件并打开。
添加下面代码,打开网络权限。
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
按照如下路径,打开对应文件。
对比下图,添加缺少的依赖。
implementation 'com.google.android.material:material:1.0.0'
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.0'
页面设计
项目新建后,会自动打开项目界面,如下图。
此时打开了两个文件,分别后缀是.java和.xml。可以理解为.xml是设计项目页面的部分,.java是设计项目逻辑关系的部分。
首先我们进行页面设计。
Android Studio支撑图形编辑,右上角有三种展示模式。分别为代码,代码与展示,图形设计。
首先我们通过图形界面放置四个文本框和二个按钮。
放置元素后,需要对其进行布局,Androi Stduio共有6种布局方式,分别为:线性布局“LinearLayout”、相对布局“RelativeLayout”、表格布局“TableLayout”、层布局“FrameLayout”
、绝对布局“AbsoluteLayout”、网格布局“GridLayout”,详细部分可网上自行了解。
我们这次使用的是线性布局,及元素在同一条水平或垂直的线上。
实例代码为:
<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>
将新建立的四个文本元素和两个按钮,进行线性布局,对应代码的功能可通过英语翻译大概了解,或自行查阅资料,这里不做解释。
我们设置了两个文本用于现实温度湿度数值,用两个按钮用于控制设备开关。
逻辑设计
实例代码:
//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();
}
}
自行修改阿里云三元组
注意:如下两行种的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");
可再次代码上进行二次加工。
项目运行
安装虚拟机
安装完成后启动虚拟机,并运行项目程序。
使用阿里云设备模拟器或者MQTT.fx进行硬件设备模拟,效果展示。
最后,大家就完成了本次设计。请求留下宝贵的点赞收藏。
我是一个励志成为嵌入式全栈工程师的大三学生,求实习!!!
B站/CSDN:此乃刘同学
liustu.com.cn
更多推荐
所有评论(0)