Android蓝牙开发介绍

来源:互联网 发布:软件开发工程师怎么样 编辑:程序博客网 时间:2024/06/06 00:18

蓝牙(BlueTooth)是一种无线技术标准,是当今移动终端最流行的三种数据传输方案之一,其余两种是WiFi和NFC(由于红外传输只能是直线传输,故更多地用于遥控器等设备,不适合数据传输)。蓝牙的传输特点是传输距离短(≤10m),速度适中,为24Mbps(比WiFi(802.11ac 1.3Gbps)慢,比NFC(≤400Kbit/s)快),功耗低(最新4.1版本)。
本文将介绍在Android移动设备上蓝牙技术的使用。


添加蓝牙权限

调用系统蓝牙技术方案实现设备间的数据传输,涉及到侵犯用户的隐私,需添加如下权限:

//程序中使用了蓝牙技术需要添加的权限<uses-permission android:name="android.permission.BLUETOOTH" />//当本程序需要和其他程序绑定时添加的权限<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

检测设备是否支持蓝牙

创建BlueToothAdapter对象,判断该对象是否为空,以确定当前设备是否支持蓝牙:

//获得BluetoothAdapter对象BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();//通过判断BluetoothAdapter对象是否为空,来确定设备是否支持蓝牙功能 if (mAdapter != null) {            //设备支持蓝牙功能        } else {            //设备不支持蓝牙功能        }

判断当前设备的蓝牙是否处于开启状态

//断言当前设备具备蓝牙功能assert(mAdapter != null);//若BluetoothAdapter.isEnabled()返回true,则当前设备处于开启状态,否则处于关闭状态if(mAdapter.isEnabled()){    //当前设备的蓝牙处于开启状态}else{    //当前设备的蓝牙处于关闭状态}

开启和关闭蓝牙功能

通过隐式intent开启蓝牙功能。可以在onActivityResult()方法中获取开启成功与否的消息;也可以使用广播接收器(BroadcastReceiver)接收系统发出的关于设备蓝牙状态的广播消息。

//使用隐式intent开启蓝牙Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);//启动蓝牙startActivityForResult(intent, 0);//在onActivityResult()方法中得知蓝牙是否开启@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);     if(requestCode == 0)     {      if(resultCode == RESULT_OK)      {        //打开成功      }      else      {        //打开失败      }     }}//通过BroadcastReceiver接收系统发出的关于设备蓝牙状态的广播消息//在onCreate方法中动态注册BroadcastReceiver@Overrideprotected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //表示程序希望接收到蓝牙状态发生改变时,系统发出的广播        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);        registerReceiver(receiver, filter);}//定义BroadcastReceiver截获广播private BroadcastReceiver receiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            //截获的广播附带有BluetoothAdapter.EXTRA_STATE信息,通过该键对应的值可以判断系统发出的是什么广播,从而确定蓝牙处于何种状态            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);            switch (state) {                //蓝牙处于关闭状态                case BluetoothAdapter.STATE_OFF:                    //"STATE_OFF"状态                    break;                //蓝牙处于开启状态                case BluetoothAdapter.STATE_ON:                    //"STATE_ON"状态                    break;                //蓝牙处于正在打开状态(开启蓝牙一个异步操作)                case BluetoothAdapter.STATE_TURNING_ON:                    //"STATE_TURNING_ON"状态                    break;                //蓝牙处于正在关闭状态                case BluetoothAdapter.STATE_TURNING_OFF:                    //"STATE_TURNING_OFF"状态                    break;                default:                    break;            }        }    };

设置设备的可见性

蓝牙设备为了安全起见,可设置为不可见,其他设备无法搜索到蓝牙处于不可见状态的设备;反之,若需要被其他设备搜索到,需要将蓝牙设为可见:

//通过隐式intent设置蓝牙的可见性Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);//设置蓝牙的可见性为300秒(5分钟)discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);startActivity(discoverableIntent);

当设备变为可被搜索或变为不可被搜索时,系统会发出一条广播,注册一个广播接收器可获得系统发出的广播:

IntentFilter filter = new IntentFilter();//监听 “设备变为可被搜索时”(也可以监听“设备变为不可见时”),系统发出的广播filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//注册该广播registerReceiver(mReceiver, filter);

在onReceive方法中,接受广播:

private BroadcastReceiver mReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        //接收“当蓝牙可见状态发生改变时”系统发出的广播if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {                //获取该广播所附带的BluetoothAdapter.EXTRA_SCAN_MODE键对应的值                int scanMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE, 0);                //若值为BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE,表示蓝牙变为可见状态                if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {                    setProgressBarIndeterminateVisibility(true);                }                 //否则蓝牙变为不可见状态                else {                    setProgressBarIndeterminateVisibility(false);                }            }}

查找并绑定设备

开启蓝牙后,可以通过下面方法查找周围已开启蓝牙且蓝牙可见的设备:

BlueToothAdapter.startDiscovery();

当程序开始查找设备、结束查找设备、发现一个可见的设备,系统都会发送一条广播,程序需要通过广播接收器接受响应状态发生时系统发出的广播,当接收到的广播所附带的action为BluetoothAdapter.ACTION_DISCOVERY_STARTED时,表示程序开始查找设备;action为BluetoothAdapter.ACTION_DISCOVERY_FINISHED时,表示程序结束查找,action为BluetoothDevice.ACTION_ FOUND时,intent附带的键BluetoothDevice.EXTRA_DEVICE所对应的值,就是这个搜索到的蓝牙设备:

//查找设备filter.addAction(BluetoothDevice.ACTION_FOUND);registerReceiver(mReceiver, filter);//接收搜索到的设备private BroadcastReceiver mReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {                setProgressBarIndeterminateVisibility(true);                //初始化数据列表                mDeviceList.clear();                mAdapter.notifyDataSetChanged();            } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {                setProgressBarIndeterminateVisibility(false);            }        //搜索到一个设备        else if (BluetoothDevice.ACTION_FOUND.equals(action)) {                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);                //将搜索到的设备添加到ListView中                mDeviceList.add(device);                //Adapter通知刷新ListView                mAdapter.notifyDataSetChanged();        }

查找到设备后,可通过createBond()方法对设备进行绑定。createBond()方法在API 19 (Android 4.4) 及以上被引入,低于API 19的版本不支持蓝牙绑定:

mListView.setOnItemClickListener(bindDeviceClick);private AdapterView.OnItemClickListener bindDeviceClick = new AdapterView.OnItemClickListener() {        @Override        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {            BluetoothDevice device = mDeviceList.get(i);            //绑定选中的设备(需API级别≥19),同时需要权限<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />            device.createBond();

当绑定或解绑设备时,系统也会发出广播,在onReceive()方法中捕获该广播:

//绑定状态发生改变if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {                BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);                //没有绑定的设备                if (remoteDevice == null) {                    //未绑定任何设备                    return;                }                //绑定设备处于何种状态                int status = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 0);                //已绑定设备                if (status == BluetoothDevice.BOND_BONDED) {                    //"Bonded " + remoteDevice.getName())                }                 //正在绑定设备                else if (status == BluetoothDevice.BOND_BONDING) {                    //"Bonding " + remoteDevice.getName())                }                 //结束绑定设备                else if (status == BluetoothDevice.BOND_NONE) {                    //"Not bond " + remoteDevice.getName())                }            }

蓝牙传输数据


Socket


在TCP/IP通信中,可使用Socket交换数据,客户端(Client)和服务器端(Server)的通信流程如上图所示。在客户端,首先建立一个socket对象,然后建立一个connect连接服务,在connect方法中传入需要要换数据的服务器端IP地址和端口号(在蓝牙传输中,这里有所不同),接着利用IO流读写数据,最后关闭连接服务;在服务器端,同样需要建立一个socket连接,并绑定端口号,接着客户端将等待client端的socket的连接请求,此时会阻塞当前线程,所以需要将该操作放到子线程中执行,当有client请求连接时,服务器端accept该请求,并建立一个新的socket对象与之通信,原有socket继续等待,当新建socket与客户端通信结束后,断开连接。(一般情况下由客户端中断连接)
在蓝牙的socket通信中,服务器端通信流程不包含上图所示的bind()和listen()步骤,也就是说,socket创建后直接到达accept,阻塞也是发生在accept;客户端的流程就是上图所示,但参数与TCP/IP通信时不一样。
总结起来,Android蓝牙的server端的建立步骤如下:

  1. 通过listenUsingRfcommWithServiceRecord创建BluetoothServerSocket对象;
  2. 监听网络accept;(阻塞线程)
  3. 处理网络socket;
  4. 关闭连接。

client端的建立步骤如下:

  1. 通过createRfcommSocketToServiceRecord创建BlueToothSocket对象;
  2. 建立服务器端connect;(阻塞线程)
  3. 处理数据;
  4. 关闭连接。

服务端

当触发监听时,程序进入监听状态,此时设备可看成分服务器端,用于等待其他设备绑定:

//创建一个新线程,用于处理会发生阻塞的监听网络的方法acceptprivate AcceptThread mAcceptThread;//获取BlueToothAdapter对象private BlueToothController mController = new BlueToothController();//创建Handler对象,用于用于在子线程中通知主线程(UI线程)更新UIprivate Handler mUIHandler = new MyHandler();private class MyHandler extends Handler {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                //开始监听时,接收由子线程发来的消息                case Constant.MSG_START_LISTENING:                //更新UI                    setProgressBarIndeterminateVisibility(true);                    break;                //结束监听时,接收由子线程发来的消息                case Constant.MSG_FINISH_LISTENING:                //更新UI                setProgressBarIndeterminateVisibility(false);                    break;                    //接收到数据后,接收由子线程发来的消息及其附加信息                case Constant.MSG_GOT_DATA:                //在UI线程中显示                    showToast("data: "+String.valueOf(msg.obj));                    break;                    //当发生错误时,接收由子线程发来的消息                case Constant.MSG_ERROR:                //显示错误原因                    showToast("error: "+String.valueOf(msg.obj));                    break;                    //当程序作为客户端连接到服务器后,接收接收由子线程发来的消息                case Constant.MSG_CONNECTED_TO_SERVER:                //通知用户连接成功                    showToast("Connected to Server");                    break;                    //当程序作为服务器端绑定了一个客户端后,接收接收由子线程发来的消息                case Constant.MSG_GOT_A_CLINET:                //通知用户绑定成功                    showToast("Got a Client");                    break;            }        }    }//触发监听状态@OverrideonClick(View v){    if( mAcceptThread != null) {                mAcceptThread.cancel();            }            //创建一个新线程,传入BlueToothAdapter对象实例和Handler对象实例            mAcceptThread = new AcceptThread(mController.getAdapter(), mUIHandler);            //启动该线程            mAcceptThread.start();}

子线程AcceptThread如下所示:

public class AcceptThread extends Thread {    private static final String NAME = "BlueToothClass";    //为了保证蓝牙连接,必须使用如下代码的唯一的UUID:    public static final String CONNECTTION_UUID = "00001101-0000-1000-8000-00805F9B34FB";    private static final UUID MY_UUID = UUID.fromString(CONNECTTION_UUID);    private final BluetoothServerSocket mmServerSocket;    private final BluetoothAdapter mBluetoothAdapter;    private final Handler mHandler;    private ConnectedThread mConnectedThread;    public AcceptThread(BluetoothAdapter adapter, Handler handler) {        mBluetoothAdapter = adapter;        mHandler = handler;        // 构造方法中创建一个临时的BluetoothServerSocket对象的引用        BluetoothServerSocket tmp = null;        try {            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);        } catch (IOException e) { }        mmServerSocket = tmp;    }//子线程中运行的代码    public void run() {        BluetoothSocket socket = null;        // 不断监听来自客户端的连接请求        while (true) {            try {//通知UI线程:“服务器端开始监听客户端的请求”mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);//accept方法会阻塞线程                socket = mmServerSocket.accept();            } catch (IOException e) {//若出现异常,退出监听                mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, e));                break;            }            // 连接成功            if (socket != null) {                // 另开启一个子线程,新建一个socket用于传输数据                manageConnectedSocket(socket);                try {                //强制关闭监听                    mmServerSocket.close();                    mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);                } catch (IOException e) {                    e.printStackTrace();                }                break;            }        }    }    private void manageConnectedSocket(BluetoothSocket socket) {        //只支持同时处理一个连接        if( mConnectedThread != null) {            mConnectedThread.cancel();        }        //通知UI线程连接到一个蓝牙设备(客户端)        mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);        //另开启的新线程,用于传输数据,传入BluetoothSocket对象引用和Handler对象引用        mConnectedThread = new ConnectedThread(socket, mHandler);        //启动线程        mConnectedThread.start();    }    //主动停止监听    public void cancel() {        try {            mmServerSocket.close();//通知UI线程:“已停止监听”            mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);        } catch (IOException e) { }    }//向客户端写入数据    public void sendData(byte[] data) {        if( mConnectedThread!=null){            mConnectedThread.write(data);        }    }}

另创建的用于与绑定的设备进行数据通信的线程:

public class ConnectedThread extends Thread {    private final BluetoothSocket mmSocket;    private final InputStream mmInStream;    private final OutputStream mmOutStream;    private final Handler mHandler;    public ConnectedThread(BluetoothSocket socket, Handler handler) {        mmSocket = socket;        InputStream tmpIn = null;        OutputStream tmpOut = null;        mHandler = handler;        //获得输入输出流        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()        // 不断循环以读取数据        while (true) {            try {                // 读取数据                bytes = mmInStream.read(buffer);                // 将读取的数据信息发送至UI线程并显示数据                if( bytes >0) {                    Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA, new String(buffer, 0, bytes, "utf-8"));                    mHandler.sendMessage(message);                }                Log.d("GOTMSG", "message size" + bytes);            } catch (IOException e) {                mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, e));                break;            }        }    }    //写入数据    public void write(byte[] bytes) {        try {            mmOutStream.write(bytes);        } catch (IOException e) { }    }    //取消连接    public void cancel() {        try {            mmSocket.close();        } catch (IOException e) { }    }}

客户端

当设备作为客户端与其他设备(服务端)交换数据时,需保证设备之间已绑定:

//作为客户端连接,设备之间处于绑定状态mListView.setOnItemClickListener(bindedDeviceClick);private AdapterView.OnItemClickListener bindedDeviceClick = new AdapterView.OnItemClickListener() {        @Override        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {            BluetoothDevice device = mBondedDeviceList.get(i);            if( mConnectThread != null) {                mConnectThread.cancel();            }            //创建子线程            mConnectThread = new ConnectThread(device, mController.getAdapter(), mUIHandler);            mConnectThread.start();        }    };

客户端的线程与服务器端的子线程类似,代码如下:

public class ConnectThread extends Thread {//UUID必须与之前的UUID一致    private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);    private final BluetoothSocket mmSocket;    private final BluetoothDevice mmDevice;    private BluetoothAdapter mBluetoothAdapter;    private final Handler mHandler;    private ConnectedThread mConnectedThread;    public ConnectThread(BluetoothDevice device, BluetoothAdapter adapter, Handler handler) {        // because mmSocket is final        BluetoothSocket tmp = null;        mmDevice = device;        mBluetoothAdapter = adapter;        mHandler = handler;        try {            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);        } catch (IOException e) { }        mmSocket = tmp;    }    public void run() {        // 首先使设备不再查找其他设备,以提高效率        mBluetoothAdapter.cancelDiscovery();        try {            //connect方法会阻塞线程            mmSocket.connect();        } catch (Exception connectException) {            mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, connectException));            // 连接错误,结束线程并抛出异常            try {                mmSocket.close();            } catch (IOException closeException) { }            return;        }        // 与设备交换数据需定义在一个新的线程中(新建一个socket专门用于交互数据)        manageConnectedSocket(mmSocket);    }    private void manageConnectedSocket(BluetoothSocket mmSocket) {        mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);        mConnectedThread = new ConnectedThread(mmSocket, mHandler);        mConnectedThread.start();    }    public void cancel() {        try {            mmSocket.close();        } catch (IOException e) { }    }    public void sendData(byte[] data) {        if( mConnectedThread!=null){            mConnectedThread.write(data);        }    }}


蓝牙规范简介

蓝牙规范(BlueTooth Profile)又叫配置文件,是一种协议。蓝牙规范定义了一种基于蓝牙的应用(如蓝牙耳机、车载蓝牙设备、蓝牙鼠标),每各蓝牙规范主要包括针对开发者的接口、消息的格式和标准,以及如何使用蓝牙协议,使用蓝牙协议通信的蓝牙设备必须使用同一UUID,否则无法配对通信。
常用的蓝牙规范:

  • A2DP(Advanced Audio Distribution Profile)蓝牙音频传输模型
    –应用方式:蓝牙立体声耳机+移动终端
    –用途:听音乐等

  • BlueTooth HeadSet 带打电话功能的蓝牙耳机
    –用途:蓝牙耳机+移动终端、车载蓝牙+移动终端


下列代码以HeadSet为例,演示了使用蓝牙耳机与手机连接的蓝牙协议:

public abstract class BluetoothHeadsetUtils {    private Context mContext;    private BluetoothAdapter mBluetoothAdapter;    private BluetoothHeadset mBluetoothHeadset;    private BluetoothDevice mConnectedHeadset;    private AudioManager mAudioManager;    private boolean mIsOnHeadsetSco;    private boolean mIsStarted;    /**     * Constructor     *      * @param context     */    public BluetoothHeadsetUtils(Context context) {        mContext = context;        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);    }    /**     * Call this to start BluetoothHeadsetUtils functionalities.     *      * @return The return value of startBluetooth() or startBluetooth11()     */    public boolean start() {        if (mBluetoothAdapter.isEnabled() == false){            mIsStarted = false;            return mIsStarted;        }        if (!mIsStarted) {            mIsStarted = true;            mIsStarted = startBluetooth();        }        return mIsStarted;    }    /**     * Should call this on onResume or onDestroy. Unregister broadcast receivers     * and stop Sco audio connection and cancel count down.     */    public void stop() {        if (mIsStarted) {            mIsStarted = false;            stopBluetooth();        }    }    /**     *      * @return true if audio is connected through headset.     */    public boolean isOnHeadsetSco() {        return mIsOnHeadsetSco;    }    public abstract void onHeadsetDisconnected();    public abstract void onHeadsetConnected();    public abstract void onScoAudioDisconnected();    public abstract void onScoAudioConnected();    /**     * Register a headset profile listener     *      * @return false if device does not support bluetooth or current platform     *         does not supports use of SCO for off call or error in getting     *         profile proxy.     */    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    private boolean startBluetooth() {        // Device support bluetooth        if (mBluetoothAdapter != null) {            if (mAudioManager.isBluetoothScoAvailableOffCall()) {                // All the detection and audio connection are done in                // mHeadsetProfileListener                if (mBluetoothAdapter.getProfileProxy(mContext, mHeadsetProfileListener, BluetoothProfile.HEADSET)) {                    return true;                }            }        }        return false;    }    /**     * API >= 11 Unregister broadcast receivers and stop Sco audio connection     * and cancel count down.     */    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    protected void stopBluetooth() {        if (mBluetoothHeadset != null) {            // Need to call stopVoiceRecognition here when the app            // change orientation or close with headset still turns on.            mBluetoothHeadset.stopVoiceRecognition(mConnectedHeadset);            mContext.unregisterReceiver(mHeadsetBroadcastReceiver);            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);            mBluetoothHeadset = null;        }    }    /**     * API >= 11 Check for already connected headset and if so start audio     * connection. Register for broadcast of headset and Sco audio connection     * states.     */    private BluetoothProfile.ServiceListener mHeadsetProfileListener = new BluetoothProfile.ServiceListener() {        /**         * This method is never called, even when we closeProfileProxy on         * onPause. When or will it ever be called???         */        @Override        public void onServiceDisconnected(int profile) {            stopBluetooth();        }        @SuppressWarnings("synthetic-access")        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        @Override        public void onServiceConnected(int profile, BluetoothProfile proxy) {            // mBluetoothHeadset is just a headset profile,            // it does not represent a headset device.            mBluetoothHeadset = (BluetoothHeadset) proxy;            // If a headset is connected before this application starts,            // ACTION_CONNECTION_STATE_CHANGED will not be broadcast.            // So we need to check for already connected headset.            List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();            if (devices.size() > 0) {                // Only one headset can be connected at a time,                // so the connected headset is at index 0.                mConnectedHeadset = devices.get(0);                onHeadsetConnected();            }            // During the active life time of the app, a user may turn on and            // off the headset.            // So register for broadcast of connection states.            mContext.registerReceiver(mHeadsetBroadcastReceiver, new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED));            // Calling startVoiceRecognition does not result in immediate audio            // connection.            // So register for broadcast of audio connection states. This            // broadcast will            // only be sent if startVoiceRecognition returns true.            mContext.registerReceiver(mHeadsetBroadcastReceiver, new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED));        }    };    /**     * API >= 11 Handle headset and Sco audio connection states.     */    private BroadcastReceiver mHeadsetBroadcastReceiver = new BroadcastReceiver() {        @SuppressWarnings("synthetic-access")        @TargetApi(Build.VERSION_CODES.HONEYCOMB)        @Override        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            int state;            if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {                state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);                if (state == BluetoothHeadset.STATE_CONNECTED) {                    mConnectedHeadset = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);                    // Calling startVoiceRecognition always returns false here,                    // that why a count down timer is implemented to call                    // startVoiceRecognition in the onTick.                    // override this if you want to do other thing when the                    // device is connected.                    onHeadsetConnected();                } else if (state == BluetoothHeadset.STATE_DISCONNECTED) {                    mConnectedHeadset = null;                    // override this if you want to do other thing when the                    // device is disconnected.                    onHeadsetDisconnected();                }            } else // audio            {                state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {                    // override this if you want to do other thing when headset                    // audio is connected.                    onScoAudioConnected();                } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {                    mIsOnHeadsetSco = false;                    // override this if you want to do other thing when headset                    // audio is disconnected.                    onScoAudioDisconnected();                }            }        }    };}

public class BluetoothHelper extends BluetoothHeadsetUtils {    private final static String TAG = BluetoothHelper.class.getSimpleName();    Context mContext;    int mCallvol;//  int mMediaVol;    AudioManager mAudioManager;    public BluetoothHelper(Context context) {        super(context);        mContext = context;        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);        mCallvol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);//      mMediaVol = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);    }    @Override    public void onHeadsetDisconnected() {        mAudioManager.setBluetoothScoOn(false);    }    @Override    public void onHeadsetConnected() {        mAudioManager.setBluetoothScoOn(true); // 打开SCO//      mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);    }    @Override    public void onScoAudioDisconnected() {        mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, mCallvol, 0);//      mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mMediaVol, 0);    }    @Override    public void onScoAudioConnected() {        mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL), 0);        //      mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);    }}

有关蓝牙协议的部分,由于Google官方并未提供更多API支持,故了解即可。

1 0