Android蓝牙开发简介

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("没有找到已配对的设备");
    }
    
    //注册,当一个设备被发现时调用onReceive
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    this.registerReceiver(mReceiver,filter);
    //当搜索结束后调用onReceive
    filter = 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);
            }
        }
    }
    
时间: 2024-10-26 16:13:26

Android蓝牙开发简介的相关文章

如何实现android蓝牙开发 自动配对连接,并不弹出提示框

如何实现android蓝牙开发 自动配对连接,并不弹出提示框 之前做一个android版的蓝牙,遇到最大的难题就是自动配对. 上网查资料说是用反射createBond()和setPin(),但测试时进行配对还是会出现提示,但配对是成功了 我就开始查找怎么关闭这个蓝牙配对提示框,后面还是伟大的android源码帮助了我. 在源码 BluetoothDevice 类中还有两个隐藏方法 cancelBondProcess()和cancelPairingUserInput() 这两个方法一个是取消配对进

Android蓝牙开发的一些经验

转载请注明来自:http://blog.csdn.net/icyfox_bupt/article/details/25487125 最近在实验室做项目,使用了Android的蓝牙开发,这里面有好多坑..所以还是希望能记下来这些东西和大家分享,不要再走我的老路了. 先说一下背景,我是开发手机与带蓝牙的智能设备(蓝牙血压计.血糖仪.手环等)设备对接的APP.也就是说,在设备端没有什么可以操作的,手机负责发起数据传输. 蓝牙连接,不需要配对 由于被曾经使用蓝牙的思路所误导,一直以为使用蓝牙是必须一个配

Android蓝牙开发

Android蓝牙开发 近期做蓝牙小车,须要Android端来控制小车的运动.以此文记录开发过程. 使用HC-06无线蓝牙串口透传模块.对于其它的蓝牙设备本文相同适用. 蓝牙开发的流程: 获取本地蓝牙适配器    -->     打开蓝牙    -->    搜索设备  -->   连接设备  -->   发送信息 首先为了避免以往我们先写入蓝牙权限: <uses-permission android:name="android.permission.BLUETOO

Android蓝牙开发入门

目录: 1. 蓝牙简史,现状 2. 蓝牙的应用场景 3. 蓝牙相关概念 4. Android蓝牙开发 1. 蓝牙简史: 蓝牙( Bluetooth)是一种无线技术标准,可以实现短距离(通常是几米范围之内)的无线通信.蓝牙技术始于1994年,迄今已经发展了超过20年.本质上它和其它几种射频通信技术类似,比如手机移动通信,近场通信技术(NFC),都是通过电磁波来实现不同设备的信息交换.区别在于无线电波的频率和发射功率不一样,从而传输距离也不一样. 2. 蓝牙的应用场景: l 移动电话和免提设备之间的

Android蓝牙开发,报BluetoothAdapter﹕ Can&#39;t create handler inside thread that has not called Looper.prepare

这个错误翻译的意思是:不能在没有Looper.prepare的线程里面创建handler. 起初我很疑惑,我根本没有用到工作线程,也没有创建handler.报错的代码如下: // Device scan callback. private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final Blu

Android 蓝牙开发

此例子基于 Android demo 对于一般的软件开发人员来说,蓝牙是很少用到的,尤其是Android的蓝牙开发,国内的例子很少     Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多技术人员的开发:    鉴于很多开发人员现在也有蓝牙开发的需求,也为了大家少走些弯路,先将我积攒的一点点在Android蓝牙开发经验与大家分享一下! 首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 <uses-permis

Android 蓝牙开发之搜索、配对、连接、通信大全

        蓝牙( Bluetooth®):是一种无线技术标准,可实现固定设备.移动设备和楼宇个人域网之间的短距离数据 交换(使用2.4-2.485GHz的ISM波段的UHF无线电波).蓝牙设备最多可以同时和7个其它蓝牙设备建立连接,进 行通信,当然并不是每一个蓝牙都可以达到最大值.下面,我们从蓝牙的基本概念开始,一步一步开始了解蓝牙. 基本概念: 安卓平台提供对蓝牙的通讯栈的支持,允许设别和其他的设备进行无线传输数据.应用程序层通过安卓API来调用蓝牙的相关功 能,这些API使程序无线连接

【转】android蓝牙开发---与蓝牙模块进行通信--不错

原文网址:http://www.cnblogs.com/wenjiang/p/3200138.html 近半个月来一直在搞android蓝牙这方面,主要是项目需要与蓝牙模块进行通信.开头的进展很顺利,但因为蓝牙模块不在我这里,所以只能用手机测试.一开头就发现手机的蓝牙不能用,为了证明这点,我刷了四次不同不同系统的官方包,正式宣布手机的蓝牙报销了,于是和朋友换手机.在测试的过程中也是非常痛苦,放假了,同学都几乎回家了,剩下的同学中竟然80%都是用非android手机!我和我的小伙伴都吓呆了!!就算

Android蓝牙开发---站在前辈的肩膀上唠嗑

描述一段背景:前年我找工作时,总碰到一个问题. 面试官问:"你会蓝牙开发吗?". 我说:"不会". 面试官答:"那,很抱歉.我们商量了一下,觉得你不适合这个岗位." 于是我就走了,心里想:"就应为一个蓝牙通讯技术不会,就把我给cut了,这面试官好有想象力." 我一个同学,都没做过编程,我半年时间都带到android开发道上了.我仅仅蓝牙没做过,研究蓝牙无非就是三两天的时间,难吗? 于是,我周末窝在家里,查阅了大量资料,实践和总