android蓝牙socket
‘壹’ Android蓝牙开发(二)经典蓝牙消息传输实现
上篇文章中,我们主要介绍了蓝牙模块,传统/经典蓝牙模块BT和低功耗蓝牙BLE及其相关的API,不熟悉的可以查看 Android蓝牙开发(一)蓝牙模块及核心API 进行了解。
本篇主要记录用到的经典蓝牙开发流程及连接通讯。
蓝牙连接前,给与相关系统权限:
安卓6.0以上系统要动态请求及获取开启GPS内容:
蓝牙核心对象获取,若获取对象为null则说明设备不支持蓝牙:
判断蓝牙是否开启,没有则开启:
蓝牙扫描:
取消扫描:
蓝牙监听广播,监听蓝牙开关,发现设备,扫描结束等状态,定义状态回调接口,进行对应操作,例如:监听到蓝牙开启后,进行设备扫描;发现设备后进行连接等。
客户端,与服务端建立长连接,进行通讯:
服务端监听客户端发起的连接,进行接收及通讯:
客户端连接及服务端监听基类,用于客户端和服务端之前Socket消息通讯,进行消息或文件的发送、接收,进行通讯关闭操作等:
我这里只是简单记录了项目中用到的蓝牙通讯,两个设备之间不通过配对进行连接、通讯。
相关详细内容及使用请查看Github项目: https://github.com/MickJson/BluetoothCS
蓝牙配对操作及其它内容,可以详细查看我下面的参考资料,写的十分详细,比如设备通过MAC地址,可以通过BluetoothAdapter获取设备,再通过客户端connect方法去进行连接等。
连接中遇到问题:read failed, socket might closed or timeout, read ret: -1。
通过改UUID,反射等方法都还是会出现错误。连接时,要确保服务端及客户端都处于完全断开状态,否则连接就会出现以上问题,但偶尔还是会有问题,期待有什么好的方法可留言告诉我。
参考资料:
Android-经典蓝牙(BT)-建立长连接传输短消息和文件
Android蓝牙开发—经典蓝牙详细开发流程
欢迎点赞/评论,你们的赞同和鼓励是我写作的最大动力!
‘贰’ android开发SPP经典蓝牙
Android 开发SPP经典蓝牙。
1、传统蓝牙采用的是SPP(Serial Port Profile)协议进行数据传输。
2、SPP的UUID:00001101-0000-1000-8000-00805F9B34FB
3、手机一般以客户端的角色主动连接SPP协议设备
概念:
BluetoothAdapter:
本地蓝牙适配器,是所有蓝牙交互的入口,表示蓝牙设备自身的一个蓝牙适配器,整个系统只有一个蓝牙适配器,通过他可以发现其他蓝牙设备,查询绑定(配对)设备列表,使用MAC地址实例化BluetoothDevice以及创建BluetoothServerSocket用来侦听来自其他设备的通信。
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//获取默认的蓝牙Adapter
BluetoothDevice:
远程的蓝牙设备。
private static BluetoothDevice myDevice;
myDevice = myBluetoothAdapter.getRemoteDevice(BDAddr);//获取远程设备,通过蓝牙的MAC地址来获取一个远程对象
两种连接方式
BluetoothSocket
客户端:调用BluetoothDevice的()可以获取该对象;调用connect()方法可以建立连接。
private static BluetoothSocket mySocket = null;
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Method m = myDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});//由BluetoothDevice衍生出BluetoothSocket, createRfcommSocket来选择连接的服务和协议
mySocket = (BluetoothSocket) m.invoke(myDevice, 1);
BluetoothServerSocket:
服务端:通过BluetoothServerSocket对象可以创建BluetoothSocket对象,调用BluetoothServerSocket的accept()的方法可以得到改对象。
开发流程:
1:声明权限:
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
2:启动和关闭蓝牙
获取蓝牙适配器,使用close()接口可以关闭蓝牙适配器
myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//获取默认的蓝牙Adapter
启动蓝牙
if (!blueadapter.isEnabled())
//判断本机蓝牙是否打开
{//如果没打开,则打开蓝牙
blueadapter.enable();
}
3.使用BlueAdatper搜索
使用bluetoothAdapter搜索设备,bluetoothAdapter.startDiscovery()在搜索过程中,系统会发出三个广播信息:
ACTION_DISCOVERY_START:开始搜索
ACTION_DISCOVERY_FINISHED:搜索结束
ACTION_FOUND:找到设备
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();//如果蓝牙设备未连接则取消搜索
}
bluetoothAdapter.startDiscovery();
}
4:(1)通过注册广播获取搜索到的设备。
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//找到设备广播
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜索完成广播
registerReceiver(receiver, intentFilter);//注册广播接收器
// receiver
private final BroadcastReceiver receiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// find a device
BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
//未配对设备
newDeviceArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}else {
//已经配对过的设备
TextView tvPaired = (TextView)findViewById(R.id.tv_paired);
tvPaired.setVisibility(View.VISIBLE);
lvPairedDevices.setVisibility(View.VISIBLE);
pairedDeviceArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
Log.i(TAG,"name:" + device.getName() + " address"+ device.getAddress());
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action){
// search finish
Log.i(TAG, "search finish!");
}
}
};
(2),直接得到当前的蓝牙设备后,就可用通过遍历pairedDevices ,得到当前手机已经配对过的蓝牙设备。
Set<BluetoothDevice> pairedDevices = myBluetoothAdapter.getBondedDevices();//获取当前蓝牙设备
if (pairedDevices.size() <= 0) return false;
for (BluetoothDevice device : pairedDevices) {
Map<String, String> map = new HashMap<String, String>();
map.put("DeviceName", device.getName());
map.put("BDAddress", device.getAddress());
list.add(map);
5.建立连接
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
Method m = myDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});//由BluetoothDevice衍生出BluetoothSocket, createRfcommSocket来选择连接的服务和协议
mySocket = (BluetoothSocket) m.invoke(myDevice, 1);
mySocket.connect();//使用BluetoothSocket来连接设备
6.把得到的蓝牙设备给通过点击ListView选择设备。
listView.setOnItemClickListener(new ListView.OnItemClickListener() {
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
SelectedBDAddress = list.get(arg2).get("BDAddress");
if (((ListView) arg0).getTag() != null) {
((View) ((ListView) arg0).getTag()).setBackgroundDrawable(null);
}
((ListView) arg0).setTag(arg1);
arg1.setBackgroundColor(Color.BLUE);
myDevice = myBluetoothAdapter.getRemoteDevice(SelectedBDAddress);
}
});
7.客户端发送数据
当两个设备成功连接之后,双方都会有一个BluetoothSocket对象,这时,就可以在设备之间传送数据了。
1.使用getOutputStream()方法来获取输出流来处理传输。
2.调用write()。
os = socket.getOutputStream();//获取输出流
if (os != null) {//判断输出流是否为空
os.write(message.getBytes("UTF-8"));
}
os.flush();//将输出流的数据强制提交
os.close();//关闭输出流
}
将输出流中的数据提交后,要记得关闭输出流,否则,可能会造成只能发送一次数据。
8.服务端接收数据
1.使用getInputStream()方法来获取输入流来处理传输。
2.调用read()。
InputStream im=null;
im=bluetoothSocket.getInputStream();
byte buf[] = new byte[1024];
if (is != null) {
is.read(buf, 0, buf.length);//读取发来的数据
String message = new String(buf);//把发来的数据转化为String类型
BuletoothMainActivity.UpdateRevMsg(message);//更新信息在显示文本框
is.close();//关闭输入流
使用服务端接收数据时,要先从客户端向服务端发起连接,只有接收到连接请求之后,才会返回一个BluetoothSocket对象。有BluetoothSocket对象才能获取到输入流。
‘叁’ Android - Socket简单使用
ServerSocket类提供如下构造器:
当ServerSocket使用完毕,应使用 close() 方法来关闭此ServerSocket。通常情况下,服务器不应该只接收一个客户端请求,而应该不断接收来自客户端的请求,所以程序可以通过循环,不断调用ServerSocket的accept方法:
Socket 常用构造器
注:上面两个构造器指定远程主机时既可以使用InetAddress来指定,也可以直接使用String对象来指定远程IP。本地主机只有一个IP地址时,使用第一个方法更简单。
在与服务器进行通讯的时候,无法判断远程的服务器是否断开连接。如果使用 OutputStream 发送数据则会影响正常的数据发送(无法区分)。所以就引入了一个心跳机制。
心跳机制实现,使用 Socket.sendUrgentData() 方法发送一个字节流数据(紧急数据)。可以通过判断服务端的 OOBINLINE 属性是否打开,来确定是否断开连接;
setSoTimeout()理解 :设置超时时间;例如:设置为2s,如果阻塞的时间>2s ,那么就会报错。
‘肆’ Android-蓝牙传输
通过蓝牙传输数据与Socket类似。在网络中使用Socket和ServerSocket控制客户端和服务端的数据读写。而蓝牙通讯也由客户端和服务端Socket来完成。蓝牙客户端Socket是BluetoothSocket,蓝牙服务端Socket是BluetoothServerSocket。这两个类都在android.bluetooth包中。
如果打算建议两个蓝牙设备之间的连接,则必须实现服务器端与客户端的机制。当两个设备在同一个RFCOMM channel下分别拥有一个连接的BluetoothSocket,这两个设备才可以说是建立了连接。
服务器设备与客户端设备获取BluetoothSocket的途径是不同的。服务器设备是通过accepted一个incoming connection来获取的,而客户端设备则是通过打开一个到服务器的RFCOMMchannel来获取的。
通过调用BluetoothAdapter的(String, UUID) 方法来获取
BluetoothServerSocket(UUID用于客户端与服务器端之间的配对)调用BluetoothServerSocket的 accept() 方法监听连接请求,如果收到请求,则返回一个BluetoothSocket实例。
如果不想在accept其他的连接,则调用BluetoothServerSocket的 close() 方法释放资源(调用该方法后,之前获得的BluetoothSocket实例并没有close。但由于RFCOMM一个时刻只允许在一条channel中有一个连接,则一般在accept一个连接后,便close掉BluetoothServerSocket)
通过搜索得到服务器端的BluetoothService,调用BluetoothService的(String, UUID)方法获取BluetoothSocket(该UUID应该同于服务器端的UUID)。
调用BluetoothSocket的 connect() 方法(该方法为block方法),如果UUID同服务器端的UUID匹配,并且连接被服务器端accept,则 connect() 方法返回。
‘伍’ android蓝牙通讯Socket.connect()方法调用不成功。为什么
UUID值出现错误。
看一下android有关bluetooth的API,用于普通蓝牙适配器和android手机蓝牙模块连接的,而且这个UUID的值必须是00001101-0000-1000-8000-00805F9B34FB。
这个是android的API上面说明的.connect().在连接的时候,android手机作client(主动和电脑建立连接),如果电脑作为server(一直监听是否有服务连接),则需要在手机端调用这样一行代码.两边的UUID必须是一样的,这是一个服务的唯一标识。