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()) } }
蓝牙传输数据
在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端的建立步骤如下:
- 通过listenUsingRfcommWithServiceRecord创建BluetoothServerSocket对象;
- 监听网络accept;(阻塞线程)
- 处理网络socket;
- 关闭连接。
client端的建立步骤如下:
- 通过createRfcommSocketToServiceRecord创建BlueToothSocket对象;
- 建立服务器端connect;(阻塞线程)
- 处理数据;
- 关闭连接。
服务端
当触发监听时,程序进入监听状态,此时设备可看成分服务器端,用于等待其他设备绑定:
//创建一个新线程,用于处理会发生阻塞的监听网络的方法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支持,故了解即可。
- Android蓝牙开发介绍
- Android 蓝牙开发介绍
- Android蓝牙开发介绍
- 蓝牙开发平台介绍
- 蓝牙----Android的蓝牙开发
- Android 蓝牙开发-蓝牙通信
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- Android 4.2蓝牙介绍
- android 蓝牙 介绍转载
- Android 4.2蓝牙介绍
- android 蓝牙介绍
- PB如何实现动态建立菜单
- JS 隔行换色,checkbox选中换色,checkbox全选
- 在Ti的DSP程序中使用C++编程
- 驱动程序开发的步骤
- 使用jQuery修改伪属性的样式,dorado中自定义弹出框的图标
- Android蓝牙开发介绍
- Windows驱动开发之准备篇
- 【算法分析】排序算法:希尔、归并、快速、堆排序
- ThreadPool&AsyncTask
- 【leetcode】【38】Count and Say
- android BaseAdapter 自定义适配器 BaseAdapter基类抽取 二
- 用属性动画简简单单实现android导航栏特效
- Tomcat7集群共享Session 基于redis进行统一管理
- JAVA对象和类