软件下载

下载 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

项目展示链接:【Android Studio 实现安卓连接阿里云物联网云平台】https://www.bilibili.com/video/BV1rCpneCEfY?vd_source=4a3d8c11d455291dbb5507669d40a8b8

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐