一个Android平台上蓝牙串口通信辅助类的实现

一个Android平台上蓝牙串口通信辅助类的实现

蓝牙是Android平台上经常使用的功能,通过蓝牙通信都需要经过设备查找,设备配对,建立通信链路等过程。为避免在不同的应用中重复写相同的代码,我们可以将蓝牙通信中通用的代码封装起来,形成一套通用的类库,供不同的应用使用,使得应用可以只关注于本身的逻辑实现,而不需要更多关注通信的细节。

蓝牙通信的原理

蓝牙技术规定每一对设备之间进行蓝牙通讯时,必须一个为主角色,另一为从角色,才能进行通信,通信时,必须由主端进行查找,发起配对,建链成功后,双方即可收发数据。

蓝牙主端设备发起呼叫,首先是查找,找出周围处于可被查找的蓝牙设备。主端设备找到从端蓝牙设备后,与从端蓝牙设备进行配对,此时需要输入从端设备的PIN码,也有设备不需要输入PIN码。

配对完成后,从端蓝牙设备会记录主端设备的信任信息,此时主端即可向从端设备发起呼叫,已配对的设备在下次呼叫时,不再需要重新配对。

蓝牙通信类的实现方法

在通信中,我们把主端成为客户端,从端称为服务端,针对服务端和客户端操作的不同,为客户端和服务端创建不同的类,类图如下所示:

bluetooth.png

BluetoothTransport:为抽取的蓝牙设备的通用功能类,用于本机蓝牙设备开启、关闭以及蓝牙设备信息查询功能。
BluetoothClient:类为蓝牙通信客户端类,完成设备查找,配对,发送链接请求创建链接的功能。
BluetoothServer :类蓝牙通信设备服务端类,完成设备可查找,接收链接请求建立链接的功能。
Tansport:为设备类接口;
TransportClient:为客户端类接口;
TransportServer:为服务端类接口;

各类代码实现如下:

Tranport:

import android.app.Activity;

public interface Transport {
    String getDeviceName();

    boolean isHaveTransport();
    boolean isTransportOpened();

    void open(Activity activity, int requestId);
    void close();
}

TransportClient:

import java.io.InputStream;
import java.io.OutputStream;

public interface TransportClient extends Transport {
    interface OnClientListener {
        void onConnected(InputStream inputStream, OutputStream outputStream);
        void onConnectionFailed();
        void onDisconnected();
        void onClose();
    }

    interface OnDiscoveryListener {
        void onDiscover(String deviceName, String deviceAddress);
        void onFinish();
    }

    OnClientListener getClientListener();
    void setClientListener(OnClientListener mClientListener);

    OnDiscoveryListener getDiscoveryListener();
    void setDiscoveryListener(OnDiscoveryListener mDiscoveryListener);

    void startDiscovery(int delay);
    void cancelDiscovery();

    void startClient(String deviceName, String deviceAddress, String serverUUID);
    void stopClient();
}

TransportServer:

import java.io.InputStream;
import java.io.OutputStream;

public interface TransportServer extends Transport {
    interface OnServerListener {
        void onConnected(InputStream inputStream, OutputStream outputStream);
        void onDisconnected();
        void onClose();
    }

    OnServerListener getServerListener();
    void setServerListener(OnServerListener mServerListener);

    void setDiscoverable(int discoverId);

    void startServer(String serverName, String serverUUID);
    void stopServer();
}

BluetoothTransport

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;

public class BluetoothTransport implements Transport {
    protected BluetoothAdapter mBluetoothAdapter;
    protected boolean bOpenedLocal;
    protected Activity activity;

    public BluetoothTransport() {
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        bOpenedLocal = false;
    }


    @Override
    public String getDeviceName() {
        if (mBluetoothAdapter != null) {
            return mBluetoothAdapter.getName();
        } else {
            return "";
        }
    }

    @Override
    public boolean isHaveTransport() {
        return mBluetoothAdapter != null;
    }

    @Override
    public boolean isTransportOpened() {
        if (mBluetoothAdapter != null) {
            return mBluetoothAdapter.isEnabled();
        } else {
            return false;
        }
    }

    @Override
    public void open(Activity activity, int requestId) {
        //弹出对话框提示用户是后打开
        Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        activity.startActivityForResult(enabler, requestId);
        //不做提示,直接打开,不建议用下面的方法,有的手机会有问题。
        // mBluetoothAdapter.enable();
        bOpenedLocal = true;
        this.activity = activity;
    }

    @Override
    public void close() {
        if (bOpenedLocal) {
            if (mBluetoothAdapter != null) {
                mBluetoothAdapter.disable();
            }
        }
    }
}

BluetoothClient

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class BluetoothClient extends BluetoothTransport implements TransportClient {
    private OnClientListener mClientListener = null;
    private OnDiscoveryListener mDiscoveryListener = null;
    private boolean bRegisterReceiver = false;
    private ScheduledExecutorService startScheduledExecutor;

    private Set<BluetoothDevice> deviceVector = new HashSet<>();
    private BluetoothSocket mClientSocket = null;

    public BluetoothClient() {
        super();
        startScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
    }

    @Override
    public OnClientListener getClientListener() {
        return mClientListener;
    }

    @Override
    public void setClientListener(OnClientListener clientListener) {
        mClientListener = clientListener;
    }

    @Override
    public OnDiscoveryListener getDiscoveryListener() {
        return mDiscoveryListener;
    }

    @Override
    public void setDiscoveryListener(OnDiscoveryListener discoveryListener) {
        mDiscoveryListener = discoveryListener;
    }

    private void scanBluetoothDevices(){
        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();

        if (pairedDevices.size() > 0) {

            for (BluetoothDevice btDev : pairedDevices) {
                deviceVector.add(btDev);
                mDiscoveryListener.onDiscover(btDev.getName(), btDev.getAddress());
            }
        }
    }

    private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

            if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。
                //获取蓝牙设备
                if(scanDevice == null || scanDevice.getName() == null) return;

                if (mDiscoveryListener != null) {
                    switch (scanDevice.getBondState()) {
                        case BluetoothDevice.BOND_BONDED:
                            break;
                        case BluetoothDevice.BOND_BONDING:
                            break;
                        case BluetoothDevice.BOND_NONE:
                            break;
                    }
                    //scanDevice.createBond();
                    deviceVector.add(scanDevice);
                    mDiscoveryListener.onDiscover(scanDevice.getName(), scanDevice.getAddress());
                }
            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                if (mDiscoveryListener != null) {
                    mDiscoveryListener.onFinish();
                }
                cancelDiscovery();
            } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                switch (device.getBondState()) {
                    case BluetoothDevice.BOND_BONDING:
//                        Log.d("BlueToothTestActivity", "正在配对......");
                        break;
                    case BluetoothDevice.BOND_BONDED:
//                        Log.d("BlueToothTestActivity", "完成配对");
                        break;
                    case BluetoothDevice.BOND_NONE:
//                        Log.d("BlueToothTestActivity", "取消配对");
                        break;
                    default:
                        break;
                }
            } else if(action.equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
            }
        }
    };

    @Override
    public void startDiscovery(int delaySecond) {
        IntentFilter filter = new IntentFilter();
        //发现设备
        filter.addAction(BluetoothDevice.ACTION_FOUND);
        //设备连接状态改变
        filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
        //蓝牙设备状态改变
        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);

        filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);

        scanBluetoothDevices();
        activity.registerReceiver(mBluetoothReceiver, filter);
        bRegisterReceiver = true;

        mBluetoothAdapter.startDiscovery();
        startScheduledExecutor.schedule(new Runnable() {
            @Override
            public void run() {
                cancelDiscovery();
            }
        }, delaySecond, TimeUnit.SECONDS);
    }

    @Override
    public void cancelDiscovery() {
        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
    }

    @Override
    public void startClient(String deviceName, String deviceAddress, String serverUUID) {
        BluetoothDevice deviceTmp = null;
        for (BluetoothDevice bd : deviceVector) {
            if (bd.getAddress().equals(deviceAddress)) {
                deviceTmp = bd;
                break;
            }
        }
        if (deviceTmp == null) {
            return;
        }

        final BluetoothDevice device = deviceTmp;
        try {
            mClientSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(serverUUID));
        } catch (IOException e) {
            mClientSocket = null;
        }

        startScheduledExecutor.execute(new Runnable() {
            private int mState = 0;

            @Override
            public void run() {
                try {
                    // socket 连接,该调用会阻塞,直到连接成功或失败
                    mClientSocket.connect();
                } catch (IOException e) {
                    try {
                        mClientSocket.close();
                    } catch (IOException ie) {

                    }
                    if (mClientListener != null) {
                        mClientListener.onConnectionFailed();
                    }
                    return;
                }
                // 启动连接线程
                if (mClientListener != null) {
                    try {
                        mClientListener.onConnected(mClientSocket.getInputStream(), mClientSocket.getOutputStream());
                    } catch (IOException ex) {
                        mClientListener.onDisconnected();
                    }
                }
            }
        });
    }

    @Override
    public void stopClient() {
        try {
            if (mClientListener != null) {
                mClientListener.onClose();
            }
            if (mClientSocket != null) {
                mClientSocket.close();
            }
        } catch (IOException e) {
        }
    }

    @Override
    public void close() {
        super.close();

        if (bRegisterReceiver) {
            activity.unregisterReceiver(mBluetoothReceiver);
            bRegisterReceiver = false;
        }

        startScheduledExecutor.shutdown();
    }
}

BluetoothServer

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

public class BluetoothServer extends BluetoothTransport implements TransportServer {
    private OnServerListener mServerListener = null;
    private BluetoothServerSocket mServerAcceptSocket = null;
    private BluetoothSocket mServerSocket = null;
    private boolean bServerRunning = false;
    private ScheduledExecutorService startScheduledExecutor;

    public BluetoothServer() {
        super();
        startScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
    }

    @Override
    public OnServerListener getServerListener() {
        return mServerListener;
    }

    @Override
    public void setServerListener(OnServerListener serverListener) {
        mServerListener = serverListener;
    }

    @Override
    public void setDiscoverable(int discoverId) {
        if (mBluetoothAdapter.isEnabled()) {
            if (mBluetoothAdapter.getScanMode() !=
                    BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
                Intent discoverableIntent = new Intent(
                        BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                discoverableIntent.putExtra(
                        BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
                activity.startActivityForResult(discoverableIntent, discoverId);
            }
        }
    }

    @Override
    public void startServer(String serverName, String serverUUID) {
        try {
            mServerAcceptSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
                    serverName, UUID.fromString(serverUUID));

            startScheduledExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    bServerRunning = true;
                    // 循环,直到连接成功
                    while (bServerRunning) {
                        try {
                            // 这是一个阻塞调用 返回成功的连接
                            // mServerAcceptSocket.close()在另一个线程中调用,可以中止该阻塞
                            mServerSocket = mServerAcceptSocket.accept();
                            mServerAcceptSocket.close();
                            mServerAcceptSocket = null;
                        } catch (IOException e) {
                            break;
                        }
                        // 如果连接被接受
                        if (mServerSocket != null) {
                            if (mServerListener != null) {
                                try {
                                    mServerListener.onConnected(mServerSocket.getInputStream(), mServerSocket.getOutputStream());
                                } catch (IOException ex) {
                                    mServerListener.onDisconnected();
                                }
                            }
                        }
                    }
                }
            });
        } catch (IOException e) {

        }
    }

    @Override
    public void stopServer() {
        try {
            if (mServerListener != null) {
                mServerListener.onClose();
            }
            bServerRunning = false;

            if (mServerAcceptSocket != null) {
                mServerAcceptSocket.close();
            }

            if (mServerSocket != null) {
                mServerSocket.close();
            }
        } catch (IOException e) {
//                    Log.e(TAG, "close() of server failed", e);
        }
    }

    @Override
    public void close() {
        super.close();

        startScheduledExecutor.shutdown();
    }
}

使用方法
TransportClient和TransportServer需要在Activity的方法中使用
创建蓝牙客户端通信的代码框架如下所示:

    BluetoothClient transportClient = new BluetoothClient();
    final int REQUEST_ENABLE = 101;
    protected void startClient() {
        if (transportClient.isHaveTransport()) {
            if (!transportClient.isTransportOpened()) {
                transportClient.open(this, REQUEST_ENABLE);
            }
        }

        transportClient.setClientListener(new TransportClient.OnClientListener() {
            @Override
            public void onConnected(InputStream inputStream, OutputStream outputStream) {
                /*
                 * 通信代码
                 */
            }

            @Override
            public void onConnectionFailed() {
                // 连接失败操作
            }

            @Override
            public void onDisconnected() {
                // 断开连接操作后的操作代码
            }

            @Override
            public void onClose() {
                // 通信关闭操作代码
            }
        });

        transportClient.setDiscoveryListener(new TransportClient.OnDiscoveryListener() {
            @Override
            public void onDiscover(String deviceName, String deviceAddress) {
                if (deviceName != null && deviceAddress != null) {
                    addDevice(deviceName, deviceAddress);
//                    deviceVector.add(device);
                }
            }

            protected void addDevice(final String deviceName, final String deviceAddress) {
                // 显示设备,供用户选择代码
                // 用户选择设备后,调用 transportTookit startClient 方法
            }

            @Override
            public void onFinish() {
                // 发现设备结束
            }
        });
        transportClient.startDiscovery(120);
    }

创建蓝牙服务端通信的代码框架如下所示:

    BluetoothServer transportServer = new BluetoothServer();
    String serverName = ""; //服务名
    String serverUUID = ""; //服务UUID
    final int REQUEST_DISCOVERABLE = 102;
    void startServer() {
        if (transportServer.isHaveTransport()) {
            if (!transportServer.isTransportOpened()) {
                transportServer.open(this, REQUEST_ENABLE);
            }
        }
        transportServer.setServerListener(new TransportServer.OnServerListener() {
            @Override
            public void onConnected(InputStream inputStream, OutputStream outputStream) {
                //通信代码
            }

            @Override
            public void onDisconnected() {
                // 链接断开代码
            }

            @Override
            public void onClose() {
                // 关闭代码
            }
        });

        transportServer.setDiscoverable(REQUEST_DISCOVERABLE);
        transportServer.startServer(serverName, serverUUID);
    }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • Guide to BluetoothSecurity原文 本出版物可免费从以下网址获得:https://doi.o...
    公子小水阅读 7,941评论 0 6
  • Android平台支持蓝牙网络协议栈,实现蓝牙设备之间数据的无线传输。本文档描述了怎样利用android平台提供的...
    Camming阅读 3,303评论 0 3
  • 蓝牙 注:本文翻译自https://developer.android.com/guide/topics/conn...
    RxCode阅读 8,652评论 11 99
  • 最近项目使用蓝牙,之前并没有接触,还是发现了很多坑,查阅了很多资料,说的迷迷糊糊,今天特查看官方文档。 说下遇到的...
    King9527阅读 1,790评论 0 1
  • 前言 最近在做Android蓝牙这部分内容,所以查阅了很多相关资料,在此总结一下。 基本概念 Bluetooth是...
    猫疏阅读 14,534评论 7 113