Android蓝牙系统

蓝牙是一种支持设备短距离通信(一般10m内)的无线电技术,可以在众多设备之间进行无线信息交换。

Android系统中的蓝牙模块

Android包含了对蓝牙网络协议栈的支持,使蓝牙设备能够无线连接其他蓝牙设备以便交换数据。

通过使用蓝牙API,一个Android应用程序能够实现如下功能:
- 扫描其他蓝牙设备
- 查询本地蓝牙适配器用于配对蓝牙设备
- 建立RFCOMM信道
- 通过服务发现连接其他设备
- 数据通信
- 管理多个连接

Android平台中蓝牙系统从上到下主要包括Java框架中的BlueTooth类、Android适配库、BlueZ库、驱动程序和协议。

蓝牙模块中的源码

  • 初始化蓝牙芯片

    通过BlueZ工具hciattach进行的,命令格式如下:    hciattach [-n] [-p] [-b] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]type决定了要初始化的设备的型号,可以使用hciattach -1来列出所支持的设备型号。Hciattach命令内部的工作步骤是:首先打开制定的tty设备,再做一些通用的设置,如flow等, 之后设置波特率为initial_speed,然后根据type调用各自的初始化代码,最后将波特率重新设置为speed。
  • 蓝牙服务

    一般不需要自己定义,只需要使用初始化脚本文件init.rc中的默认内容即可。
  • 管理蓝牙电源

    可以调用rfkill接口来控制电源管理,如果已经实现了rfkill接口,则无需再进行配置。

和蓝牙相关的类

BluetoothSocket类

  • BluetoothSocket类基础

    Android的蓝牙系统和Socket套接字密切相关,蓝牙端的监听接口和TCP端口类似,都是使用 Socket和ServerSocket类。在服务器端使用BluetoothServerSocket类来创建一个监听服务端口。当一个连接被BluetoothServerSocket所接受,它会返回一个新的BluetoothSocket来管理该连接。在客户端,使用一个单独的BluetoothSocket类去初始化一个外接连接和管理该连接。

    最通常使用的蓝牙端口是RFCOMM,它是一个面向连接,通过蓝牙模块进行数据流传输方式,也被称为串行端口规范(SPP)。

    创建一个BluetoothSocket去连接到一个已知设备,使用方法BluetoothDevice.createRfcommSocketToServiceRecord()。然后调用connect()方法去尝试一个面向远程设备的连接。这个调用将被阻塞指导一个连接已经建立或者该链接失效。

    当端口连接成功后,通过getInputStream()和getOutputStream()来打开I/O流,从而获得各自的InputStream和OutputStream对象。

    BluetoothSocket类的线程是安全的,因为close()方法会马上放弃外界操作并关闭服务器端口。

  • BluetoothSocket类的公共方法

    • public void close()
    • public void connect()
    • public InputStream getInputStream()
    • public OutputStream getOutputStream()
    • public BluetoothDevice getRemoteDevice()

BluetoothServerSocket类

  • BluetoothServerSocket类的公共方法

    • public BluetoothSocket accept(int timeout)
    • public BluetoothSocket accept()
    • public void close()

BluetoothAdapter类

  • BluetoothAdapter类基础

    代表本地的蓝牙适配器设备,通过此类可以让用户能执行基本的蓝牙任务,如初始化设备的搜索、查询可匹配的设备集、使用一个已知的MAC地址来初始化一个BluetoothDevice类、创建一个BluetoothServerSocket类以监听其他设备对本机的连接请求等。

    调用静态方法getDefaultAdapter()获得,这是所有蓝牙动作使用的第一步。当拥有本地适配器以后,用户可以获得一系列的BluetoothDevice对象,这些对象代表所有拥有getBondedDevice()方法的已经匹配的设备;用startDiscovery()方法来开始设备的搜寻;或者创建一个BluetoothServerSocket类,通过listenUsingRfcommWithServiceRecord(String,UUID)方法来监听新来的连接请求。

    大部分方法需要BLUETOOTH权限,一些方法同时需要BLUETOOTH_ADMIN权限。

  • BluetoothAdapter类的常量

    • String ACTION_DISCOVERY_FINISHED

      广播事件:本地蓝牙适配器已经完成设备的搜索过程,需要BLUETOOTH权限接收。常量值:android.bluetooth.adapter.action.DISCOVERY_FINISHED
    • String ACTION_DISCOVERY_STARTED

      广播事件:本地蓝牙适配器已经开始对远程设备的搜寻过程。用户可以通过cancelDiscovery()类来取消正在执行的查找过程,需要BLUETOOTH权限接收。常量值:android.bluetooth.adapter.action.DISCOVERY_STARTED
    • String ACTION_LOCAL_NAME_CHANGED

      广播事件:本地蓝牙适配器已经更改了它的蓝牙名称,需要BLUETOOTH权限接收。常量值:android.bluetooth.adapter.action.LOCAL_NAME_CHANGED
    • String ACTION_REQUEST_DISCOVERABLE

      Activity活动:显示一个请求被搜寻模式的系统活动。Android运用onActivityResult(int,int,Intent)回收方法来传递该活动结果的通知。需要BLUETOOTH权限。常量值:android.bluetooth.adapter.action.REQUEST_DISCOVERYABLE
    • String ACTION_REQUEST_ENABLE

      Activity活动:显示一个允许用户打开蓝牙模块的系统活动。Android运用onActivityResult(int,int,Intent)回收方法来传递该活动结果的通知。需要BLUETOOTH权限。常量值:android.bluetooth.adapte.action.REQUEST_ENABLE
    • String ACTION_SCAN_MODE_CHANGED

      广播活动:指明蓝牙扫描模块或者本地适配器已经发生变化,需要BLUETOOTH权限。常量值:android.bluetooth.adapter.action.SCAN_MODE_CHANGED
    • String ACTION_STATE_CHANGED

      广播活动:本来的蓝牙适配器的状态已经改变,如蓝牙模块已经被打开或者关闭,需要BLUETOOTH权限接收。常量值:android.bluetooth.adapter.action.STATE_CHANGED
    • int ERROR

      功能:标记该类的错误值,确保和该类中的任意其他整数常量不相等。它为需要一个标记错误值的函数提供了便利,例如:Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,BluetoothAdapter.ERROR);
    • String EXTRA_DISCOVERABLE_DURATION

      功能:试图在ACTION_REQUEST_DISCOVERABLE常量中作为一个可选的整型附加域,来为短时间内的设备发现请求一个特定的持续时间,默认值为120秒,超过300秒的请求将被限制。常量值:android.bluetooth.adapter.extra.DISCOVERABLE_DURATION
    • String EXTRA_LOCAL_NAME

      功能:试图在ACTION_LOCAL_NAME_CHANGED常量中作为一个字符串附加域,来请求本地蓝牙的名称。常量值:android.bluetooth.adapter.extra.LOCAL_NAME
    • String EXTRA_PREVIOUS_SCAN_MODE

      功能:试图在ACTION_SCAN_MODE_CHANGED常量中作为一个整型附加域,来请求以前的扫描模式,可能值如下:    SCAN_MODE_NONE    SCAN_MODE_CONNECTABLE    SCAN_MODE_CONNECTABLE_DISCOVERABLE常量值:android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE
    • String EXTRA_PREVIOUS_STATE

      功能:试图在ACTION_STATE_CHANGED常量中作为一个整型附加域,来请求以前的供电状态。可能值如下:    STATE_OFF    STATE_TURNING_ON    STATE_ON    STATE_TURNING_OFF常量:android.bluetooth.adapter.extra.PREVIOUS_STATE
    • String EXTRA_SCAN_MODE

      功能:试图在ACTION_SCAN_MODE_CHANGED常量作为一个整型附加域,来请求当前的扫描模式,可能值如下:    SCAN_MODE_NONE    SCAN_MODE_CONNECTABLE    SCAN_MODE_CONNECTABLE_DISCOVERABLE常量值:android.bluetooth.adapter.extra.SCAN_MODE
    • String EXTRA_STATE

      功能:试图在ACTION_STATE_CHANGED常量中作为一个整型附加域,来请求当前供电状态,可能值如下:    STATE_OFF    STATE_TURNING_ON    STATE_ON    STATE_TURNING_OFF常量值:android.bluetooth.adapter.extra.STATE
    • int SCAN_MODE_CONNECTABLE

      功能:指明在本地蓝牙适配器中,查询扫描功能失效,但页面扫描功能有效。因此,该设备不能被远程蓝牙设备发现,但如果以前曾经发现过该设备,则远程设备可以对其进行连接。常量值:21(0x00000015)
    • int SCAN_MODE_CONNECTABLE_DISCOVERABLE

      功能:指明在本地蓝牙适配器中, 查询扫描功能都有效,因此,该设备既可以被远程蓝牙设备发现,也可以被其连接。常量值:23(0x00000017)
    • int SCAN_MODE_NONE

      功能:指明在本地蓝牙适配器中,查询扫描功能和页面扫描功能都失效,因此,该设备既不可以被远程蓝牙设备发现,也不可以被连接。常量值:20(0x00000014)
    • int STATE_OFF

      功能:指明本地蓝牙适配器模式已经关闭常量值:10(0x0000000a)
    • int STATE_ON

      功能:指明本地蓝牙适配器模块已经打开,并且准备被使用
    • int STATE_TURNING_OFF

      功能:指明本地蓝牙适配器模块正在关闭。本地客户端可以立刻尝试友好地断开任意外部连接。常量值:13(0x0000000d)
    • int STATE_TURNING_ON

      功能:指明本地蓝牙适配器模块正在打开,然而本地客户在尝试使用这个适配器之前需要为STATE_ON状态而等待常量值:11(0x000000b)
  • BluetoothAdapter类的公共方法

    • public boolean cancelDiscovery()
    • public static boolean checkBluetoothAddress(String address)
    • public boolean disable()
    • public boolean enable()
    • public String getAddress()
    • public Set getBondedDevices()
    • public static synchronized BluetoothAdapter getDefaultAdapter()
    • public String getName()
    • public BluetoothDevice getRemoteDevice(String address)
    • public int getScanMode()
    • public int getState()
    • public boolean isDiscovering()
    • public boolean isEnabled()
    • public BluetoothServerSocket ListenUsingRfcommWithServiceRecord(String name,UUID uuid)
    • public boolean setName(String name)
    • public boolean startDiscovery()

BluetoothClass.Service类

用于定义所有的服务类常量,任意BluetoothClass由0或多个服务类编码组成。在类BluetoothClass.Service中包含如下常量:

  • int AUDIO
  • int CAPTURE
  • int INFORMATION
  • int LIMITED_DISCOVERABILITY
  • int NETWORKING
  • int OBJECT_TRANSFER
  • int POSITIONING
  • int RENDER
  • int TELEPHONY

BluetoothClass.Device类

用于定义所有的设备类的常量,每个BluetoothClass有一个带有主要和较小部分的设备类进行编码,里面的常量代表主要和较小的设备类部分(完整的设备类)的组合。

在Android平台开发蓝牙应用程序

  • 1.设置权限

    <uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
  • 2.启动蓝牙

    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if(mBluetoothAdapter == null){    //表示手机不支持蓝牙    return;}if(!mBluetoothAdapter.isEnabled()){ //蓝牙未开启,则开启蓝牙    Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);    startActivityForResult(enableIntent,REQUEST_ENABLE_BT);}//...public void onActivityResult(int requestCode,int resultCode,Intent data){    if(requestCode == REQUEST_ENABLE_BT){        if(resultCode == RESULT_OK){            //蓝牙已经开启        }    }}
  • 3.发现蓝牙设备

    //使本机蓝牙在300秒内可被搜索private void ensureDiscoverable(){    if(mBluethAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE){        Intent discoverableIntent = new Intent(BluetoothAdapter.ATION_REQUEST_DISCOVERABLE);         intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,300);        startActivity(discoverableIntent);    }}//查找已经配对的蓝牙设备,即以前已经配对过的设备Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();if(pairedDevices.size() > 0){    findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);    for(BluetoothDevice device : pairedDevices){        //device.getName() + " " + device.getAddress();    }}else{    mPairedDevicesArrayAdapter.add("没有找到已配对的设备");}//注册,当一个设备被发现时调用onReceiveIntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);this.registerReceiver(mReceiver,filter);//当搜索结束后调用onReceivefilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);this.registerReceiver(mReceiver,filter);//...private BroadcastReceiver mReceiver = new BroadcastReceiver(){    @Override    public void onReceiver(Context context,Intent intent){        String action = intent.getAction();        if(BluetoothDevice.ACTION_FOUND.equal(action)){            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);            //已经配对的则跳过            if(device.getBondState() != BluetoothDevice.BOND_BONDED){                mNewDevicesArrayAdapter.add(device.getName()+"\n"+device.getAddress());             }        }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equal(action)){            //搜索结束            if(mNewDevicesArrayAdapter.getCount() == 0){                mNewDevicesArrayAdapter.add("没有搜索到设备");            }        }    }}
  • 4.建立连接

    //UUID可以看做一个端口号private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");//像一个服务器一样时刻监听是否有连接建立private class AcceptThread extend Thread{    private BluetoothServerSocket serverSocket;    public AcceptThread(boolean secure){        BluetoothServerSocket temp = null;        try{            temp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_INSECURE,MY_UUID);        }catch(IOException e){            Log.e("TAG","listen() failed "+e);        }        serverSocket = temp;    }    public void run(){        BluetoothSocket socket = null;        while(true){            try{                socket = serverSocket.accept();            }catch(IOException e){                Log.e("TAG","accept() failed "+e);                break;            }        }        if(socket != null){            //此时可以新建一个数据交换线程,把此socket传进去        }    }    //取消监听    public void cancel(){        try{            serverSocket.close();        }catch(){            Log.e("TAG","Socket Type "+socketType+" close() of server failed "+e);        }    }}
  • 5.交换数据

    //另一个设备去连接本机,相当于客户端private class ConnectThread extends Thread{    private BluetoothSocket socket;    private BluetoothDevice device;    public ConnectThread(BluetoothDevice device,boolean secure){        this.device = device;        BluetoothSocket tmp = null;        try{            tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);        }catch(IOException e){            Log.e("TAG","create() failed "+e);        }    }    public void run(){        mBluetoothAdapter.cancelDiscovery(); //取消设备查找        try{            socket.connect();        }catch(IOException e){            try{                socket.close();            }catch(IOException e1){                Log.e("TAG","unable to close "+"socket during connection failure "+e1);            }            connectionFailed(); //连接失败            return;        }        //此时可以新建一个数据交换线程,把此socket传进去    }    public void cancel(){        try{            socket.close();        }catch(){            Log.e("TAG","close() of connect socket failed "+e);        }    }}
  • 6.建立数据通信线程

    //建立连接后,进行数据通信的线程private class ConnectedThread extend Thread{    private BluetoothSocket socket;    private InputStream inStream;    private OutputStream outStream;    public ConnectedThread(BluetoothSocket socket){        this.socket = socket;        try{            //获取输入/输出流            inStream = socket.getInpuStream();            outStream = socket.getOutputStream();        }catch(IOException e){            Log.e("TAG","temp sockets not created "+e);        }    }    public void run(){        byte[] buff = new byte[1024];        int len = 0;        //读取数据需不断监听,写不需要        while(true){            try{                len = inStream.read(buff);                //把读取到的数据发送给UI进行显示                  Message msg = handler.obtainMessage(BluetoothChat.MESSAGE_READ,len,-1,buff);            }catch(IOException e){                Log.e("TAG","disconnected "+e);                connectionLost(); //失去连接                start(); //重新启动服务器                break;            }        }    }    public void write(byte[] buffer){        try{            outStream.write(buffer);            handler.obtainMessage(BluetoothChat.MESSAGE_WRITE,-1,-1,buffer);            msg.sendToTarget();        }catch(IOException e){            Log.e("TAG","Exception during write "+e);        }    }    public void cancel(){        try{                socket.close();        }catch(IOException e){            Log.e("TAG","close() of connect socket failed "+e);        }    }}

更多相关文章

  1. 浅析Android(安卓)M新功能Adoptable Storage Devices(适配的存储
  2. 第2步:第一个“Hello,world!”之Android(安卓)App(从零开始学Andro
  3. android之handler更新UI
  4. Android(安卓)U盘文件显示在文件管理器
  5. adb安装配置及连接手机
  6. 如何获得Android设备名称(ADB命令详细介绍)
  7. 通过ADB命令行卸载或删除你的Android设备中的应用
  8. Android中贪吃蛇游戏的学习(一)
  9. Android(安卓)蓝牙4.0详细

随机推荐

  1. Mysql中正则表达式Regexp常见用法
  2. MySQL查询指定字段不是数字与逗号的sql
  3. MYSQL开启远程访问权限的方法
  4. MySQL延迟问题和数据刷盘策略流程分析
  5. Workbench连接不上阿里云服务器Ubuntu的M
  6. MySQL 的覆盖索引与回表的使用方法
  7. 详解Mysql查询条件中字符串尾部有空格也
  8. MySQL 学习总结 之 初步了解 InnoDB 存储
  9. 解决MySql客户端秒退问题(找不到my.ini)
  10. MySQL延时复制库方法详解