Advertisement

智能家居

阅读量:

课程简介

智能家居是互联网环境下物联网技术延伸的产物。它借助物联网技术实现了各类家庭设备间的互联,并提供了智能控制或定时监控的服务。相较于传统家居生活模式,在智能化改造后的小米之家不仅延续了基本的家庭居住功能,并进一步整合了建筑系统、网络通信平台以及智能家电等先进科技;同时通过智能化手段实现了从单向信息传递到双向互动交流的转变。

本次课程包含两个典型案例研究。第一个案例涉及智能摄像头技术,我将展示如何利用个人开发的Android应用程序来操控网络摄像头设备以实现对摄像头远程监控的目的。第二个案例探讨的是蓝牙继电器的应用,请注意这里我们使用了"继电器"这一专业术语来描述电子开关装置的具体应用场景。通过手机的蓝牙连接与智能设备进行交互并结合逻辑控制电路设计的方法来实现对开关的精确控制从而达到电器智能化的效果

智能家居发展情况

智能家居起源

智能家居概念自古就有,在1984年之前并未引起广泛关注与研究。然而直至1984年美国联合科技公司(United Technologies Building System)将建筑设备信息化、整合化概念应用于美国康涅狄格州(Connecticut)哈特佛市(Hartford)的CityPlaceBuilding时,真正意义上的智能建筑首次出现于这一地区,从此标志着全球开始广泛布局智能化住宅建设进程。智能家居系统经历了从简单自动化到全面智能化的四个发展阶段

1、家庭自动化

由一个中央微处理器接收相关电子产品(用于检测环境变化)的信息后再传递给其他产品

2、家庭网络

家庭网络是一种新型的技术,在其应用范围内将各种家用电器(如安全监控设备、智能照明设备)与互联网连接起来。

3、网络家电

基于数字技术、网络化技术以及智能控制技术开发新型家电产品,并对其进行优化改进;例如,在空调领域中应用广泛的就是网络空调。

4、信息家电

能够通过网络系统交互信息的家电产品。

国内发展现状

目前我国智能家居仍处于新兴产业发展阶段,并恰好处在导入与成长阶段的关键转折点上。中国政府于2013年8月14日发布了旨在扩大信息消费、促进内需增长的相关政策文件,并积极推动宽带接入服务建设以为其奠定坚实的行业发展基础。加之当前智能家电市场尚未形成成熟消费者认知体系,在这种情况下当前市场仍具有巨大的增长潜力而其行业前景则显得更加广阔

1、萌芽期/智能小区期(1994年-1999年)

概念熟悉、产品认知的阶段,还没有出现专业的智能家居生产厂商。

2、开创期(2000年-2005年)

成立了五十多家智能家居研发生产企业,没有进入国内市场。

3、徘徊期(2006-2010年)

过分夸大智能家居功能的表现之后出现, 行业用户与媒体开始对智能家居的实际应用效果提出质疑. 国内企业正在进行转型升级, 如美国的罗格朗公司与霍尼韦尔等国际巨头

过分夸大智能家居功能之后出现, 行业用户与媒体开始对智能家居的实际应用效果提出质疑. 国内企业正在进行转型升级, 如美国的罗格朗公司与霍尼韦尔等国际巨头

4、融合演变期(2011-2020年)

自2014年起, 各个企业在智能家居领域纷纷投入资源展开布局. 经过一年半左右的时间磨合, 到2015年为止, 各类合作企业均已成功进入了产出阶段. 在此期间, 智能家居新品络绎不绝地涌现出来.

智能家电

智能灯泡
智能摄像头
智能空调

蓝牙灯泡

什么是蓝牙灯泡

智能家居的一部分是将蓝牙模块与灯泡结合在一起,并通过手机与蓝牙模块实现通讯。这样调节电压板的状态就可以实现开关及触控功能了。

应用场景

住宅区;起居室;客厅;厨房;卫生间;办公场所;会议室区;地下空间;汗蒸舱;美容院;医院;疗养中心等处。

优缺点

优点:方便、快捷、能耗低、寿命长、扩展性好
缺点:受距离限制(5-10米),不稳定

基本组成

灯泡

智能家居

蓝牙模块

智能家居

手机,目标:通过手机控制灯泡的打开、关闭、点动操作

基本流程

智能家居
复制代码
    //获取本地蓝牙适配器
    BluetoothAdapter mBluetoothAdapter =  BluetoothAdapter.getDefaultAdapter();
    
    //打开手机蓝牙
    mBluetoothAdapter .enable();     
    //关闭手机蓝牙
    mBluetoothAdapter.disable();
    
    //扫描蓝牙设备
    mBluetoothAdapter.startDiscovery();
    //取消扫描蓝牙设备,减少资源的消耗
    mBluetoothAdapter.cancelDiscovery();

startDiscovery()和cancelDiscovery()应确保在工程中注册蓝牙广播收发器

复制代码
    //添加蓝牙广播接受者
    
    IntentFilter filter = new IntentFilter();
    // 开始扫描的广播
    filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);    
    // 扫描完成的广播
    filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);   
    // 发现一个可用的设备的广播
    filter.addAction(BluetoothDevice.ACTION_FOUND);
    mBluetoothReceiver = new BluetoothReceiver();
    //注册监听
    registerReceiver(mBluetoothReceiver, filter);

蓝牙广播接受者

复制代码
    class BluetoothReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
                //获取蓝牙设备
        }
    }

连接设备

复制代码
    public void connectServer(final BluetoothDevice device) {
    new Thread(new Runnable(){
        @Override
        public void run() {
        try {
                    System.out.println(randomUUID.toString());
                    BluetoothSocket clientSocket = device.
            createRfcommSocketToServiceRecord(
        UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"));
                    clientSocket.connect();
                    out = clientSocket.getOutputStream();
                    System.out.println("连接成功");
                    Looper.prepare();
                   Toast.makeText(BluetoothDemoActivity.this, "连接成功", 0).show();
                   Looper.loop();
        } catch (IOException e) {
                   e.printStackTrace();
        }
        }}).start();
    }

蓝牙2.1版本无需使用匹配的UUID即可,但该方案不具备准确性保障,而 newer versions of Bluetooth require matching UUIDs for enhanced reliability.为了避免影响兼容性,在此方案中采用了统一的匹配策略.UUID: 000011...34fb 匹配密码为:1518( newer versions of Bluetooth can be configured without password verification to reduce power consumption)

Looper充当了一个封装 msg 循环与 msg 队列的作用。\n\n由 Looper.prepare() 在单一线程内启动了一个 msg 循环。\n\n由 Looper.loop() 会一直处理 msg ,直至退出该方法。

打开灯泡

复制代码
    private void openLight() {
        if(out == null) return;
        try {
            // 向服务端写数据
            byte[] b = new byte[5];
            b[0] = (byte) 0x01;
            b[1] = (byte) 0x99;
            b[2] = (byte) 0x10;
            b[3] = (byte) 0x10;
            b[4] = (byte) 0x99;
            out.write(b);
            out.flush();
        } catch (IOException e) {
            Toast.makeText(this, "打开失败", 0).show();
            e.printStackTrace();
        }
    }

注销广播接受者

复制代码
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mBluetoothReceiver);
    }

智能摄像头

准备工作

网络摄像头,我这里采用的是 品牌: EasyN/易视眼 型号: TM007 ,产品图片如下。

智能家居

对应SDK

智能家居

我们的软件依赖于第三方智能网平台的技术支持,并且因此必须调用其提供的SDK。

工作原理

当我们购买回网络摄像头时,我们需要为其配置无线网账户及密码,以便于将我们的硬件接入物联网云平台.该平台由台湾TUTK公司http://www.tutk.com/开发推出,名为IOTC(Internet of Things Cloud 物联网云).该摄像头采用了深圳市普顺达科技有限公司 http://www.easyn.cn/提供的硬件设备.

这两家公司的网页首页截图如下:

智能家居
智能家居

当摄像头通电并配置好网络后会自动联入IOTC服务器随后进入等待状态 并持续接收来自App端发出的操作指令ApP通过硬件设备的身份信息如UID用户名以及密码登录至IOTC服务器 然后向该平台发送相应的操作指令 IOTC接收到该操作指令后将立即转发至相关设备 经由设备反馈至该平台 最终完成数据交互 这一过程凸显了智能终端与摄像头之间的高效协同 其中 在这一连串操作中扮演核心角色的是位于系统中心的核心平台——IOTC平台

智能家居

代码实现

本案例的效果图展示如下:包含两个界面的布局设计。第一个界面是一个登录界面,在文本框中依次输入设备名称(自行命名)、唯一识别码(UID)以及初始密码;其中名称是我们自行为摄像头设备命名的标识;UID可以直接获取自设备本身,并无需修改;密码则是在设备初始设置时赋值为默认值,并允许后续进行变更设置;为了防止潜在的安全威胁(例如黑客入侵),本设计的关键在于确保UID信息的安全性;通过这种方式能够有效保障系统数据的安全性与稳定性

智能家居

设计一个详细的布局方案包含两个版本的结构安排。由于第一个登录界面过于基础省略了。请提供第二个版本的详细说明

activity_camera.xml

复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <com.tutk.IOTC.Monitor
        android:id="@+id/monitor"
        android:layout_width="400dp"
        android:layout_height="320dp" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="horizontal" >
    
        <TextView
            android:id="@+id/tv_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="连接状态:" />
    </LinearLayout>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:gravity="bottom" >
        <View
            android:id="@+id/center"
            android:layout_width="10dp"
            android:layout_height="10dp"
            android:layout_centerInParent="true"
            android:layout_margin="20dp"
            android:background="#ff0000" />
        <ImageButton
            android:id="@+id/ib_left"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@id/center"
            android:background="@drawable/left"
            android:contentDescription="@null" />
        <ImageButton
            android:id="@+id/ib_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@id/center"
            android:background="@drawable/right"
            android:contentDescription="@null" />
        <ImageButton
            android:id="@+id/ib_bottom"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/center"
            android:layout_centerHorizontal="true"
            android:background="@drawable/bottom"
            android:contentDescription="@null" />
        <ImageButton
            android:id="@+id/ib_top"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@id/center"
            android:layout_centerHorizontal="true"
            android:background="@drawable/top"
            android:contentDescription="@null" />
    </RelativeLayout>
    </LinearLayout>

在本文件中添加了类库,请参见如下图所示的位置。由于类库的保密性,在该文档中不再提供下载链接。

智能家居

在核心代码中存在两个主要的Activity类

MainActivity代码如下:

复制代码
    package com.example.smartcamera;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v7.app.ActionBarActivity;
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import com.google.testapp.R;
    
    public class MainActivity extends ActionBarActivity {
    public static final String NAME = "name";
    public static final String PSW = "psw";
    public static final String UID = "uid";
    private EditText et_name;
    private EditText et_pwd;
    private EditText et_uid;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    /** * 初始化子控件
     */
    private void initView() {
    
        et_name = (EditText) findViewById(R.id.et_devname);
        et_pwd = (EditText) findViewById(R.id.et_psw);
        et_uid = (EditText) findViewById(R.id.et_uid);
    }
    /** * 点击Button 开始链接
     * * @param view
     */
    public void connect(View view) {
        String name = et_name.getText().toString();
        String pwd = et_pwd.getText().toString();
        String uid = et_uid.getText().toString();
        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd) ||
                TextUtils.isEmpty(uid)) {
            Toast.makeText(this, "数据不能为空。", Toast.LENGTH_SHORT).show();
            return;
        }
        Intent intent = new Intent(this, CameraPlayActivity.class);
        intent.putExtra(NAME, name);
        intent.putExtra(PSW, pwd);
        intent.putExtra(UID, uid);
        //跳转到播放页面
        startActivity(intent);
    }
    }

CameraPlayActivity代码如下:

复制代码
    package com.example.smartcamera;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.ImageButton;
    import android.widget.TextView;
    
    import com.google.testapp.R;
    import com.tutk.IOTC.AVIOCTRLDEFs;
    import com.tutk.IOTC.AVIOCTRLDEFs.SMsgAVIoctrlPtzCmd;
    import com.tutk.IOTC.Camera;
    import com.tutk.IOTC.IRegisterIOTCListener;
    import com.tutk.IOTC.Monitor;
    
    public class CameraPlayActivity extends Activity implements OnClickListener,
        IRegisterIOTCListener {
    private Monitor     monitor;
    private ImageButton ib_left;
    private ImageButton ib_right;
    private ImageButton ib_top;
    private ImageButton ib_bottom;
    private TextView    tv_state;
    private String      mName;
    private String      mPsw;
    private String      mUID;
    private Camera      mCamera;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                // 针对不同的连接状态做不同的处理
                case Camera.CONNECTION_STATE_CONNECT_FAILED:
                    tv_state.setText("连接失败");
                    break;
                // 如果是已经连接成功
                case Camera.CONNECTION_STATE_CONNECTED:
                    tv_state.setText("已连接");
                    // 调用play()方法,完成Camera 和Monitor 的绑定,最终将画面显示出来
                    play();
                    break;
                case Camera.CONNECTION_STATE_CONNECTING:
                    tv_state.setText("连接中...");
                    break;
                case Camera.CONNECTION_STATE_DISCONNECTED:
                    tv_state.setText("未连接");
                    break;
                case Camera.CONNECTION_STATE_TIMEOUT:
                    tv_state.setText("连接超时");
                    break;
                case Camera.CONNECTION_STATE_UNKNOWN_DEVICE:
                    tv_state.setText("未知设备");
                    break;
                case Camera.CONNECTION_STATE_UNSUPPORTED:
                    tv_state.setText("不支持的设备");
                    break;
                case Camera.CONNECTION_STATE_WRONG_PASSWORD:
    
    
                    tv_state.setText("密码不正确");
                    break;
                case Camera.CONNECTION_STATE_NONE:
                    tv_state.setText("未知错误");
                    break;
                default:
                    tv_state.setText("未知" + msg.what);
                    break;
            }
        }
    
        ;
    };
    
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        // 初始化控件
        initView();
        // 初始化数据
        initData();
        // 开始连接网络
        connectNet();
    }
    
    /** * 显示画面的核心方法
     */
    protected void play() {
        /** * 如果条件满足则<br>
         * 1 将界面monitor 和Camera 进行绑定<br>
         * 2 开始播放Camera 画面
         * */
        if (mCamera != null && mCamera.isChannelConnected(Camera.DEFAULT_AV_CHANNEL)) {
            monitor.attachCamera(mCamera, Camera.DEFAULT_AV_CHANNEL);
            mCamera.startShow(Camera.DEFAULT_AV_CHANNEL, true);
        }
    }
    
    private void connectNet() {
        // 初始化摄像头内部加载c 语言库
        Camera.init();
        // 新创建一个Camera 实例不是Android 中的Camera 而是com.tutk.IOTC.Camera.Camera
        mCamera = new Camera();
        /** * 给Camera 注册IOTC 监听<br>
         * IOTC Internet of Things Cloud 物联网云平台<br>
         * 该平台是台湾TUTK 公司推出,有偿使用,因此SDK 不开源
         */
        mCamera.registerIOTCListener(this);
        /** * 连接到IOTC 云平台<br>
         * 连接传入的是UID <br>
         * 每个硬件设备出厂时都有唯一的UID编号,并且出厂时已经将该编号注册到IOTC平台服务器<br>
         * 硬件在连接服务器的时候需要带着自己的身边标识(就是UID)
         */
        mCamera.connect(mUID);
        /** * 仅仅让硬件连接网络还是不够了,作为终端用户我们想看到云平台上的摄像头画面那么还需要用户
         * 通过用户名和密码登陆进行身份验证<br>
         * * @parameter Camera.DEFAULT_AV_CHANNEL
         * 我们可以把一个摄像头硬件理解为一个电视机,那么电视机可以有多个频道,这里我
         * 们使用默认频道即可
         * @parameter mName 用户名<br>
         * @parameter mPsw 密码<br>
         * * 当开始连接的时候,由于之前注册了Camera 监听,因此接收到的数据会以回调的形
         * 式传到形参中
         * ,因此这个时候我们就去IRegisterIOTCListener 的回调方法中等数据就行了
         */
        mCamera.start(Camera.DEFAULT_AV_CHANNEL, mName, mPsw);
    }
    
    /** * 从上一个Activity 中获取用户数据
     */
    private void initData() {
        Intent intent = getIntent();
        mName = intent.getStringExtra(MainActivity.NAME);
        mPsw = intent.getStringExtra(MainActivity.PSW);
        mUID = intent.getStringExtra(MainActivity.UID);
    }
    
    /** * 当前类已经实现了OnClickListener 接口因此绑定点击事件只需要传递this 即可
     */
    private void initView() {
        monitor = (Monitor) findViewById(R.id.monitor);
        ib_left = (ImageButton) findViewById(R.id.ib_left);
        ib_right = (ImageButton) findViewById(R.id.ib_right);
        ib_top = (ImageButton) findViewById(R.id.ib_top);
        ib_bottom = (ImageButton) findViewById(R.id.ib_bottom);
    
        tv_state = (TextView) findViewById(R.id.tv_state);
        // 初始化点击事件
        ib_bottom.setOnClickListener(this);
        ib_left.setOnClickListener(this);
        ib_right.setOnClickListener(this);
        ib_top.setOnClickListener(this);
    }
    
    /** * 上下左右点击事件的绑定
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.ib_left:
                /** * 发送左移动画面指令<br>
                 * PTZ :在安防监控应用中是Pan/Tilt/Zoom 的简写,代表云台全方位(左右/上下)移
                 * 动及镜头变倍、变焦控制。<br>
                 * 因为上下左右都需要发送指令,因此我抽取出一个方法
                 */
                sendPTZ(AVIOCTRLDEFs.AVIOCTRL_PTZ_LEFT);
                break;
            case R.id.ib_right:
                sendPTZ(AVIOCTRLDEFs.AVIOCTRL_PTZ_RIGHT);
                break;
            case R.id.ib_bottom:
                sendPTZ(AVIOCTRLDEFs.AVIOCTRL_PTZ_DOWN);
                break;
            case R.id.ib_top:
                sendPTZ(AVIOCTRLDEFs.AVIOCTRL_PTZ_UP);
                break;
        }
    }
    
    ;
    
    /** * 发送移动指令
     */
    private void sendPTZ(int type) {
        /** * 给摄像头发送指令
         * * @parameter Camera.DEFAULT_AV_CHANNEL 默认频道
         * AVIOCTRLDEFs.IOTYPE_USER_IPCAM_PTZ_COMMAND 指令类型
         * SMsgAVIoctrlPtzCmd.parseContent((byte) type, (byte) 0,
         * (byte) 0, (byte) 0, (byte) 0, (byte)
         * Camera.DEFAULT_AV_CHANNEL)
    
         * 指令数据,第三个参数是字节数组我们直接使用SMsgAVIoctrlPtzCmd 类的
         * parseContent 方法生成即可
         * */
        mCamera.sendIOCtrl(Camera.DEFAULT_AV_CHANNEL,
                AVIOCTRLDEFs.IOTYPE_USER_IPCAM_PTZ_COMMAND, SMsgAVIoctrlPtzCmd.parseContent((byte
                        ) type, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) Camera
                        .DEFAULT_AV_CHANNEL));
    }
    
    // 销毁时退出摄像头
    @Override
    protected void onDestroy() {
        super.onDestroy();
        quit();
    }
    
    /** * 当接收到服务器的数据时回调该函数
     * * @param resultCode 返回状态码
     */
    @Override
    public void receiveChannelInfo(Camera arg0, int channel, int resultCode) {
        Message msg = Message.obtain();
        msg.what = resultCode;
        handler.sendMessage(msg);
    }
    
    @Override
    public void receiveFrameData(Camera arg0, int arg1, Bitmap arg2) {
    }
    
    @Override
    public void receiveFrameInfo(Camera arg0, int arg1, long arg2, int arg3, int arg4,
                                 int arg5, int arg6) {
    
    }
    
    @Override
    public void receiveIOCtrlData(Camera arg0, int arg1, int arg2, byte[] arg3) {
    }
    
    @Override
    public void receiveSessionInfo(Camera arg0, int arg1) {
    }
    
    @Override
    public void onBackPressed() {
        super.onBackPressed();
    
    
        quit();
    }
    
    /** * 断开连接
     */
    private void quit() {
        if (monitor != null) {
            // 1.解除绑定
            monitor.deattachCamera();
            // 2.停止显示
            mCamera.stopShow(Camera.DEFAULT_AV_CHANNEL);
            // 3.断开连接
            // 3.1 取消渠道号
            mCamera.stop(Camera.DEFAULT_AV_CHANNEL);
            // 3.2 断开连接
            mCamera.disconnect();
            // 4.注销监听
            mCamera.unregisterIOTCListener(this);
        }
    }
    }

添加访问网络权限 因为我们的App是访问网络的,当然得记得添加权限

复制代码
    <uses-permission android:name="android.permission.INTERNET"/>

智能继电器

智能继电器广泛应用于调节家用设备的电源状态。其主要区别在于集成有独特的蓝牙技术,在此基础之上支持通过手机完成无线连接,并接收来自智能手机发出的相关命令A/B/C/D/E等不同命令来启动或停止相关开关;最后能够 Able to regulate the power supply of various household appliances indirectly.

准备工作

1、智能继电器

通过网络渠道进行了蓝牙继电器的采购。产品详情页面可能存在商业推广的嫌疑,请访问https://detail.tmall.com/item.htm?id=44157073788&spm=a1z09.2.9.133.cmtDUE&_u=2am3rd62f16以获取详细信息

2、灯泡组 电池 电线 螺丝刀工具等

该继电器是专为家庭220伏供电设计的。但由于实验只需3伏电池即可使用,则我们可以采用发光二极管来替代灯泡。这样不仅降低了电路复杂度,并且确保了绝对的安全性。

该商品详情页可访问以下链接查看:https://detail.tmall.com/item.htm?id=17637399755&spm=a1z09.2.9.100.cmtDUE&_u=2am3rd662b0

4、无需编写SDK代码,则只需掌握继电器蓝牙操作指令即可;且在购买页面提供完整的相关操作指令。

工作原理

手机启动蓝牙搜索后发现可用设备列表中的一个续动器,
随后通过预设的配对密码完成与续动器的连接。
值得注意的是,在这个设计中采用了一个带有多个手动开关组的续动器,
智能手机将数据信号发送至续动器进行操作,
随后该装置会根据接收到的数据信号执行开启或关闭的动作,
从而实现了智能手机与家用设备之间的远程控制功能。
以下是我制作完成后的实物图:
中间方形的是蓝牙续动器,
右侧是发光二极管,
二极管使用干电池供电,
手机通过软件控制灯泡的开和关。

智能家居

代码实现

该软件包含两个界面对周围环境进行搜索。运行效果图如图所示。第一个界面对周围环境进行搜索。系统会将搜索到的蓝牙设备以ListView的形式展示。接着点击与当前选中的智能继电器相对应的蓝牙连接指示。转至第二个界面上完成对智能继电器的各种操作。

智能家居

activity_main.xml对应第一个界面:

复制代码
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <Button
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:onClick="checkBluetooth"
            android:text="监测蓝牙设备" />
        <Button
            android:id="@+id/btn_scan"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:onClick="scanBluetooth"
            android:text="扫描蓝牙" />
    </LinearLayout>
    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >
    </ListView>
    </LinearLayout>

activity_control.xml对应第二个界面

复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <Button
            android:id="@+id/btn_kaiguan1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关1"/>
    
        <Button
            android:id="@+id/btn_kaiguan2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关2"/>
    
        <Button
            android:id="@+id/btn_kaiguan3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关3"/>
    
        <Button
            android:id="@+id/btn_kaiguan4"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关4"/>
    
        <Button
            android:id="@+id/btn_kaiguan5"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关5"/>
    
        <Button
            android:id="@+id/btn_kaiguanAll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="全部"/>
    
        <Button
            android:id="@+id/btn_kaiguan5_diandong"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关5 点动1s"/>
    
        <Button
            android:id="@+id/btn_kaiguan5_husuo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关5 互锁"/>
    
        <Button
            android:id="@+id/btn_kaiguan5_zisuo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:text="开关5 自锁"/>
    </LinearLayout>
    </ScrollView>

MainActivity.java是入口Activity

复制代码
    package com.example.bluetoothtest;
    
    import android.support.v7.app.ActionBarActivity;
    
    package com.example.bluetoothtest;
    
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.Bundle;
    import android.support.v7.app.ActionBarActivity;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.ArrayAdapter;
    import android.widget.Button;
    import android.widget.ListView;
    import android.widget.Toast;
    
    import com.google.testapp.R;
    
    import java.util.ArrayList;
    
    public class MainActivity extends ActionBarActivity {
    private static final String BTN_SCANING       = "正在扫描蓝牙设备...";
    private static final String BTN_SCAN          = "扫描蓝牙设备";
    private static final int    REQUEST_ENABLE_BT = 1;
    private BluetoothAdapter mBluetoothAdapter;
    private ArrayList<String> mArrayAdapter = new ArrayList<>();
    private Button btn_scan;
    private ArrayList<BluetoothDevice> devices = new ArrayList<>();
    private ListView             mListView;
    private ArrayAdapter<String> mAdapter;
    // Create a BroadcastReceiver for ACTION_FOUND
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // When discovery finds a device
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // Get the BluetoothDevice object from the Intent
                BluetoothDevice device =
                        intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                // Add the name and address to an array adapter to show in a ListView
                mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                devices.add(device);
                mAdapter.notifyDataSetChanged();
            }
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    
    private void initView() {
        mListView = (ListView) findViewById(R.id.lv);
        mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
                mArrayAdapter);
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position,
                                    long id) {
                Intent intent = new Intent(MainActivity.this,
                        ControlActivity.class);
                intent.putExtra("device", devices.get(position));
                mBluetoothAdapter.cancelDiscovery();
                btn_scan.setText(BTN_SCAN);
                startActivity(intent);
            }
        });
        btn_scan = (Button) findViewById(R.id.btn_scan);
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    }
    
    /** * 监测蓝牙是否开启,如果没有开,请求打开
     * * @param view
     */
    public void checkBluetooth(View view) {
    
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "对不起,您的设备不支持蓝牙。", 0).show();
            return;
        }
        //监测蓝牙是否可用
        boolean enabled = mBluetoothAdapter.isEnabled();
        if (!enabled) {
            Intent enableBtIntent = new
                    Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            return;
        }
        //查询已经配对的蓝牙
        queryingPairedDevices();
    }
    
    private void queryingPairedDevices() {
        mArrayAdapter.clear();
        devices = new ArrayList(mBluetoothAdapter.getBondedDevices());
        // If there are paired devices
        if (devices.size() > 0) {
            // Loop through paired devices
            for (BluetoothDevice device : devices) {
                // Add the name and address to an array adapter to show in a ListView
                黑马程序员——只要学不死,就往死里学,冲击年薪20 万!
    
                mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
            }
            mAdapter.notifyDataSetChanged();
        } else {
            Toast.makeText(this, "没有找到已经配对的蓝牙", 0).show();
        }
    }
    
    /** * 扫描蓝牙
     * * @param view
     */
    public void scanBluetooth(View view) {
        if (mBluetoothAdapter.isEnabled() && !mBluetoothAdapter.isDiscovering()) {
            boolean startDiscovery = mBluetoothAdapter.startDiscovery();
            if (!startDiscovery) {
                Toast.makeText(this, "开始扫描失败", 0).show();
                return;
            }
            Toast.makeText(this, "开始扫描", 0).show();
            mArrayAdapter.clear();
            devices.clear();
            btn_scan.setText(BTN_SCANING);
            // Register the BroadcastReceiver
            IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
            // Don't forget to unregister during onDestroy
            registerReceiver(mReceiver, filter);
        } else {
            String scan_state = btn_scan.getText().toString();
            if (BTN_SCANING.equals(scan_state)) {
                boolean cancelDiscovery = mBluetoothAdapter.cancelDiscovery();
                if (cancelDiscovery) {
                    btn_scan.setText(BTN_SCAN);
                }
                unregisterReceiver(mReceiver);
            }
        }
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            Toast.makeText(this, "蓝牙已经可以使用", 0).show();
            checkBluetooth(null);
        } else if (resultCode == RESULT_CANCELED) {
            Toast.makeText(this, "对不起,您的设备没有打开蓝牙,无法使用该应用咯", 0).show();
            return;
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
            btn_scan.setText(BTN_SCAN);
        }
    }
    }

ControlActivity.java是核心类,用于发送指令

复制代码
    package com.example.bluetoothtest;
    
    import android.annotation.SuppressLint;
    import android.app.Activity;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothSocket;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Looper;
    import android.os.SystemClock;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.Toast;
    
    import com.google.testapp.R;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.UUID;
    
    public class ControlActivity extends Activity implements OnClickListener {
    /** * 用于设置开关的状态,显示在Button 上的文字就是
     */
    private final static String KAIGUAN1_STATE_OPEN = "开关1 已经打开";
    private final static String KAIGUAN2_STATE_OPEN = "开关2 已经打开";
    private final static String KAIGUAN3_STATE_OPEN = "开关3 已经打开";
    private final static String KAIGUAN4_STATE_OPEN = "开关4 已经打开";
    private final static String KAIGUAN5_STATE_OPEN = "开关5 已经打开";
    private final static String KAIGUAN1_STATE_CLOSE = "开关1 已经关闭";
    private final static String KAIGUAN2_STATE_CLOSE = "开关2 已经关闭";
    private final static String KAIGUAN3_STATE_CLOSE = "开关3 已经关闭";
    private final static String KAIGUAN4_STATE_CLOSE = "开关4 已经关闭";
    private final static String KAIGUAN5_STATE_CLOSE = "开关5 已经关闭";
    private final static String KAIGUANALL_STATE_OPEN = "所有开关已经打开";
    private final static String KAIGUANALL_STATE_CLOSE = "所有开关已经关闭";
    /** * 产生一个唯一序列值,用于跟智能继电器的蓝牙连接的时候给自己的蓝牙设备做一个标记作用
     */
    public static final UUID MY_UUID = UUID.randomUUID();
    /** * 打开对应的开关的指令集<br>
     * 这些指令大家在卖家的产品说明中有,直接抄过来的<br>
     * 通过这些指令大家发现通信的时候每一个指令都是一个字节数组
     * */
    private static final byte[] COMMAND_OPEN_1 = new byte[] { (byte) 0x01, (byte) 0x99,
            (byte) 0x10, (byte) 0x10, (byte) 0x99 };
    private static final byte[] COMMAND_OPEN_2 = new byte[] { (byte) 0x01, (byte) 0x99,
            (byte) 0x20, (byte) 0x20, (byte) 0x99 };
    private static final byte[] COMMAND_OPEN_3 = new byte[] { (byte) 0x01, (byte) 0x99,
            (byte) 0x30, (byte) 0x30, (byte) 0x99 };
    private static final byte[] COMMAND_OPEN_4 = new byte[] { (byte) 0x01, (byte) 0x99,
            (byte) 0x40, (byte) 0x40, (byte) 0x99 };
    private static final byte[] COMMAND_OPEN_5 = new byte[] { (byte) 0x01, (byte) 0x99,
            (byte) 0x50, (byte) 0x50, (byte) 0x99 };
    /** * 关闭对应的开关指令
     */
    private static final byte[] COMMAND_CLOSE_1 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x11, (byte) 0x11, (byte) 0x99 };
    private static final byte[] COMMAND_CLOSE_2 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x21, (byte) 0x21, (byte) 0x99 };
    private static final byte[] COMMAND_CLOSE_3 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x31, (byte) 0x31, (byte) 0x99 };
    private static final byte[] COMMAND_CLOSE_4 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x41, (byte) 0x41, (byte) 0x99 };
    private static final byte[] COMMAND_CLOSE_5 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x51, (byte) 0x51, (byte) 0x99 };
    /** * 全部开关控制指令
     */
    private static final byte[] COMMAND_OPEN_ALL = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x64, (byte) 0x64, (byte) 0x99 };
    private static final byte[] COMMAND_CLOSE_ALL = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x65, (byte) 0x65, (byte) 0x99 };
    /** * 开关5 点动1s 所谓的点动1s 就是通电1s 后断点
     黑马程序员——只要学不死,就往死里学,冲击年薪20 万!
    
     */
    private static final byte[] COMMAND_DIANDONG_5 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x53, (byte) 0x53, (byte) 0x99 };
    /** * 互锁自锁这两个概念不好理解互锁的效果是把开关5 打开了,把开关4 关闭了。自锁的效果跟开关
     * 很类似,不过稍微有差异,对于我们来讲可以忽略
     */
    private static final byte[] COMMAND_HUSUO_5 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x54, (byte) 0x54, (byte) 0x99 };
    private static final byte[] COMMAND_ZISUO_5 = new byte[] { (byte) 0x01, (byte)
            0x99, (byte) 0x55, (byte) 0x55, (byte) 0x99 };
    private Button btn_kaiguan1;
    private Button btn_kaiguan2;
    private Button btn_kaiguan3;
    private Button btn_kaiguan4;
    private Button btn_kaiguan5;
    private Button btn_kaiguanAll;
    private Button btn_kaiguan5_diandong;
    private Button btn_kaiguan5_husuo;
    private Button btn_kaiguan5_zisuo;
    //蓝牙设备
    private BluetoothDevice mDevice;
    //通过蓝牙设备获取到的流
    private BluetoothSocket socket;
    // 是否连接成功用于记录连接状态
    private boolean isConnected = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_control);
        initView();
        initData();
        initConnection();
    }
    /** * 初始化视图
     */
    private void initView() {
        btn_kaiguan1 = (Button) findViewById(R.id.btn_kaiguan1);
        btn_kaiguan2 = (Button) findViewById(R.id.btn_kaiguan2);
        btn_kaiguan3 = (Button) findViewById(R.id.btn_kaiguan3);
        btn_kaiguan4 = (Button) findViewById(R.id.btn_kaiguan4);
        btn_kaiguan5 = (Button) findViewById(R.id.btn_kaiguan5);
        btn_kaiguanAll = (Button) findViewById(R.id.btn_kaiguanAll);
        btn_kaiguan5_diandong = (Button) findViewById(R.id.btn_kaiguan5_diandong);
        btn_kaiguan5_husuo = (Button) findViewById(R.id.btn_kaiguan5_husuo);
        btn_kaiguan5_zisuo = (Button) findViewById(R.id.btn_kaiguan5_zisuo);
        // 设置点击事件
        btn_kaiguan1.setOnClickListener(this);
        btn_kaiguan2.setOnClickListener(this);
        btn_kaiguan3.setOnClickListener(this);
        btn_kaiguan4.setOnClickListener(this);
        btn_kaiguan5.setOnClickListener(this);
        btn_kaiguanAll.setOnClickListener(this);
        btn_kaiguan5_diandong.setOnClickListener(this);
        btn_kaiguan5_husuo.setOnClickListener(this);
        btn_kaiguan5_zisuo.setOnClickListener(this);
        // 设置默认状态
        btn_kaiguan1.setText(KAIGUAN1_STATE_CLOSE);
        btn_kaiguan2.setText(KAIGUAN2_STATE_CLOSE);
        btn_kaiguan3.setText(KAIGUAN3_STATE_CLOSE);
        btn_kaiguan4.setText(KAIGUAN4_STATE_CLOSE);
        btn_kaiguan5.setText(KAIGUAN5_STATE_CLOSE);
        btn_kaiguanAll.setText(KAIGUANALL_STATE_CLOSE);
    }
    /** * 初始化数据
     */
    private void initData() {
        Intent intent = getIntent();
        BluetoothDevice device = intent.getParcelableExtra("device");
        if (null == device) {
            Toast.makeText(this, "没有获取到数据", 0).show();
            finish();
        }
        this.mDevice = device;
    }
    /** * 初始化蓝牙连接<br>
     * 因为蓝牙连接是线程阻塞且耗时的操作,因此需要放到子线程中,当连接成功后修改连接状态为true
     */
    private void initConnection() {
        Toast.makeText(this, "蓝牙开始连接", 0).show();
        new Thread(new Runnable() {
            @SuppressLint("NewApi")
            @Override
            public void run() {
                if (socket == null) {
                    try {
                        /** * 通过蓝牙设备获取蓝牙Socket 流
                         */
                        socket =
                                mDevice.createRfcommSocketToServiceRecord(MY_UUID);
                    } catch (IOException e) {
                        Looper.prepare();
                        Toast.makeText(ControlActivity.this, "连接失败。" + e, 0).show();
                        Looper.loop();
                        e.printStackTrace();
                        return;
                    }
                }
                if (!socket.isConnected()) {
                    try {
                        socket.connect();
                    } catch (IOException e) {
                        e.printStackTrace();
                        Looper.prepare();
                        Toast.makeText(ControlActivity.this, "连接失败。" + e, 0).show();
                        Looper.loop();
                        return;
                    }
                }
                if (outputStream == null) {
                    try {
                        /** * 通过socket 获取输出流
                         */
                        outputStream = socket.getOutputStream();
                        Looper.prepare();
                        Toast.makeText(ControlActivity.this, "连接成功。", 0).show();
                        isConnected = true;
                        Looper.loop();
                        isConnected = true;
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
    //如果频繁点击则拒绝,因为对继电器的物理损坏比较大,一秒只能点击一次
    long time = SystemClock.uptimeMillis();
    @Override
    public void onClick(View v) {
        //如果还没连接成功则拒绝往下操作
        if (!isConnected) {
            Toast.makeText(this, "蓝牙正在连接中。。。", 0).show();
            return ;
        }
        if (SystemClock.uptimeMillis() - time < 1000) {
            Toast.makeText(this, "您的操作太频繁了,请稍后再试", 0).show();
            time = SystemClock.uptimeMillis();
            return;
        }
        Button button = (Button) v;
        String state = button.getText().toString();
        int id = v.getId();
        switch (id) {
            case R.id.btn_kaiguan1:
                if (KAIGUAN1_STATE_CLOSE.equals(state)) {
                    //发送对应的指令
                    sendCommand(COMMAND_OPEN_1);
                    btn_kaiguan1.setText(KAIGUAN1_STATE_OPEN);
                } else {
                    sendCommand(COMMAND_CLOSE_1);
                    btn_kaiguan1.setText(KAIGUAN1_STATE_CLOSE);
                }
                break;
            case R.id.btn_kaiguan2:
                if (KAIGUAN2_STATE_CLOSE.equals(state)) {
                    sendCommand(COMMAND_OPEN_2);
                    btn_kaiguan2.setText(KAIGUAN2_STATE_OPEN);
                } else {
                    sendCommand(COMMAND_CLOSE_2);
                    btn_kaiguan2.setText(KAIGUAN2_STATE_CLOSE);
                }
                break;
            case R.id.btn_kaiguan3:
                if (KAIGUAN3_STATE_CLOSE.equals(state)) {
                    sendCommand(COMMAND_OPEN_3);
                    btn_kaiguan3.setText(KAIGUAN3_STATE_OPEN);
                } else {
                    sendCommand(COMMAND_CLOSE_3);
                    btn_kaiguan3.setText(KAIGUAN3_STATE_CLOSE);
                }
                break;
            case R.id.btn_kaiguan4:
                if (KAIGUAN4_STATE_CLOSE.equals(state)) {
                    sendCommand(COMMAND_OPEN_4);
                    btn_kaiguan4.setText(KAIGUAN4_STATE_OPEN);
                } else {
                    sendCommand(COMMAND_CLOSE_4);
                    btn_kaiguan4.setText(KAIGUAN4_STATE_CLOSE);
                }
                break;
            case R.id.btn_kaiguan5:
                if (KAIGUAN5_STATE_CLOSE.equals(state)) {
                    sendCommand(COMMAND_OPEN_5);
                    btn_kaiguan5.setText(KAIGUAN5_STATE_OPEN);
                } else {
                    sendCommand(COMMAND_CLOSE_5);
                    btn_kaiguan5.setText(KAIGUAN5_STATE_CLOSE);
                }
                break;
            case R.id.btn_kaiguanAll:
                if (KAIGUANALL_STATE_CLOSE.equals(state)) {
                    sendCommand(COMMAND_OPEN_ALL);
                    btn_kaiguan1.setText(KAIGUAN1_STATE_OPEN);
                    btn_kaiguan2.setText(KAIGUAN2_STATE_OPEN);
                    btn_kaiguan3.setText(KAIGUAN3_STATE_OPEN);
                    btn_kaiguan4.setText(KAIGUAN4_STATE_OPEN);
                    btn_kaiguan5.setText(KAIGUAN5_STATE_OPEN);
                    btn_kaiguanAll.setText(KAIGUANALL_STATE_OPEN);
                } else {
                    sendCommand(COMMAND_CLOSE_ALL);
                    btn_kaiguan1.setText(KAIGUAN1_STATE_CLOSE);
                    btn_kaiguan2.setText(KAIGUAN2_STATE_CLOSE);
                    btn_kaiguan3.setText(KAIGUAN3_STATE_CLOSE);
                    btn_kaiguan4.setText(KAIGUAN4_STATE_CLOSE);
                    btn_kaiguan5.setText(KAIGUAN5_STATE_CLOSE);
                    btn_kaiguanAll.setText(KAIGUANALL_STATE_CLOSE);
                }
                break;
            case R.id.btn_kaiguan5_diandong:
                sendCommand(COMMAND_DIANDONG_5);
                break;
            case R.id.btn_kaiguan5_husuo:
                sendCommand(COMMAND_HUSUO_5);
                break;
            case R.id.btn_kaiguan5_zisuo:
                sendCommand(COMMAND_ZISUO_5);
                break;
            default:
                break;
        }
    }
    private OutputStream outputStream;
    /** * 发送指令考虑到发送数据也是耗时操作,因此也在子线程中执行
     */
    @SuppressLint("NewApi")
    private void sendCommand(final byte[] command) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    outputStream.write(command);
                    outputStream.flush();
                } catch (IOException e) {
                    Looper.prepare();
                    Toast.makeText(ControlActivity.this, e.toString(), 0).show();
                    Looper.loop();
                    e.printStackTrace();
                }
            }
        }).start();
    }
    //释放资源
    @SuppressLint("NewApi")
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (outputStream != null) {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (socket != null) {
            if (socket.isConnected()) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            socket = null;
            isConnected = false;
        }
    }
    }
Android视频教程

全部评论 (0)

还没有任何评论哟~