android开发

智能家居控制系统移动端(毕设)

移动端模块设计
Client module design

Ⅰ.android应用界面设计(Interface design of client)

· Android studio

Android studio是谷歌基于IntelliJ IDEA开发的一套android应用集成开发环境IDE。作为谷歌推出的欲取代Eclipse进行安卓端应用开发环境,其一出生就具备强大的功能,处理支持Java语言进行开发,同时通过NDK导入支持Koltin和Lua语言开发,并集成了丰富的开发工具包括Gradle构建工具和一些测试工具,性能工具与网络监控的工具等。除基本开发环境外,其还自带一套安卓虚拟机系统供开发人员进行调试开发。

经过不断的版本迭代,Android studio已经发展到4.1版本,并提供包括平板电脑和可穿戴设备的开发功能。在不断优化下,经历了初期Bug优化后,现在已成为主流android应用开发环境。

· Android 应用界面

Android移动端应用APP主要有三个界面,分别由3个Activity实现,它们依次是登陆界面(login),主界面(meun)和数据库可视化界面(database)。

Login界面功能如名字所示,主要提供登录身份验证服务。测试按钮主要是测试与服务端Socket连接是否可行,点击即可绑定连接服务端的IP地址和端口并测试。当测试连通时表示服务端Socket在线,可以进行后续访问服务,同时客户端打开登录操作权限,允许操作者进行登录验证操作。身份登录验证分为账户密码验证和人脸信息验证,分别发送输入框输入的账号密码数据和人脸图像数据Base64编码数据。

图1-1 登陆界面功能展示
Figure 1-1 Login interface function display

主界面主要提供直接的实时数据展示,最上方会显示当前授权的操作者用户名,操作者可以直接通过数据更新按钮向服务端请求当前实时数据。当服务端返回数据后会在主界面直接展示,并显示数据刷新时间。每个终端都可以实现开关灯操作,当点击这些按钮时会向服务端发送相应请求,服务端解析后会向Zigbee终端下达相应指令。除此之外,还可以在此几面打开新的Activity,去实现数据库的数据展示。当在下方选择对应数据展示后,会开启新的界面去展示数据。

图 1-2 主界面展示
Figure 1-2 Main interface display

当在主界面请求数据库数据访问时,应用会启动新的Activity,并根据应用选择的数据表进行相应的设置,以匹配显示的数据。同时向服务端请求响应的数据内容。当收到服务端返回的数据内容后进行处理并显示更新在界面上。

图 1-3 数据表展示
Figure 1-3 Data table displa
y

Ⅱ. android应用通讯框架(communication of client)

· Java Socket通讯

Android移动应用开发采用Java语言编写,其中通讯方式与服务端相同,采用Socket通讯。其本质上还是TCP/IP协议报文传输。Api文件封装于Java.net.socket中。

Socket使用与C#服务端使用类似。在移动端app设计中,其担任客户端角色,并不需要建立一个Socket服务器来持续侦听请求,而是需要访问服务端时,直接连接服务端Socket server,故只需要设计Java Socket Client即可。

因为android系统特性,所有联网访问请求必须运行在子线程中,以防止联网程序的卡顿,逻辑死循环,异常等影响到主进程。故网络请求程序需要运行在子线程中。也因此对接收数据的处理,例如UI更新显示等也不能在子线程中执行,需采取其他手段。在通讯环节需要开辟两个子线程,分别用于建立Socket客户端连接服务端程序并持续接收数据,以及向服务端发送请求数据。

Socket数据收发设计与服务端不相同,因为Android Studio为Java开发提供的Sdk工具十分齐全故对数据的接收采用新逻辑,使用Java.io里面提供的InputStream,PrintWriter,BufferedWriter和OutputStreamWriter实现。其过程为通过InputStream获取Socket的输入流,使用InputStream内部的read函数即可获取接收到的数据,格式为Byte[]字节流。发送时通过BufferedWriter封装预发送的数据,使用OutputStreamWriter实例化发送类函数,实例化构造通过PrintWriter实现,并设置autoFlush,即发送Buff内部数据发送完毕后自动关闭发送流。具体实现代码如下:

public class Socketclient extends Thread {//客户端线程带数据接收
      @Override
      public void run() {
          try {
              so = new Socket(IP,PORT);//定义Socket client
              in = so.getInputStream();//获取socket的输入流
              out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(so.getOutputStream())), true);//定义输出流
              byte[]by=new byte[1024];
              while(true) {
                  int len=in.read(by);//数据长度
                  String recmsg;
                  if(len>0) {
                      recmsg = new String(by, 0, len);//数据
                      handler1();//访问主线程内容
                    }
                  }
              }
          } catch (IOException e) {
              e.printStackTrace();//异常抛出
          }
      }
  }
public class socketsend  extends Thread {//数据发送线程
        private String sendmeg;
        public void setSendmeg(String s , int i)//发送数据函数
        {
            this.sendmeg = s;//获取发送数据字符串
        }
        @Override
        public void run()  {
            try {
                out.write(sendmeg);//发送数据
                out.flush();//发送流关闭
                }
            }catch (InterruptedException e)//异常抛出
            {
            }
        }
    }

最后,由于Android系统的权限管理限制,使用WIFI,数据流量进行网络服务需要申请相应网络权限。因此在移动端APP网络模块设计开发中还需要在Androidmanifest.xml清单文件中打开相应权限。权限清单为:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

· 客户端通讯指令

客户端通讯主要内容是应用发送的请求指令,其指令内容是面向用户的。指令内容代表用户实际下达内容,与监测端Zigbee网络接收的指令不同,对于客户端下达的指令,使由服务端识别指令内容,然后根据下达内容判定用户指令意图,按照约定内容向监测端Zigbee网关下达指令。

图 2-4 指令传输流程
Figure 2-4 Instruction transmission flow
客户端指令/String指令内容响应Zigbee指令/Byte[]
Tryconnect测试与服务端的连接成功响应accessable
Facelogin请求人脸登录响应waitface
imageend人脸数据发送结束
confirm:*请求账号密码登录成功响应confirmed
logindata请求数据库login表数据login表json数据
operatedata请求数据库operate表数据operate表json数据
bedroomdata请求数据库卧室数据卧室传感器采集json数据
livingroomdata请求数据库客厅数据客厅传感器采集json数据
kitchendata请求数据库厨房数据厨房传感器采集json数据
realtimedata请求实时传感器数据实时传感器采集json数据0x3A, 0x00, 0x01, 0x0A, 0x00, 0x00, 0x23, 0x00
olx/clx打开/关闭x号终端灯0x3A, 0x00, 0x0x, 0x0A, 0x01, 0x30, 0x23/                   0x3A, 0x00, 0x0x, 0x0A, 0x00, 0x31, 0x23
表 2-1 指令表
Table 2-1 instruction list

Ⅲ. android应用数据处理(data processing of client)

· 实时数据处理

实时数据处理包括两方面,分别为向上与向下两侧。向上为一些实时的通讯指令上传,主要为安全性验证中的登录验证和一些请求指令。向下为对返回的实时传感器感知数据进行处理。向上处理仅需要将对应指令使用Socket通讯服务发送至服务端系统即可,主要实时数据处理为向下处理。

在meun Activity中设计了访问实时数据的操作。在服务端响应了指令请求realtimedata后,会向监测端请求传感器数据,收到数据后临时保存至sensordata类中,使用类定义成员函数data2json()将数据转换为json格式字符串,并回传至android移动端应用。即实时数据为一组包含传感器数据的json格式字符串。

在移动客户端应用中使用Android studio提供的org.json包。内部的JSONException和JSONException提供处理解包json格式的方法。使用JSONObject定义一个json对象实例,此函数可以将json格式字符串实例化为一个Json数据,以便后续操作对json数据进行处理。当原始json数据进行了嵌套封装,就需要对json数据进行脱壳,获取其内部真实数据,使用jsonObject.getJSONObject(“head”)来提取目标head下的json数据。对脱壳解嵌套后的数据进行jsonObject.getString(“value”)操作可以提取对应Value下的数据。具体实现代码抽象为:

JSONObject jsonObject = new JSONObject(s);
JSONObject result = jsonObject.getJSONObject(“bedroom”);
result.getString(“temperature”)//获取温度数据

· 数据库数据处理

除实时数据获取外,移动端应用还具备通过服务端访问数据库数据的功能。SQL Server数据库提供了3个数据表分别存储用户数据信息,系统操作信息和传感器数据。但服务端与移动端进行匹配,设置了5个数据库查询指令,分别为logindata,operatedata,bedroomdata,livingroomdata和kitchendata,他们分别返回用户数据信息,系统操作信息,卧室传感器感知数据,客厅传感器感知数据和厨房传感器感知数据。数据依旧采用Json格式字符串。其嵌套脱壳,value提取依旧采用org.json包提供的方法解决。不过此时嵌套的JSON数据为平行嵌套,需要利用Json数组来逐一提取,使用的方法成员为JSONArray。利用JSONArray对Json数据处理:

JSONArray dataarray = jsonObject.getJSONArray(serchorder);

然后循环对比查找预匹配的数据head,方法如下:

JSONObject objectdata = dataarray.getJSONObject(i);

数据提取后使用Android studio提供的Listview进行显示。使用Listview显示前,需要利用适配器对数据进行预处理。通过定义一个List类来存储解析出的数据库数据。同时新建一个item.xml来设置数据库可视化的列表名。列表约定为4列,名字根据请求的数据动态显示。新建一个list命名为listdata方便适配器进行数据绑定。定义为:

List<Map<String ,Object>> listdata;

利用map函数将数据进行key-value绑定,并添加进listdata中。实现为:

Map<String,Object> map = new HashMap<String, Object>();
map.put(key,value);
listdata.add(map);

数据处理完毕后,即为可视化操作,将数据绑定至适配器,提供给Listview显示。适配器使用SimpleAdapter实现。代码为:

listView.setAdapter(new SimpleAdapter(this,listdata,R.layout.item,from,to));

from与to分别为定义的两个字符串数组和整型数组,实现将item.xml设置的列名与数据的Key值绑定,便于在正确的列显示正确的value值。

· Activity数据处理

在移动端应用程序中还存在APP内部的数据处理传输。其分为两部分,一部分为同一个Activity内部数据处理传输,另一部分为Activity之间的数据处理传输。其中最重要,最主要处理为Activity之间的数据处理。

  • Activity内部数据处理

Activity内部数据处理主要是线程间的数据处理传输问题。由于移动端所有的数据请求借助于网络服务实现。android系统规定所用联网服务必须在子线程中实现,不得定义在主UI线程中。这样处理是为了解决android系统之前卡顿的现象,因为网络服务的响应时间远高于操作系统级别的响应,如果在主UI线程中定义网络服务,当出现网络阻塞时,会发生连带现象进而阻塞UI线程,造成使用卡顿。

因为网络请求的数据也是通过网络返回的,所以实际接受数据是在子线程中完成的。然而子线程不能干扰主UI线程,故无法在子线程中将接受的数据传输给UI进行显示。需要借助handler实现。

Handler是Java中一个用于异步消息处理的函数方法。本质是一个消息队列,通过异步处理收集所有线程中的数据并放入队列,然后对队列中的消息进行处理。相当于一个线程中的缓冲池,因此常常用于在子线程中更新UI界面。

在移动端APP线程数据处理时采用Handler解决,先定义并实例化一个Handler,使用handleMessage(Message message)子成员函数来实现数据传输。然后使用Handler.Post()方法调用Runnable方法开辟一个子线程处理传输到Handler里的数据,并在此可以实现UI界面的更新。具体实现如下:

Handler handler = new Handler(){
        @Override
        public void handleMessage(Message message){
            super.handleMessage(message);
        }
    };//使用Handler方法
    private void handler1(final String s) {//供其他线程调用
        handler.post(new Runnable() {//使用Runable方法
            @Override
            public void run() {//线程执行主体
               dealjson(s);//数据处理操作,UI更新在此完成
            }
        });
    }
  • Activity之间数据处理

Activity之间的数据传递最为重要也最为频繁。当移动端APP切换到新的Activity显示新的界面UI时,需要许多原先已知的数据作为支撑。比如每个Activity基本涉及了网络服务线程,均需要使用Socket进行通讯,Socket通讯所需的IP地址与PORT端口在登录时一次设置完成,后续的访问均访问此IP&PORT绑定的服务端。所以这些必须数据需要在Activity进行切换的同时一并传输至新的Activity。

Activity之间数据传输有两种,分别是Intent直接数据绑定传输,二是使用Intent+Bundle绑定传输。

第一种是在利用Intent打开新的Activity时一并绑定并传输,绑定采用key-value模式,通过Intent.putExtra(key,value)语句进行绑定。读取时采用Intent.getXXXExtra(key)语句实现,其中XXX为绑定关键字Key对应的数据类型。这种方式一般使用传递少量数据,在本设计中,基本为大数据传输,故不采用此方法。

第二种是将数据以Key-value模式统一绑定在一个Bundle中,然后将Bundle添加至Intent中,随Intent一并传输至新Activity。Bundle数据添加语句为Bundle.putXXX(key,value);,获取Bundle中的数据操作为Bundle.getXXX(key),XXX为关键字Key对应的数据的数据类型。将Bundle放到Intent中的操作为Intent.putExtra(Bundle);

在移动端APP设计开发中有许多数据需要在Activity之间传递,表列出了所有在Activity之间传递的数据及其详细信息。

源Activity目的ActvityKey类型用途
Login(登录)Meun(主界面)ipStringSocket服务端IP地址
Login(登录)Meun(主界面)portIntSocket服务端端口
Login(登录)Meun(主界面)userString登录用户的用户名
Meun(主界面)Datatable(数据库可视化)ipStringSocket服务端IP地址
Meun(主界面)Datatable(数据库可视化)portIntSocket服务端端口
Meun(主界面)Datatable(数据库可视化)typeInttype取值范围为1-5,分别对应5个数据库访问操作。根据type值的不同,需要对数据库可视化界面UI进行更新,以适配表名和列名
表 3-1 Activity间数据传递
Table 3-2 Data passing between activities

· 图像数据处理

移动端APP应用在设计账户密码登录的同时,还实现了人脸识别登录的服务,因为人脸认证识别功能部署在服务端一侧,故移动端APP仅需要将图像数据按照服务端要求进行Base64编码后发送即可。

移动端APP设计图像信息通过手机自带相机获取。Android Studio提供了媒体访问工具MediaStore,对于手机摄像头数据的捕获采用MediaStore工具下的MediaStore.ACTION_IMAGE_CAPTURE实现。同时创建文件保存对应图形信息。最后利用Intent打开摄像头工具,开启Intent数据回传函数,接受回传的图像信息。具体实现如下:

File dir = new File(Environment.getExternalStorageDirectory(), "pictures");//定义图像信息存储文件
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, 8888);//使用Intent启动服务


protected void onActivityResult(int requestCode, int resultCode, Intent data) {//图像信息回传函数
 super.onActivityResult(requestCode, resultCode, data);
 if (requestCode == 8888 && resultCode == RESULT_OK) {//收到数据
     Bitmap photo = (Bitmap) data.getExtras().get("data");
//将图像数据转换为bitmap数据
     ByteArrayOutputStream outputStream = new ByteArrayOutputStream();//将Bitmap数据转换为字节流数据
     photo.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);
//将图像转换为JPEG格式,保存质量为50%
     String image = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
//对Jpeg格式数据进行Base64编码
        }
    }

留言

您的邮箱地址不会被公开。 必填项已用 * 标注