原文地址:html
Android 蓝牙开发(一)蓝牙通讯 CSDNjava
Android 蓝牙开发(一)蓝牙通讯 简书android
开发环境:安全
jdk1.8服务器
Eclipse Luna Service Release 1 (4.4.1)异步
运行环境:socket
华为荣耀6(Android4.4)、华为p9(Android7.0)ide
实现功能:函数
Android 蓝牙开发 (开关蓝牙、搜索设备、蓝牙配对、链接、通讯、断开链接等)。ui
代码包里面,有两个部分,一个是源码,一个是V7支持包。
随着可穿戴设备的流行,研究蓝牙是必不可少的一门技术了。
总结了下蓝牙开发使用的一些东西分享一下。
蓝牙权限
首先须要AndroidManifest.xml文件中添加操做蓝牙的权限。
<uses-permissionandroid:name="Android.permission.BLUETOOTH" /> //容许程序链接到已配对的蓝牙设备。 <uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" /> //容许程序发现和配对蓝牙设备。
BluetoothAdapter
操做蓝牙主要用到的类 BluetoothAdapter类,使用时导包
import android.bluetooth.BluetoothAdapter;
源码具体位置frameworks/base/core/Java/android/bluetooth/BluetoothAdapter.java
BluetoothAdapter 表明本地设备的蓝牙适配器。该BluetoothAdapter能够执行基本的蓝牙任务,例如启
动设备发现,查询配对的设备列表,使用已知的MAC地址实例化一个BluetoothDevice类,并建立一个
BluetoothServerSocket监听来自其余设备的链接请求。
获取蓝牙适配器
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
开启蓝牙
if(!mBluetoothAdapter.isEnabled()){ //弹出对话框提示用户是后打开 Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabler, REQUEST_ENABLE); //不作提示,直接打开,不建议用下面的方法,有的手机会有问题。 // mBluetoothAdapter.enable(); }
获取本地蓝牙信息
//获取本机蓝牙名称 String name = mBluetoothAdapter.getName(); //获取本机蓝牙地址 String address = mBluetoothAdapter.getAddress(); Log.d(TAG,"bluetooth name ="+name+" address ="+address); //获取已配对蓝牙设备 Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices(); Log.d(TAG, "bonded device size ="+devices.size()); for(BluetoothDevice bonddevice:devices){ Log.d(TAG, "bonded device name ="+bonddevice.getName()+" address"+bonddevice.getAddress()); }
搜索设备
mBluetoothAdapter.startDiscovery();
中止搜索
mBluetoothAdapter.cancelDiscovery();
搜索蓝牙设备,该过程是异步的,经过下面注册广播接受者,能够监听是否搜到设备。
IntentFilter filter = new IntentFilter(); //发现设备 filter.addAction(BluetoothDevice.ACTION_FOUND); //设备链接状态改变 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //蓝牙设备状态改变 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(mBluetoothReceiver, filter);
监听扫描结果
经过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUNDE)。其中参数intent能够获取蓝牙设备BluetoothDevice。
该demo中是链接指定名称的蓝牙设备,BLUETOOTH_NAME为"Galaxy Nexus",若是扫描不到,记得改这个蓝牙名称。
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){ @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(TAG,"mBluetoothReceiver action ="+action); if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。 //获取蓝牙设备 BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(scanDevice == null || scanDevice.getName() == null) return; Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress()); //蓝牙设备名称 String name = scanDevice.getName(); if(name != null && name.equals(BLUETOOTH_NAME)){ mBluetoothAdapter.cancelDiscovery(); //取消扫描 mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting)); //链接到设备。 mBlthChatUtil.connect(scanDevice); } }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){ } } };
设置蓝牙可见性
有时候扫描不到某设备,这是由于该设备对外不可见或者距离远,须要设备该蓝牙可见,这样该才能被搜索到。
可见时间默认值为120s,最多可设置300。
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); startActivity(discoverableIntent); } }
android 蓝牙之间能够经过SDP协议创建链接进行通讯,通讯方式相似于日常使用socket。
首先建立BluetoothServerSocket ,BluetoothAdapter中提供了两种建立BluetoothServerSocket 方式,以下图所示为建立安全的RFCOMM Bluetooth socket,该链接是安全的须要进行配对。而经过listenUsingInsecureRfcommWithServiceRecord建立的RFCOMM Bluetooth socket是不安全的,链接时不须要进行配对。
其中的uuid须要服务器端和客户端进行统一。
private class AcceptThread extends Thread { // 本地服务器套接字 private final BluetoothServerSocket mServerSocket; public AcceptThread() { BluetoothServerSocket tmp = null; // 建立一个新的侦听服务器套接字 try { tmp = mAdapter.listenUsingRfcommWithServiceRecord( SERVICE_NAME, SERVICE_UUID); //tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID); } catch (IOException e) { Log.e(TAG, "listen() failed", e); } mServerSocket = tmp; } public void run() { BluetoothSocket socket = null; // 循环,直到链接成功 while (mState != STATE_CONNECTED) { try { // 这是一个阻塞调用 返回成功的链接 // mServerSocket.close()在另外一个线程中调用,能够停止该阻塞 socket = mServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "accept() failed", e); break; } // 若是链接被接受 if (socket != null) { synchronized (BluetoothChatUtil.this) { switch (mState) { case STATE_LISTEN: case STATE_CONNECTING: // 正常状况。启动ConnectedThread。 connected(socket, socket.getRemoteDevice()); break; case STATE_NONE: case STATE_CONNECTED: // 没有准备或已链接。新链接终止。 try { socket.close(); } catch (IOException e) { Log.e(TAG, "Could not close unwanted socket", e); } break; } } } } if (D) Log.i(TAG, "END mAcceptThread"); } public void cancel() { if (D) Log.d(TAG, "cancel " + this); try { mServerSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of server failed", e); } } }
mServerSocket经过accept()等待客户端的链接(阻塞),直到链接成功或失败。
客户端主要用来建立RFCOMM socket,并链接服务端。
先扫描周围的蓝牙设备,若是扫描到指定设备则进行链接。mBlthChatUtil.connect(scanDevice)链接到设备,
链接过程主要在ConnectThread线程中进行,先建立socket,方式有两种,
以下代码中是安全的(createRfcommSocketToServiceRecord)。另外一种不安全链接对应的函数是createInsecureRfcommSocketToServiceRecord。
private class ConnectThread extends Thread { private BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { mmDevice = device; BluetoothSocket tmp = null; // 获得一个bluetoothsocket try { mmSocket = device.createRfcommSocketToServiceRecord (SERVICE_UUID); } catch (IOException e) { Log.e(TAG, "create() failed", e); mmSocket = null; } } public void run() { Log.i(TAG, "BEGIN mConnectThread"); try { // socket 链接,该调用会阻塞,直到链接成功或失败 mmSocket.connect(); } catch (IOException e) { connectionFailed(); try {//关闭这个socket mmSocket.close(); } catch (IOException e2) { e2.printStackTrace(); } return; } // 启动链接线程 connected(mmSocket, mmDevice); } public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "close() of connect socket failed", e); } } }
接着客户端socket主动链接服务端。链接过程当中会自动进行配对,须要双方赞成才能够链接成功。
客户端与服务端链接成功后都会调用connected(mmSocket, mmDevice),建立一个ConnectedThread线程()。
该线程主要用来接收和发送数据。客户端和服务端处理方式同样。该线程经过socket得到输入输出流。
private InputStream mmInStream = socket.getInputStream();
private OutputStream mmOutStream =socket.getOutputStream();
发送数据
public void write(byte[] buffer) { try { mmOutStream.write(buffer); // 分享发送的信息到Activity mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer) .sendToTarget(); } catch (IOException e) { Log.e(TAG, "Exception during write", e); } }
接收数据
线程循环进行接收数据。
public void run() { // 监听输入流 while (true) { try { byte[] buffer = new byte[1024]; // 读取输入流 int bytes = mmInStream.read(buffer); // 发送得到的字节的ui activity Message msg = mHandler.obtainMessage(MESSAGE_READ); Bundle bundle = new Bundle(); bundle.putByteArray(READ_MSG, buffer); msg.setData(bundle); mHandler.sendMessage(msg); } catch (IOException e) { Log.e(TAG, "disconnected", e); connectionLost(); break; } } }
一、运行,右键项目:Run as -》Android Application (备注:Eclipse须要配置Android开发环境)
二、运行效果以下:
客户端
服务端