Part 3 - 物联网移动App架构设计

2021-08-23
5分钟阅读时长

【版本】

当前版本号v20220929

版本修改说明
v20220929修正一个缺失的图片
v20220915删减部分代码,避免出现编译错误
v20220823初始化版本

【实验名称】 实验3.1 Java 多线程编程模型

【实验目的】

  • 掌握 Java 的简单的多线程编程

【实验环境】

  • 一台运行 64 位版本的 Windows(8、10 或 11)、Linux、macOS(10.14 Mojave 或更高版本)或 Chrome 操作系统的计算机。
  • 计算机可以访问互联网。
  • 内存:至少6G
  • 硬盘:至少空余20G
  • JDK 8或以上
  • IntelliJ IDEA

【实验资源】

【实验要求】

  • 基于现有的代码进行完善,实现甲和乙2个单独的线程运行,其中甲进行倒数计数,乙进行正数计数。

【实验步骤】

  1. 安装JDK8,打开IDEA,创建一个Java项目。

  2. 创建类ThreadTest,代码如下所示。

package iot.app.thread;

public class ThreadTest {
    public static void main(String[] args){
        //甲从7数到1
        new Thread(new DecCounter("甲",7)).start();
        //乙从1数到7
        new Thread(new AscCounter("乙",7)).start();
    }

    public static class DecCounter implements Runnable{

        private String name;
        private int num;

        public DecCounter(String name,int num){
            this.name=name;
            this.num=num;
        }
        @Override
        public void run() {
            for(int i=num;i>0;i--) {
                System.out.println(name+":"+i);
                try {
                    Thread.sleep(1000);//睡1秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static class AscCounter implements Runnable{

        private String name;
        private int num;

        public AscCounter(String name,int num){
            this.name=name;
            this.num=num;
        }
        @Override
        public void run() {
            //TODO:请完善这里的代码,实现正数计数。
        }
    }
}

  1. 完善内部类AscCounter的代码,实现正数计数(参考DescCounter)。

【实验名称】 实验3.2 Java 编写观察者模式例子

【实验目的】

  • 掌握使用 Java 编写观察者模式

【实验环境】

  • 一台运行 64 位版本的 Windows(8、10 或 11)、Linux、macOS(10.14 Mojave 或更高版本)或 Chrome 操作系统的计算机。
  • 计算机可以访问互联网。
  • 内存:至少6G
  • 硬盘:至少空余20G
  • JDK 8或以上
  • IntelliJ IDEA

【实验资源】

【实验要求】

  • 基于现有的代码进行完善,实现每当报社(目标对象)发行新的报纸,甲和乙两位读者(观察者)都能够立刻收到通知。

【实验步骤】

  1. 安装JDK8,打开IDEA,创建一个Java项目。

  2. 请完善以下代码中的PaperOfficerun方法

项目架构

src
  |-iot.app.observer
    |- Observer
	|- PaperOffice
	|- Reader
	|- Subject

Observer.java

package iot.app.observer;

/**
 * 观察者
 */
public interface Observer {
    /**
     * 观察目标对象
     * @param sub
     */
    void observe(Subject sub);

    /**
     * 更新目标对象数据
     */
    void update();
}

PaperOffice.java

package iot.app.observer;

import java.util.LinkedList;
import java.util.List;

/**
 * 报社
 */
public class PaperOffice implements Subject,Runnable {



    private String paper;

    private List<Observer> obsList=new LinkedList<>();

    @Override
    public void attach(Observer obs) {
        obsList.add(obs);
    }

    @Override
    public void notifyObservers() {
        for(Observer obs:obsList){
            obs.update();
        }
    }

    @Override
    public Object getData() {
        return paper;
    }

    public void setPaper(String paper) {
        this.paper = paper;
    }

    @Override
    public void run() {
        String[] ps=new String[]{"周一晚报","周二晚报","周三晚报","周四晚报","周五晚报","周六晚报","周日晚报"};
        int i=0;
        while(i<ps.length){
            //TODO:请完善此方法
        }
    }

    public static void main(String[] args){
        PaperOffice po=new PaperOffice();
        Reader a=new Reader("甲");//读者甲
        Reader b=new Reader("乙");//读者乙
        a.observe(po);//甲观察报社
        b.observe(po);//乙观察报社
        new Thread(po).start();
    }
}

Reader.java

package iot.app.observer;

/**
 * 读者
 */
public class Reader implements Observer{

    private Subject sub;
    public String name;

    public Reader(String name) {
        this.name=name;
    }

    /**
     * 观察
     * @param sub
     */

    @Override
    public void observe(Subject sub) {
        this.sub=sub;
        sub.attach(this);
    }

    /**
     * 更新目标对象数据
     */
    @Override
    public void update() {
        System.out.println(name+"收到"+sub.getData());
    }

}

Subject.java

package iot.app.observer;

/**
 * 目标对象
 */
public interface Subject {

    /**
     * 关联观察者
     * @param obs
     */
    void attach(Observer obs);
    /**
     * 通知所有观察者
     */
    void notifyObservers();

    /**
     * 获取数据
     * @return
     */
    Object getData();

}

【实验名称】 实验3.3 MVVM架构实现

【实验目的】

  • 掌握 Android 的MVVM架构设计

【实验环境】

  • 一台运行 64 位版本的 Windows(8、10 或 11)、Linux、macOS(10.14 Mojave 或更高版本)或 Chrome 操作系统的计算机。
  • 计算机可以访问互联网。
  • 内存:至少6G
  • 硬盘:至少空余20G
  • JDK 8或以上
  • Android Studio

【实验资源】

【实验要求】

  • 创建新的项目 smarthome,实现登录界面的双向数据的绑定。

【实验步骤】

  1. 打开Android Studio,新建一个名为 smarthome 的项目。

  2. 打开iot_app_smarthome\app\build.gradle,注意在原有android配置项内加入以下配置。

android {
	...
    //开启数据绑定
	dataBinding {
        enabled = true
    }
}
  1. 新增LoginActivity,加入用户名输入框组件、密码输入框组件、登录按钮和填充按钮。

LoginActivity.java

  • 项目路径,其中iot.app.smarthome.ui.login是包路径。
iot_app_smarthome
  |- app
    |-src
	  |- main
	    |- java
		  |- iot.app.smarthome.ui.login
		    |- LoginActivity.java
  • 内容
package iot.app.smarthome.ui.login;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProvider;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import iot.app.smarthome.R;
import iot.app.smarthome.databinding.ActivityLoginBinding;
import iot.app.smarthome.vm.login.LoginViewModel;
public class LoginActivity extends AppCompatActivity {

    private LoginViewModel loginViewModel;
    private ActivityLoginBinding activityLoginBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //从视图中获取绑定对象
        activityLoginBinding =DataBindingUtil.setContentView(this, R.layout.activity_login);
        //设置绑定对象的生命周期拥有者为本对象
        activityLoginBinding.setLifecycleOwner(this);
        //创建 ViewModel
        loginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);
        //绑定对象设值 ViewModel
        activityLoginBinding.setLoginViewModel(loginViewModel);
	    Button loginBtn=(Button)findViewById(R.id.loginBtn);
        loginBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                //输入的用户名,展示ViewModel对应的用户名属性,查看是否一致。 
		        Toast.makeText(LoginActivity.this,"ViewModel username:"+loginViewModel.getLoginVo().username.get(),Toast.LENGTH_SHORT).show();
            }
        });
        //自动填充按钮绑定点击事件
        Button fillBtn=(Button)findViewById(R.id.fillBtn);
        fillBtn.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                //给ViewModel 的username设值,观察视图的用户名是否绑定对应的值
                loginViewModel.getLoginVo().username.set("zhangsan");
            }
        });

    }
}

activity_login.xml

  • 项目路径
iot_app_smarthome
  |- app
    |-src
	  |- main
	    |- res
		  |- layout
		    |- activity_login.xml
  • 内容
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="loginViewModel"
            type="iot.app.smarthome.vm.login.LoginViewModel" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.login.LoginActivity">

    <LinearLayout
        android:layout_width="409dp"
        android:layout_height="729dp"
        android:orientation="vertical"
        tools:layout_editor_absoluteX="1dp"
        tools:layout_editor_absoluteY="1dp"
        tools:ignore="MissingConstraints">

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="登录"
            android:textAlignment="center" />

        <EditText
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:hint="用户名"
            android:text="@={loginViewModel.loginVo.username}"
            android:inputType="textPersonName" />

        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:text="@={loginViewModel.loginVo.password}"
            android:hint="密码"
            android:inputType="textPassword" />

        <Button
            android:id="@+id/loginBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="登录" />
        <Button
            android:id="@+id/fillBtn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="填充" />

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
  1. 新增LoginViewModel

LoginViewModel.java

  • 项目路径,其中iot.app.smarthome.vm.login是包路径。
iot_app_smarthome
  |- app
    |-src
	  |- main
	    |- java
		  |- iot.app.smarthome.vm.login
		    |- LoginViewModel.java
  • 内容
package iot.app.smarthome.vm.login;

import androidx.lifecycle.ViewModel;

import iot.app.smarthome.api.Api;
import iot.app.smarthome.api.ResMsg;
import iot.app.smarthome.model.login.LoginRequest;
import iot.app.smarthome.model.login.ObservableLoginInfo;
import iot.app.smarthome.model.login.UserTokenVo;
import retrofit2.Call;

public class LoginViewModel  extends ViewModel {

    private ObservableLoginInfo loginVo;

    public LoginViewModel(){
        loginVo=new ObservableLoginInfo();
    }

    public ObservableLoginInfo getLoginVo() {
        return loginVo;
    }
    public void setLoginVo(ObservableLoginInfo loginVo) {
        this.loginVo = loginVo;
    }

}
  1. 新增ObservableLoginInfo。ObservableField 类型的值可以绑定界面组件。
  • 项目路径,其中iot.app.smarthome.model.login是包路径。

ObservableLoginInfo.java

iot_app_smarthome
  |- app
    |-src
	  |- main
	    |- java
		  |- iot.app.smarthome.model.login
		    |- ObservableLoginInfo.java
  • 内容
package iot.app.smarthome.model.login;

import androidx.databinding.ObservableField;

public class ObservableLoginInfo {
    public ObservableField<String> username=new ObservableField<>();
    public ObservableField<String> password=new ObservableField<>();
    
}
  1. 构建项目,发布到模拟器运行,观察是否数据实现了双向绑定。