简易蓝牙测试程序

前言

因为项目须要,笔者须要在安卓平台开发一个程序,可以用蓝牙和下层的单片机通信。java

出于测试目的,笔者先用两部均支持蓝牙的安卓设备进行设备间通信实验,本文就是在这个实验基础上写就的。android

查阅了一些参考书籍和博客文章,笔者非常失望,由于它们都是罗列代码(并且几乎都是套用安卓官方自带的那两个例子——API Guides下的蓝牙指导代码和samples下的BluetoothChat),可是并无给出系统的宏观分析,让人颇感失望。程序员

认真研读API Guides下的蓝牙指导代码后,笔者先绘制出了类图,而后在其指导下完成了整个实验程序的构建,其间固然多有反复与修改,做为程序员你们确定都懂的——“三分编程,七分调试”编程

背景知识

1.蓝牙是什么?app

一种近距离无线通讯协议,小功率蓝牙设备(通常咱们见到的都是)的通信速率约为1Mbps,通信距离为10m。框架

2.蓝牙分主从吗?socket

分的,蓝牙组网的方式是:1主(<8)从。蓝牙组的网有个可爱的名字——“微微网”(piconet),蓝牙设备在微微网的地址(注意不是mac地址)是3位,所以一个微微网最多有8台被激活设备。设备的主从角色的分配是在组成微微网时临时肯定的,不过蓝牙技术支持“主从转换”。ide

3.蓝牙设备都有哪些状态?工具

激活,呼吸,保持,休眠:功率依次递减。oop

框架

咱们先来看看通常的通信模型是怎样的

打开-》创建链接-》通信-》断开链接-》关闭

打开设备是一切工做的前提,创建链接须要保证两个蓝牙设备之间的可见性而搜索就是找寻周围的蓝牙设备(此操做比较占带宽,通信时务必关掉),通信就是把两个设备用某种方式链接起来(一主一从)而后发送消息与接收消息,最后须要断开链接,关闭设备。

据此,设计UI以下(接收消息按钮仅仅是为了美观,代码中并未实现什么功能):

这个程序仅用到了一个活动:

//实现OnClickListener接口是一个技巧,这样在活动中给控件设置监听的时候直接传this就行了,代码会简洁许多

 

 

 比较重要的是三个内部类:

//这三个都是线程类,和蓝牙相关的会阻塞的操做都封装在它们中

 

 代码

代码写的不是很完善,但彻底可以达到测试的功能

建议把代码复制下来,再用IDE工具查看,先看框架(outline),再看细节

//看代码有迷惑的地方,再去看前面的类图,在树林中迷了路,此时须要登高四望

//全部的输出均会打印到logcat中,用System.out过滤一下

//注意:使用蓝牙,须要声明BLUETOOTH权限,若是须要扫描设备或者操做蓝牙设置,则还须要BLUETOOTH_ADMIN权限,本实验两个权限都须要

 

测试

1.拿出两台设备,安装好程序,完成配对//配对的过程须要人为操做,与这个程序没有关系

2.两台设备均打开蓝牙(从系统界面或是这个程序的“打开蓝牙按钮都可”)

3.一台设备经过adb链接到电脑,点击“启动主机”按钮

4.在另外一台设备点击“启动从机”按钮

5.在这台设备点击“发送消息”按钮

6.不出意外的话,logcat的System.out会打印出三条记录——1,2,3

MainActivity.java

package com.example.testbluetooth;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.widget.Button;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener {
    
    //蓝牙
    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
    //蓝牙状态
    final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {//接收蓝牙发现的消息
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                System.out.println("From mBroadcastReceiver:"+device.getName() + "-" + device.getAddress());
            }
        }
    };
    //消息处理
    private static final int MESSAGE_READ = 0;
    private Handler mHandler = new Handler(){
        public void handleMessage(Message msg){
            switch(msg.what){
            case MESSAGE_READ:
                byte[] buffer = (byte[])msg.obj;//buffer的大小和里面数据的多少没有关系
                for(int i=0; i<buffer.length; i++){
                    if(buffer[i] != 0){
                        System.out.println(buffer[i]);
                    }
                }
                break;
            }
        }
    };
    //线程
    ConnectedThread mConnectedThread;
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //控件
        findViewById(R.id.open).setOnClickListener(this);
        findViewById(R.id.close).setOnClickListener(this);
        findViewById(R.id.search).setOnClickListener(this);
        findViewById(R.id.server).setOnClickListener(this);
        findViewById(R.id.client).setOnClickListener(this);
        findViewById(R.id.send).setOnClickListener(this);
        findViewById(R.id.receive).setOnClickListener(this);
        findViewById(R.id.paired).setOnClickListener(this);
        //注册广播
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mBroadcastReceiver, filter); // Don't forget to unregister during onDestroy
    }


    // a fictional method in the application 
    //that will initiate the thread for transferring data
    void manageConnectedSocket(BluetoothSocket socket){//不知何故,可是在此处用Toast会出现Looper问题//Toast.makeText(this, "A socket opened!", Toast.LENGTH_SHORT).show();
        //System.out.println("From manageConnectedSocket:"+socket);
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
    }


    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        //设备支持不支持蓝牙和有没有插蓝牙芯片没有关系,这个是操做系统的事情
        //若是系统不支持蓝牙,会返回空,经测试,即便没有蓝牙芯片,bluetooth的值可为非空
        //可是若是没有插蓝牙芯片,系统会阻塞住
        
        switch (v.getId()) {
        case R.id.open:
            if (!mBluetoothAdapter.isEnabled()) {//若是蓝牙没有打开
                mBluetoothAdapter.enable();//这种打开方式能够跳过系统打开蓝牙的界面
            }            
            break;
        case R.id.close:
            if (mBluetoothAdapter.isEnabled()) {//若是蓝牙已经打开
                mBluetoothAdapter.disable();
            }
            break;
        case R.id.search:
            if (!mBluetoothAdapter.isDiscovering()) {//若是蓝牙不处于搜索状态(即找寻附近蓝牙)
                mBluetoothAdapter.startDiscovery();//Enabling discoverability will automatically enable Bluetooth. 
            }
            break;
        case R.id.server:
            new AcceptThread().start();
            ((Button)findViewById(R.id.client)).setVisibility(View.INVISIBLE);
            break;
        case R.id.client:
            for(BluetoothDevice device:pairedDevices){
                new ConnectThread(device).start();
                ((Button)findViewById(R.id.server)).setVisibility(View.INVISIBLE);
            }
            break;
        case R.id.send:
            if(mConnectedThread != null){
                byte[] bytes = new byte[]{1,2,3};
                mConnectedThread.write(bytes);
            }
            break;
        case R.id.receive:
            break;
        case R.id.paired:
            pairedDevices = mBluetoothAdapter.getBondedDevices();
            for(BluetoothDevice device:pairedDevices){
                System.out.println("From paired:"+device.getName());
            }
            break;
        default:
            break;
        }
    }
    
    private class AcceptThread extends Thread {
        private final BluetoothServerSocket mmServerSocket;
     
        public AcceptThread() {
            // Use a temporary object that is later assigned to mmServerSocket,
            // because mmServerSocket is final
            BluetoothServerSocket tmp = null;
            try {
                // MY_UUID is the app's UUID string, also used by the client code
                tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("blind_nav", UUID.fromString("0c312388-5d09-4f44-b670-5461605f0b1e"));
            } catch (IOException e) { }
            mmServerSocket = tmp;
        }
     
        public void run() {
            BluetoothSocket socket = null;
            // Keep listening until exception occurs or a socket is returned
            while (true) {
                try {
                    //System.out.println("From AcceptThread:");
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    break;
                }
                // If a connection was accepted
                if (socket != null) {
                    // Do work to manage the connection (in a separate thread)
                    manageConnectedSocket(socket);
                    try {
                        mmServerSocket.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    break;
                }
            }
        }
     
        /** Will cancel the listening socket, and cause the thread to finish */
        public void cancel() {
            try {
                mmServerSocket.close();
            } catch (IOException e) { }
        }
    }
    
    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
     
        public ConnectThread(BluetoothDevice device) {
            // Use a temporary object that is later assigned to mmSocket,
            // because mmSocket is final
            BluetoothSocket tmp = null;
            mmDevice = device;
     
            // Get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = device.createRfcommSocketToServiceRecord(UUID.fromString("0c312388-5d09-4f44-b670-5461605f0b1e"));
            } catch (IOException e) { }
            mmSocket = tmp;
        }
     
        public void run() {
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();
     
            try {
                // Connect the device through the socket. This will block
                // until it succeeds or throws an exception
                mmSocket.connect();//这个操做须要几秒钟,不是当即能见效的
            } catch (IOException connectException) {
                // Unable to connect; close the socket and get out
                try {
                    mmSocket.close();
                } catch (IOException closeException) { }
                return;
            }
     
            // Do work to manage the connection (in a separate thread)
            manageConnectedSocket(mmSocket);
        }
     
        /** Will cancel an in-progress connection, clean up all internal resources, and close the socket */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }
    
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
     
        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;
     
            // Get the input and output streams, using temp objects because
            // member streams are final
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) { }
     
            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }
     
        public void run() {
            byte[] buffer = new byte[1024];  // buffer store for the stream
            int bytes; // bytes returned from read()
     
            // Keep listening to the InputStream until an exception occurs
            while (true) {
                try {
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);
                    // Send the obtained bytes to the UI activity
                    //Message msg = new Message();
                    //msg.what = MESSAGE_READ;
                    mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                            .sendToTarget();
                } catch (IOException e) {
                    break;
                }
            }
        }
     
        /* Call this from the main activity to send data to the remote device */
        public void write(byte[] bytes) {
            try {
                mmOutStream.write(bytes);
            } catch (IOException e) { }
        }
     
        /* Call this from the main activity to shutdown the connection */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }
    
    
}

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/open"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="打开蓝牙" />

        <Button
            android:id="@+id/close"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="关闭蓝牙" />
    </LinearLayout>

    <Button
        android:id="@+id/search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="搜索蓝牙" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/server"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="启动主机" />

        <Button
            android:id="@+id/client"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="启动从机" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/send"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="发送消息" />
        <Button
            android:id="@+id/receive"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="接收消息" />
    </LinearLayout>

    <Button
        android:id="@+id/paired"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:text="已配对蓝牙" />

</LinearLayout>

 

相关文章
相关标签/搜索