android 蓝牙数据传输速率SPP使用outputstream.write速率问题

清除回答草稿
&&&您需要以后才能回答,未注册用户请先。Android 蓝牙及蓝牙通讯讲解
一、蓝牙介绍: (一)、Bluetooth的由来及现状
蓝牙一词源于公元十世纪丹麦国王HaraldBlatand名字中的Blatand。Blatand的英文之意就是Blue tooth。这是因为这位让丹麦人引以为傲的国王酷爱吃蓝莓以至于牙龈都被染成蓝色。由于Blatand统一了丹麦和挪威,所以,作为无线通信技术的一种,蓝牙技术之所以取名Bluetooth可谓志向远大。
不过,在以Android为代表的智能机出现以前,蓝牙在早期智能机甚至功能机中一直扮演着“鸡肋”的角色。那么,随着无线通信技术的快速发展以及Android的普及,蓝牙技术在我们生活中的应用也越来越多,包括蓝牙耳机和鼠标,及蓝牙局域网应用(聊天、游戏等)。
(二)、蓝牙规范介绍
Core Specification(核心规范)
作用:用于规定蓝牙设备必须实现的通用功能和协议层次。它由软件和硬件模块组成,两个模块之间的信息和数据通过主机控制接口(HCI)的解释才能进行传递
核心规范是蓝牙协议家族的基础,自蓝牙技术联盟(Bluetooth SIG,Special Interest Group)在1999年颁布蓝牙核心规范1.0版本以来,到目前为止蓝牙SIG一共发布了七个重要版本。每一个版本都促使蓝牙技术朝着更快、更安全、更省电的方向发展。
二、蓝牙的用法 (一)、蓝牙API
Android所有关于蓝牙开发的类都在android.bluetooth包下,只有8个类:
BluetoothAdapter 本地蓝牙适配器 BluetoothClass 蓝牙类(主要包括服务和设备) BluetoothClass.Device 蓝牙设备类 BluetoothClass.Device.Major 蓝牙设备管理 BluetoothClass.Service 蓝牙服务类 BluetoothDevice 蓝牙设备(远程蓝牙设备) BluetoothServiceSocket 监听蓝牙连接的类 BluetoothSocket 蓝牙连接类 1、BluetoothAdapter :
表示本地的蓝牙适配器 (蓝牙射频)。BluetoothAdapter 是为所有蓝牙交互的入口点。它可以发现其他蓝牙设备、 查询绑定 (配对) 设备的列表、 实例化已知的 MAC 地址的BluetoothDevice(蓝牙设备) 和创建 BluetoothServerSocket 用于侦听来自其他设备的通信。直到我们建立bluetoothSocket连接之前,都要不断操作它 。BluetoothAdapter里的方法很多,常用的有以下几个:
cancelDiscovery() //根据字面意思,是取消发现,也就是说当我们正在搜索设备的时候调用这个方法将不再继续搜索;
disable() //关闭蓝牙;
enable() //打开蓝牙;这个方法打开蓝牙不会弹出提示,更多的时候我们需要问下用户是否打开,以下两行代码同样是打开蓝牙,不过会提示用户:
Intemtenabler= newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enabler,reCode); //同startActivity(enabler);
getAddress() //获取本地蓝牙地址
getDefaultAdapter() //获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdapter
getName() //获取本地蓝牙名称
getRemoteDevice(String address) //根据蓝牙地址获取远程蓝牙设备
getState() //获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)
isDiscovering() //判断当前是否正在查找设备,是返回true
isEnabled() //判断蓝牙是否打开,已打开返回true,否则,返回false
listenUsingRfcommWithServiceRecord(String name,UUIDuuid) //根据名称,UUID创建并返回BluetoothServerSocket对象,这是创建BluetoothSocket服务器端的第一步 。第一个参数表示蓝牙服务的名称,可以是任意字符串,第二个参数是UUID。
startDiscovery() //开始搜索,这是搜索的第一步2、BluetoothDevice
表示远程蓝牙设备。使用此类并通过BluetoothSocket类可以请求连接远程设备,或查询这台设备的信息如其名称、 地址、 类和绑定状态。
createRfcommSocketToServiceRecord(UUIDuuid)
根据UUID创建并返回一个BluetoothSocket,这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket。这个类其他的方法,如getAddress()、getName(),同BluetoothAdapter。
【备注:】蓝牙—RFCOMM协议
串口仿真协议(RFCOMM),RFCOMM是一个简单的协议,其中针对9针RS-232串口仿真附加了部分条款.可支持在两个蓝牙设备之间同时保持高达60路的通信连接.RFCOMM的目的是针对如何在两个不同设备上的应用之间保证一条完整的通信路径。
3、BluetoothServerSocket
表示打开服务器套接字侦听传入的请求 (类似于 TCP ServerSocket)。为了连接两台 Android 设备,一台设备必须用此类打开一个服务器套接字。当远程蓝牙设备向此设备发出连接请求时,而且当连接被接收时,BluetoothServerSocket 将返回连接的 BluetoothSocket。这个类有三个方法。
accept()accept(inttimeout): //两者的区别在于后者指定了过时时间,需要注意的是,执行这两个方法的时候,直到接收到了客户端的请求(或是过期之后),都会阻塞线程,应该放在新线程里运行! 还需要注意,这两个方法都返回一个BluetoothSocket,最后的连接也是服务器端与客户端这两个BluetoothSocket的连接。
close():关闭 4、BluetoothSocket
跟BluetoothServerSocket相对,是客户端。表示一个蓝牙套接字 (类似于 TCP Socket) 的接口。这是一个允许应用程序与另一台蓝牙设备通过InputStream和OutputStream来交换数据的连接点。其一共5个方法,一般都会用到。
close():关闭connect():连接getInptuStream():获取输入流getOutputStream():获取输出流getRemoteDevice():获取远程设备,这里指的是获取bluetoothSocket指定连接的那个远程蓝牙设备 5、BluetoothClass
描述的一般特征和蓝牙设备的功能。这是一整套只读的属性用于定义设备的主要和次要设备类和它的服务。然而,这并不是支持所有蓝牙配置文件和服务的设备,但很适用于获取设备类型
6、BluetoothProfile
表示一个蓝牙配置文件。蓝牙配置文件是基于蓝牙通信设备之间的无线接口规范。如免提规范(Hands-Free profile)
7、BluetoothHeadset
蓝牙耳机与手机一起使用配置文件 ,这包括蓝牙耳机和免提(v1.5) 的配置文件
8、BluetoothA2dp
定义了如何高质量的音频可以进行流式处理从一个设备到另一个通过蓝牙连接。”A2DP”代表先进音频分配协议
9、BluetoothHealth
表示控制蓝牙服务健康设备协议
10、BluetoothHealthCallback
BluetoothHealthCallback 一个抽象类,您使用来实现 BluetoothHealth 回调,你必须扩展此类并实现回调方法以接收有关更改的更新应用程序的注册和蓝牙通道状态。BluetoothHealthAppConfiguration 表示一个蓝牙健康第三方应用程序注册与远程蓝牙健康设备进行通信的应用程
11、BluetoothHealthAppConfiguration
表示一个蓝牙健康第三方应用程序注册与远程蓝牙健康设备进行通信的应用程序配置
12、BluetoothProfile.ServiceListener
通知 BluetoothProfile IPC 客户端界面时已被连接或断开服务 (即运行一个特定的配置文件内部服务)
(二)、使用蓝牙的权限 &uses-permissionandroid:name="android.permission.BLUETOOTH"/&
&uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/&(三)、 UUID(universal unique identifier , 全局唯一标识符)
格式如下:UUID格式一般是”xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,可到http://www.uuidgenerator.com 申请。UUID分为5段,是一个8-4-4-4-12的字符串,这个字符串要求永不重复。
String uuid = java.util.UUID.randomUUID().toString();
一般在创建Socket时需要UUID作为端口的唯一性,如果两台Android设备互联,则没有什么特殊的,如果让非Android的蓝牙设备连接Android蓝牙设备,则UUID必须使用某个固定保留的UUID
Android中创建UUID:
UUID uuid = UUID.fromString("0-805F9B34FB")
常用固定的UUID
蓝牙串口服务(SPP)
SerialPortServiceClass_UUID = '{0-805F9B34FB}' LANAccessUsingPPPServiceClass_UUID = '{0-805F9B34FB}'拨号网络服务 DialupNetworkingServiceClass_UUID = '{0-805F9B34FB}'信息同步服务 IrMCSyncServiceClass_UUID = '{0-805F9B34FB}' SDP_OBEXObjectPushServiceClass_UUID = '{0-805F9B34FB}'文件传输服务 OBEXFileTransferServiceClass_UUID = '{0-805F9B34FB}' IrMCSyncCommandServiceClass_UUID = '{0-805F9B34FB}' SDP_HeadsetServiceClass_UUID = '{0-805F9B34FB}' CordlessTelephonyServiceClass_UUID = '{0-805F9B34FB}' SDP_AudioSourceServiceClass_UUID = '{00-805F9B34FB}' SDP_AudioSinkServiceClass_UUID = '{00-805F9B34FB}' SDP_AVRemoteControlTargetServiceClass_UUID = '{00-805F9B34FB}' SDP_AdvancedAudioDistributionServiceClass_UUID = '{00-805F9B34FB}' SDP_AVRemoteControlServiceClass_UUID = '{00-805F9B34FB}' VideoConferencingServiceClass_UUID = '{00-805F9B34FB}' IntercomServiceClass_UUID = '{0-805F9B34FB}'蓝牙传真服务 FaxServiceClass_UUID = '{0-805F9B34FB}' HeadsetAudioGatewayServiceClass_UUID = '{0-805F9B34FB}' WAPServiceClass_UUID = '{0-805F9B34FB}' WAPClientServiceClass_UUID = '{0-805F9B34FB}'蓝牙打印服务 HCRPrintServiceClass_UUID = '{0-805F9B34FB}' HCRScanServiceClass_UUID = '{0-805F9B34FB}' CommonISDNAccessServiceClass_UUID = '{0-805F9B34FB}' VideoConferencingGWServiceClass_UUID = '{0-805F9B34FB}' UDIMTServiceClass_UUID = '{00-805F9B34FB}' UDITAServiceClass_UUID = '{00-805F9B34FB}' AudioVideoServiceClass_UUID = '{00-805F9B34FB}' SIMAccessServiceClass_UUID = '{00-805F9B34FB}' PnPInformationServiceClass_UUID = '{0-805F9B34FB}' GenericNetworkingServiceClass_UUID = '{0-805F9B34FB}' GenericFileTransferServiceClass_UUID = '{0-805F9B34FB}' GenericAudioServiceClass_UUID = '{0-805F9B34FB}' GenericTelephonyServiceClass_UUID = '{0-805F9B34FB}'个人局域网服务 PANUServiceClass_UUID = '{0-805F9B34FB}' NAPServiceClass_UUID = '{0-805F9B34FB}' GNServiceClass_UUID = '{0-805F9B34FB}' DirectPrintingServiceClass_UUID = '{0-805F9B34FB}' ReferencePrintingServiceClass_UUID = '{0-805F9B34FB}' ImagingServiceClass_UUID = '{00-805F9B34FB}' ImagingResponderServiceClass_UUID = '{00-805F9B34FB}' ImagingAutomaticArchiveServiceClass_UUID = '{00-805F9B34FB}' ImagingReferenceObjectsServiceClass_UUID = '{00-805F9B34FB}' SDP_HandsfreeServiceClass_UUID = '{00-805F9B34FB}' HandsfreeAudioGatewayServiceClass_UUID = '{00-805F9B34FB}' DirectPrintingReferenceObjectsServiceClass_UUID = '{0-805F9B34FB}' ReflectedUIServiceClass_UUID = '{0-805F9B34FB}' BasicPringingServiceClass_UUID = '{0-805F9B34FB}' PrintingStatusServiceClass_UUID = '{0-805F9B34FB}'人机输入服务 HumanInterfaceDeviceServiceClass_UUID = '{0-805F9B34FB}' HardcopyCableReplacementServiceClass_UUID = '{0-805F9B34FB}' (四)、使用蓝牙的步骤:【五步曲】 1、获取本地蓝牙适配器
BluetoothAdapter mAdapter= BluetoothAdapter.getDefaultAdapter();
2、打开蓝牙 //弹出对话框提示用户是否打开Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enabler, REQUEST_ENABLE);//不做提示,强行打开// mAdapter.enable();}补充一下,使设备能够被搜索Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);startActivityForResult(enabler,REQUEST_DISCOVERABLE); 3、搜索设备 1)mAdapter.startDiscovery()是第一步,可是你会发现没有返回的蓝牙设备,怎么知道查找到了呢?
2)定义BroadcastReceiver,代码如下BroadcastReceiver mReceiver = newBroadcastReceiver() {
publicvoidonReceive(Context context, Intent intent){String action = intent.getAction();
//找到设备
if(BluetoothDevice.ACTION_FOUND.equals(action)) {BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if(device.getBondState() != BluetoothDevice.BOND_BONDED) {Log.v(TAG, "find device:"+ device.getName()+ device.getAddress());}
//搜索完成
} elseif(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {setTitle( "搜索完成");
if(mNewDevicesAdapter.getCount() == 0) {Log.v(TAG, "find over");}} //执行更新列表的代码
这样,没查找到新设备或是搜索完成,相应的操作都在上段代码的两个if里执行了,不过前提是你要先注册
BroadcastReceiver,具体代码如下,该段代码,一般写在onCreate()里.
IntentFilter filter = newIntentFilter(BluetoothDevice.ACTION_FOUND);registerReceiver(mReceiver, filter);filter = newIntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);registerReceiver(mReceiver, filter); 4、建立连接
首先Android sdk(2.0以上版本)支持的蓝牙连接是通过BluetoothSocket建立连接,服务器端(BluetoothServerSocket)和客户端(BluetoothSocket)需指定同样的UUID,才能建立连接,因为建立连接的方法会阻塞线程,所以服务器端和客户端都应启动新线程连接
1)服务器端:BluetoothServerSocket serverSocket = mAdapter. listenUsingRfcommWithServiceRecord(serverSocketName,UUID);serverSocket.accept(); 2)客户端:还记得刚才在BroadcastReceiver获取了BLuetoothDevice么?BluetoothSocket clienSocket=dcvice. createRfcommSocketToServiceRecord(UUID);clienSocket.connect(); 5、数据传递
通过以上操作,就已经建立的BluetoothSocket连接了,数据传递无非是通过流的形式
1)获取流inputStream = socket.getInputStream();outputStream = socket.getOutputStream(); 2)写出、读入BluetoothServerSocketBluetoothServerSocket BluetoothAdapter.listenUsingRfcommWithServiceRecord(String name, UUID)通过此方法监听BluetoothSocket的连接BluetoothServerSocket.accept() 开始接收BluetoothSocketBluetoothServerSocket.close() 关闭服务BluetoothSocketBluetoothSocket BluetoothDevice.createInsecureRfcommSocketToServiceRecord(UUID uuid)通过此方法向指定的BluetoothDevice发送Socket连接UUID: - 0000- 1000- 8000- 00805F9B34FBconnect()尝试连接booleanisConnected()是否已连接,要求最低sdk 14+BluetoothDevice getRemoteDevice()获取当前正在或已连接的设备InputStream getInputStream()获取输入流OutputStream getOutputStream()获取输出流在读取数据时用数据流DataInputStream/DataOutputStream基于Socket技术实现蓝牙聊天蓝牙的配对查找已配对的蓝牙设备Set&BluetoothDevice& BluetoothAdapter.getBondedDevices()查找附件的蓝牙设备BluetoothAdapter.startDiscovery())BluetoothAdapter.isDiscovering()是否正在查找BluetoothAdapter.cancelDiscovery()取消查找注册广播接收器接收查到的设备信息BluetoothAdapter.ACTION_DISCOVERY_STARTED 开始查找BluetoothDevice.ACTION_FOUND 查找到蓝牙设备BluetoothDevice.EXTRA_DEVICE 获取查找到的设备信息,此数据为ParcelableExtra,需要intent.getParcelableExtra()获取到BluetoothDevice对象BluetoothAdapter.ACTION_DISCOVERY_FINISHED 查找结束判断配对状态intBluetoothDevice.getBondState()获取设备的配对状态BluetoothDevice.BOND_BONDED 已配对BluetoothDevice.BOND_BONDING 正在配对BluetoothDevice.BOND_NONE 未配对与指定未配对的设备配对配对:通过反射获取BluetoothDevice的booleancreateBond()方法,并执行取消配对:通过反射获取BluetoothDevice的booleanremoveBond()方法,并执行三、蓝牙通信案例【实现服务器端与多个客户端通过蓝牙聊天】 (一)、服务器端程序制作步骤:
1、服务器线程
构造方法,创建BluetoothServerSocket对象;
通过bluetoothAdapter 的listenUsingRfcommWithServiceRecord()方法;
重写run()方法。死循环中,BluetoothServerSocket对象等待接收客户端的请求 ,如果建立连接,则将客户端线程放入客户端线程集合,并启动客户端线程。
BluetoothSocket clientSocket = serverSocket.accept();ClientThread clientThread = newClientThread(clientSocket);clientsList.add(clientThread);clientThread.start();
2、客户端线程
构造方法,获取客户端设备名称,并获取客户端套接字对象的输入、输出流对象;
clientName = clientSocket.getRemoteDevice().getName();is = newDataInputStream(clientSocket.getInputStream());os = newDataOutputStream(clientSocket.getOutputStream());
重写run()方法。死循环中,读取客户端发送过来的数据,追加到聊天记录中。启动发送线程,向所有客户端发送聊天数据。
String info = is.readUTF();appendContent(info); newSendThread(info).start(); // 向所有客户端发送此数据
自定义sendInfo()消息发送方法。os.writeUTF(info);
3、发送消息线程
构造方法,初始化要发送的信息数据;
this.info =
重写run()方法。循环客户端线程集合,获取当前已连接的客户端,并逐一向其发送数据 。
clientThread.sendInfo(info);
(二)、客户端程序制作步骤:
1、客户端线程
构造方法,初始化BluetoothDevice,连接选择的设备,建立连接,并获取客户端套接字对象的输入、输出流对象;
this.bluetoothDevice =clientSocket = bluetoothDevice .createRfcommSocketToServiceRecord(MainActivity.MY_UUID); clientSocket.connect();is = newDataInputStream(clientSocket.getInputStream());os = newDataOutputStream(clientSocket.getOutputStream());
重写run()方法。死循环中,读取客户端输入流数据,追加到聊天记录中。
String info = is.readUTF();appendContent(info);
自定义sendInfo()消息发送方法。
os.writeUTF(info);
2、发送消息线程
构造方法,初始化要发送的信息数据;
this.info =
重写run()方法。调用连接线程的sendInfo()方法,向服务器端发送数据 。
clientThread.sendInfo(info);
大家都在看
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
今日搜狐热点1.蓝牙串行端口基于SPP协议(Serial Port Profile),能在蓝牙设备之间创建串口进行数据传输&2.SPP的UUID:0-805F9B34FB&3.Android手机一般以客户端的角色主动连接SPP协议设备
1.检测蓝牙状态&若蓝牙未打开,则打开蓝牙~
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
protected void onResume() {
super.onResume();
if (!bluetoothAdapter.isEnabled()) {
// open blueTooth
Intent enableBtIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
2.注册设备搜索广播信息&使用registerReceiver注册broadcastReceiver来获取搜索设备等消息
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(){
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());
//已经配对过的设备
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!");
3.使用BlueAdatper搜索&使用bluetoothAdapter搜索设备,bluetoothAdapter.startDiscovery()在搜索过程中,系统会发出三个广播信息:&ACTION_DISCOVERY_START:开始搜索&ACTION_DISCOVERY_FINISHED:搜索结束&ACTION_FOUND:找到设备
public void onClick(View v) {
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
bluetoothAdapter.startDiscovery();
4.获取搜索到的蓝牙设备信息&在BroadcastReceiver的onReceive()里取得搜索到的蓝牙设备信息(如名称,MAC,RSSI)&5.通过蓝牙设备的MAC地址来建立一个BluetoothDevice对象:
BluetoothDevice romoteDevice = bluetoothAdapter.getRemoteDevice(mDeviceAddress);
6.由BluetoothDevice衍生BluetoothSocket&通过BluetoothSocket的createRfcommSocketToServiceRecord()方法来选择连接的协议/服务,这里用的是SPP(UUID:0-805F9B34FB)
bluetoothSocket = romoteDevice.createRfcommSocketToServiceRecord(UUID.fromString(SPP_UUID));
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "socket init failed", Toast.LENGTH_SHORT).show();
7.使用BluetoothSocket来连接、读写蓝牙设备&读写可以归到一个独立线程去实现~
bluetoothSocket.connect();
Toast.makeText(this, "connect success", Toast.LENGTH_SHORT).show();
} catch (IOException e2) {
e2.printStackTrace();
Toast.makeText(this, "connect failed", Toast.LENGTH_SHORT).show();
bluetoothSocket.close();
bluetoothSocket = null;
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "socket close failed", Toast.LENGTH_SHORT).show();
inputStream = bluetoothSocket.getInputStream();
} catch (IOException e2) {
e2.printStackTrace();
Toast.makeText(this, "get inputstream failed", Toast.LENGTH_SHORT).show();
OutputStream os = bluetoothSocket.getOutputStream();
byte[] osBytes = etInput.getText().toString().getBytes();
for (int i = 0; i & osBytes. i++) {
if (osBytes[i] == 0x0a)
byte[] osBytesNew = new byte[osBytes.length+n];
for (int i = 0; i & osBytesNew. i++) {
//mobile "\n"is 0a,modify 0d 0a then send
if (osBytesNew[i] == 0x0a) {
osBytesNew[n] = 0x0d;
osBytesNew[n] = 0x0a;
osBytesNew[n] = osBytes[i];
os.write(osBytesNew);
} catch (Exception e) {
e.printStackTrace();
阅读(...) 评论()关于Android蓝牙串口通信那点破事
Android蓝牙串口通讯
闲着无聊玩起了Android蓝牙模块与单片机蓝牙模块的通信,简单思路就是要手机通过蓝牙发送控制指令给单片机,并作简单的控制应用。单片机的蓝牙模块连接与程序暂且略过,此文主要描述Android手机蓝牙客户端遇到的那点破事。进入正题:
连接蓝牙设备——蓝牙客户端:
Android手机一般以客户端的角色主动连接SPP协议设备(接上蓝牙模块的数字传感器),客户端连接流程是:
1.使用registerReceiver注册BroadcastReceiver来获取蓝牙状态、搜索设备等消息;
private BroadcastReceiver searchDevices = new BroadcastReceiver() {&
public void onReceive(Context context, Intent intent) {
&&&&&&&&&&
String action = intent.getAction();
&&&&&&&&&&
Bundle b = intent.getExtras();
&&&&&&&&&&
Object[] lstName = b.keySet().toArray();&
&&&&&&&&&&
显示所有收到的消息及其细节
&&&&&&&&&&
for (int i =
0; i & lstName.length;
&&&&&&&&&&&&&
String keyName = lstName[i].toString();
&&&&&&&&&&&&&
Log.e(keyName,
String.valueOf(b.get(keyName)));
&&&&&&&&&&
&&&&&&&&&&
//搜索设备时,取得设备的MAC地址
&&&&&&&&&&
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
&&&&&&&&&&&&&
BluetoothDevice device = intent
&&&&&&&&&&&&&&&&&&&&
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
&&&&&&&&&&&&&
String str= device.getName() + "|" +
device.getAddress();
&&&&&&&&&&&&&
&&&&&&&&&&&&&
if (lstDevices.indexOf(str) == -1)//
防止重复添加
&&&&&&&&&&&&&&&&&
lstDevices.add(str); //
获取设备名称和mac地址
&&&&&&&&&&&&&
adtDevices.notifyDataSetChanged();
&&&&&&&&&&
2.使用BlueAdatper的搜索:
btAdapt.startDiscovery();
& & 3.在BroadcastReceiver的onReceive()里取得搜索所得的蓝牙设备信息(如名称,MAC,RSSI);
& & 4.通过设备的MAC地址来建立一个BluetoothDevice对象;
5.由BluetoothDevice衍生出BluetoothSocket,准备SOCKET来读写设备;
6.通过BluetoothSocket的createRfcommSocketToServiceRecord()方法来选择连接的协议/服务,这里用的
是SPP(UUID:0-805F9B34FB);
try {&&&&&
btSocket =
btDev.createRfcommSocketToServiceRecord(uuid);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e(TAG, "Low: Connection failed.",
成功后进行连接:
btSocket.connect();&&&&&&&&&&&&
Log.e(TAG, " BT connection established, data transfer link
;//自定义函数进行蓝牙通信处理
} catch (IOException e) {
Log.e(TAG, " Connection failed.", e);&
setTitle("连接失败..");
7.Connect之后(如果还没配对则系统自动提示),使用
&&BluetoothSocket的getInputStream()和getOutputStream()来读写蓝牙设备。
&读写可以归到一个独立线程去实现~
注意:读时必须一直循环读取串口缓冲区,写可以不需要。&
按以上7步逐次走过后,你就会发现Android蓝牙模块是多么的坑爹了。
出现问题:
在第6步一般初学者都会报错:
执行.connect()发生Connection refused
& 此时执行不下去咯,怎么办怎么办呢?
& & 于是边debug边网上找攻略,总算在Google出老外的一些做法,尝试了下,貌似还可行。也即把
btSocket的建立方法采用另一种方法替代,这里都使用端口1
btDev.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
btSocket =
(BluetoothSocket) m.invoke(btDev, Integer.valueOf(1));
&&&&&&&&&&&&&
} catch (SecurityException e1) {
&&&&&&&&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&
e1.printStackTrace();
&&&&&&&&&&&&&
} catch (NoSuchMethodException e1) {
&&&&&&&&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&
e1.printStackTrace();
&&&&&&&&&&&&&
} catch (IllegalArgumentException e) {
&&&&&&&&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&
e.printStackTrace();
&&&&&&&&&&&&&
} catch (IllegalAccessException e) {
&&&&&&&&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&
e.printStackTrace();
&&&&&&&&&&&&&
} catch (InvocationTargetException e) {
&&&&&&&&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&
e.printStackTrace();
&&&&&&&&&&
至此,这个问题貌似倒也解决了,程序继续往下跑。
但这里请记住之前的异常,先别急着抛开~人家不一定一直都是异常哦
接下来的任务是,让手机通过蓝牙跟单片机的蓝牙模块通信,并发送数据,通过电脑串口调试助手显示出来。具体实现,在mangeConnectedSocket(btSocket)方法中实现,里面通过启动另一个Activity实现。不是重点,略过。
直到这里,我们都只是把手机蓝牙模块充当客户端来使用,那什么时候会用到服务端呢?其实,之前手机蓝牙与单片机蓝牙模块的通信,单片机蓝牙模块就充当了服务端(处于监听状态,被手机蓝牙连接)。为了更好地搞清楚Android蓝牙通信,我们接下来使用2个手机的蓝牙进行通信。简单地说,就是做一个“手机蓝牙扣扣”,⊙﹏⊙b汗
一开始就想天真地把之前的程序同时烧到2部手机中,发现只有一部手机能正常建立socket连接(主动连接的那台),而另一部却迟迟没有响应。原因很简单,服务端的程序还没有编写!&
于是,开始服务端程序:开辟一个新的线程实现
连接蓝牙设备——蓝牙服务端:
class AcceptThread extends Thread {
private final BluetoothServerSocket serverSocket;
public AcceptThread() {
// Use a temporary object that is later assigned
to mmServerSocket,
// because mmServerSocket is
BluetoothServerSocket tmp=null;
btAdapt.listenUsingRfcommWithServiceRecord("MyBluetoothApp",
Log.e(TAG, "++BluetoothServerSocket
established!++");
& Method listenMethod =
&btAdapt.getClass().getMethod("listenUsingRfcommOn",
&&Class[]{int.class});
= ( BluetoothServerSocket) listenMethod.invoke(btAdapt,
&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&
Integer.valueOf( 1));
&&&&&&&&&&
} catch (SecurityException e) {
&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&
e.printStackTrace();
} catch (IllegalArgumentException e) {
&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&
e.printStackTrace();
} catch (NoSuchMethodException e) {
&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&
e.printStackTrace();
} catch (IllegalAccessException e) {
&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&
e.printStackTrace();
} catch (InvocationTargetException e) {
&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&
e.printStackTrace();
serverSocket=
&& public void run() {
// Keep listening until exception occurs or a
socket is returned
& //mState!=STATE_CONNECTED
&& while(true)
{//这里是一直循环监听,也可以设置mState来判断
&&&&&&&&&&
socket = serverSocket.accept();
&&&&&&&&&&
Log.e(TAG, "++BluetoothSocket established! DataLink
open.++");
&&&&&&&&&&&
} catch (IOException e) {
&&&&&&&&&&&&&&&
&&&&&&&&&&&
&&&&&&&&&&&
// If a connection was accepted
&&&&&&&&&&&
if (socket != null)
&&&&&&&&&&&&&&&
// Do work to manage the connection (in a separate
&&&&&&&&&&&&&&&
manageConnectedSocket();&&&&
&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&
serverSocket.close();
&&&&&&&&&&&&&
} catch (IOException e) {
&&&&&&&&&&&&&&&&&
// TODO Auto-generated catch block
&&&&&&&&&&&&&&&&&
e.printStackTrace();
&&&&&&&&&&&&&
&&&&&&&&&&&&&&&
&&&&&&&&&&&
public void cancel() {
&&&&&&&&&&&
serverSocket.close();
} catch (IOException e) { }
安装测试:当2部手机都装上并打开同样的程序后,通过蓝牙检索并连接,经测试可以成功连接上,双双进入“聊天界面”,嘿嘿
注意,这时候重新拾回之前那个异常,把socket连接建立的方法重新改为
btSocket =
btDev.createRfcommSocketToServiceRecord(uuid);//客户端
对应的服务端程序:
btAdapt.listenUsingRfcommWithServiceRecord("MyBluetoothApp",
uuid);//服务端
这样继续重新运行安装测试,在2部手机上运行发现之前那个bug消失了~2部手机又双双进入聊天界面。
任一一部手机都只能成功启动一次作为客户端的主动连接,当退出聊天界面回到主界面时(服务端的AcceptThread还在继续运行着),可再次主动连接另一部手机时就又报异常Connection refused。也就是说
客户端的蓝牙套接字2次连接时出错~哎(注意我的客户端蓝牙连接程序是没有放到一个独立线程,而是放到一个按钮监听事件中)&
又折腾了好久,没发现个所以然来,看来连完一次退出再连时就只好重启程序咯。有哪位大神知道为什么的麻烦告知下哈!
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 outputstream write 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信