IOException:读取失败,套接字可能已关闭-Android 4.3上的蓝牙
目前,当我在Nexus7(2012)上使用Android 4.3(Build JWR66Y,我猜是第二次4.3更新)打开BluetoothSocket时,我正在尝试处理一个奇怪的异常。我看过一些相关的帖子(例如),但似乎没有一个能解决这个问题。此外,正如这些线程中所建议的,重新配对没有帮助,不断尝试连接(通过愚蠢的循环)也没有效果 我正在处理一个嵌入式设备(noname OBD-II汽车适配器,类似于)。我的安卓2.3.7手机没有任何连接问题,同事的Xperia(安卓4.1.2)也能正常工作。另一个谷歌Nexus(我不知道是‘一’还是‘S’,但不是‘4’)在Android 4.3上也失败了 下面是连接建立的片段。它在服务中创建的自己的线程中运行IOException:读取失败,套接字可能已关闭-Android 4.3上的蓝牙,android,bluetooth,serial-port,android-4.3-jelly-bean,Android,Bluetooth,Serial Port,Android 4.3 Jelly Bean,目前,当我在Nexus7(2012)上使用Android 4.3(Build JWR66Y,我猜是第二次4.3更新)打开BluetoothSocket时,我正在尝试处理一个奇怪的异常。我看过一些相关的帖子(例如),但似乎没有一个能解决这个问题。此外,正如这些线程中所建议的,重新配对没有帮助,不断尝试连接(通过愚蠢的循环)也没有效果 我正在处理一个嵌入式设备(noname OBD-II汽车适配器,类似于)。我的安卓2.3.7手机没有任何连接问题,同事的Xperia(安卓4.1.2)也能正常工作。另
private class ConnectThread extends Thread {
private static final UUID EMBEDDED_BOARD_SPP = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
private BluetoothAdapter adapter;
private boolean secure;
private BluetoothDevice device;
private List<UUID> uuidCandidates;
private int candidate;
protected boolean started;
public ConnectThread(BluetoothDevice device, boolean secure) {
logger.info("initiliasing connection to device "+device.getName() +" / "+ device.getAddress());
adapter = BluetoothAdapter.getDefaultAdapter();
this.secure = secure;
this.device = device;
setName("BluetoothConnectThread");
if (!startQueryingForUUIDs()) {
this.uuidCandidates = Collections.singletonList(EMBEDDED_BOARD_SPP);
this.start();
} else{
logger.info("Using UUID discovery mechanism.");
}
/*
* it will start upon the broadcast receive otherwise
*/
}
private boolean startQueryingForUUIDs() {
Class<?> cl = BluetoothDevice.class;
Class<?>[] par = {};
Method fetchUuidsWithSdpMethod;
try {
fetchUuidsWithSdpMethod = cl.getMethod("fetchUuidsWithSdp", par);
} catch (NoSuchMethodException e) {
logger.warn(e.getMessage());
return false;
}
Object[] args = {};
try {
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice deviceExtra = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
Parcelable[] uuidExtra = intent.getParcelableArrayExtra("android.bluetooth.device.extra.UUID");
uuidCandidates = new ArrayList<UUID>();
for (Parcelable uuid : uuidExtra) {
uuidCandidates.add(UUID.fromString(uuid.toString()));
}
synchronized (ConnectThread.this) {
if (!ConnectThread.this.started) {
ConnectThread.this.start();
ConnectThread.this.started = true;
unregisterReceiver(this);
}
}
}
};
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
registerReceiver(receiver, new IntentFilter("android.bluetooth.device.action.UUID"));
fetchUuidsWithSdpMethod.invoke(device, args);
} catch (IllegalArgumentException e) {
logger.warn(e.getMessage());
return false;
} catch (IllegalAccessException e) {
logger.warn(e.getMessage());
return false;
} catch (InvocationTargetException e) {
logger.warn(e.getMessage());
return false;
}
return true;
}
public void run() {
boolean success = false;
while (selectSocket()) {
if (bluetoothSocket == null) {
logger.warn("Socket is null! Cancelling!");
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
// Always cancel discovery because it will slow down a connection
adapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
// Close the socket
try {
shutdownSocket();
} catch (IOException e2) {
logger.warn(e2.getMessage(), e2);
}
}
}
if (success) {
deviceConnected();
} else {
deviceDisconnected();
openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
}
}
private boolean selectSocket() {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
logger.info("Attempting to connect to SDP "+ uuid);
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
uuid);
}
bluetoothSocket = tmp;
return true;
} catch (IOException e) {
logger.warn(e.getMessage() ,e);
}
return false;
}
}
我有一些其他OBD-II适配器(更多扩展),它们都可以工作。有没有可能我遗漏了什么,或者这可能是安卓系统中的一个bug?我终于找到了解决办法。魔法隐藏在
蓝牙设备
类的引擎盖下(请参阅)
现在,当我收到该异常时,我将实例化一个回退BluetoothSocket
,类似于下面的源代码。如您所见,通过反射调用隐藏方法createRfcommSocket
。我不知道为什么这个方法是隐藏的。源代码将其定义为public
尽管
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
fallbackSocket.connect();
我终于找到了解决办法。魔法隐藏在
蓝牙设备
类的引擎盖下(请参阅)
现在,当我收到该异常时,我将实例化一个回退BluetoothSocket
,类似于下面的源代码。如您所见,通过反射调用隐藏方法createRfcommSocket
。我不知道为什么这个方法是隐藏的。源代码将其定义为public
尽管
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
fallbackSocket.connect();
我也收到了同样的
IOException
,但是我发现Android系统的演示:“BluetoothChat”项目已经成功了。我确定问题出在UUID上
因此,我将我的
UUID.fromString(“00001001-0000-1000-8000-00805F9B34FB”)
替换为UUID.fromString(“8ce255c0-200a-11e0-ac64-0800200c9a66”)
,它在大多数场景下都能正常工作,只是有时需要重启蓝牙设备 我也收到了同样的IOException
,但我发现Android系统的演示:“BluetoothChat”项目已经成功了。我确定问题出在UUID上
因此,我将我的
UUID.fromString(“00001001-0000-1000-8000-00805F9B34FB”)
替换为UUID.fromString(“8ce255c0-200a-11e0-ac64-0800200c9a66”)
,它在大多数场景下都能正常工作,只是有时需要重启蓝牙设备 我的代码也有同样的问题,这是因为自从安卓4.2蓝牙协议栈改变后。因此,我的代码在安卓<4.2的设备上运行良好,在其他设备上,我遇到了著名的异常“读取失败,套接字可能关闭或超时,读取ret:-1”
问题在于socket.mPort
参数。使用socket=device.createrFComSocketToServiceRecord(SERIAL_UUID)创建套接字时
,则mPort
获取整数值“-1”,而该值似乎不适用于android>=4.2,因此需要将其设置为“1”。坏消息是,createrFComSocketToServiceRecord
只接受UUID作为参数,而不接受mPort
,因此我们必须使用其他方法。@matthes发布的答案也适用于我,但我简化了它:socket=(BluetoothSocket)device.getClass().getMethod(“createRfcommSocket”,新类[]{int.Class})。invoke(device,1)代码>。我们需要使用两个套接字属性,第二个作为后备
因此,代码是(用于连接ELM327设备上的SPP):
嗯,我的代码也有同样的问题,这是因为自从安卓4.2蓝牙协议栈改变后。因此,我的代码在安卓<4.2的设备上运行良好,在其他设备上,我遇到了著名的异常“读取失败,套接字可能关闭或超时,读取ret:-1”
问题在于socket.mPort
参数。使用socket=device.createrFComSocketToServiceRecord(SERIAL_UUID)创建套接字时
,则mPort
获取整数值“-1”,而该值似乎不适用于android>=4.2,因此需要将其设置为“1”。坏消息是,createrFComSocketToServiceRecord
只接受UUID作为参数,而不接受mPort
,因此我们必须使用其他方法。@matthes发布的答案也适用于我,但我简化了它:socket=(BluetoothSocket)device.getClass().getMethod(“createRfcommSocket”,新类[]{int.Class})。invoke(device,1)代码>。我们需要使用两个套接字属性,第二个作为后备
因此,代码是(用于连接ELM327设备上的SPP):
我的症状和这里描述的一样。我可以连接一次蓝牙打印机,但无论我做了什么,随后的连接都会失败,导致“插座关闭”
我觉得有点奇怪,这里描述的变通方法是必要的。在检查代码之后,我发现我忘记关闭套接字的InputStream和OutputSteram,并且没有正确终止ConnectedThreads
我使用的ConnectedThread与这里的示例相同:
请注意,ConnectThread和ConnectedThread是两个不同的类
启动ConnectedThread的任何类都必须在线程上调用interrupt()和cancel()。
我在connectedRead.cancel()方法中添加了mmInStream.close()和mmOutStream.close()
在正确关闭线程/流/套接字之后,我可以创建新的套接字,而不会出现任何问题
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
public class BluetoothConnector {
private BluetoothSocketWrapper bluetoothSocket;
private BluetoothDevice device;
private boolean secure;
private BluetoothAdapter adapter;
private List<UUID> uuidCandidates;
private int candidate;
/**
* @param device the device
* @param secure if connection should be done via a secure socket
* @param adapter the Android BT adapter
* @param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
*/
public BluetoothConnector(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
List<UUID> uuidCandidates) {
this.device = device;
this.secure = secure;
this.adapter = adapter;
this.uuidCandidates = uuidCandidates;
if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
this.uuidCandidates = new ArrayList<UUID>();
this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
}
}
public BluetoothSocketWrapper connect() throws IOException {
boolean success = false;
while (selectSocket()) {
adapter.cancelDiscovery();
try {
bluetoothSocket.connect();
success = true;
break;
} catch (IOException e) {
//try the fallback
try {
bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
Thread.sleep(500);
bluetoothSocket.connect();
success = true;
break;
} catch (FallbackException e1) {
Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
} catch (InterruptedException e1) {
Log.w("BT", e1.getMessage(), e1);
} catch (IOException e1) {
Log.w("BT", "Fallback failed. Cancelling.", e1);
}
}
}
if (!success) {
throw new IOException("Could not connect to device: "+ device.getAddress());
}
return bluetoothSocket;
}
private boolean selectSocket() throws IOException {
if (candidate >= uuidCandidates.size()) {
return false;
}
BluetoothSocket tmp;
UUID uuid = uuidCandidates.get(candidate++);
Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(uuid);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
}
bluetoothSocket = new NativeBluetoothSocket(tmp);
return true;
}
public static interface BluetoothSocketWrapper {
InputStream getInputStream() throws IOException;
OutputStream getOutputStream() throws IOException;
String getRemoteDeviceName();
void connect() throws IOException;
String getRemoteDeviceAddress();
void close() throws IOException;
BluetoothSocket getUnderlyingSocket();
}
public static class NativeBluetoothSocket implements BluetoothSocketWrapper {
private BluetoothSocket socket;
public NativeBluetoothSocket(BluetoothSocket tmp) {
this.socket = tmp;
}
@Override
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
@Override
public OutputStream getOutputStream() throws IOException {
return socket.getOutputStream();
}
@Override
public String getRemoteDeviceName() {
return socket.getRemoteDevice().getName();
}
@Override
public void connect() throws IOException {
socket.connect();
}
@Override
public String getRemoteDeviceAddress() {
return socket.getRemoteDevice().getAddress();
}
@Override
public void close() throws IOException {
socket.close();
}
@Override
public BluetoothSocket getUnderlyingSocket() {
return socket;
}
}
public class FallbackBluetoothSocket extends NativeBluetoothSocket {
private BluetoothSocket fallbackSocket;
public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
super(tmp);
try
{
Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};
fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
}
catch (Exception e)
{
throw new FallbackException(e);
}
}
@Override
public InputStream getInputStream() throws IOException {
return fallbackSocket.getInputStream();
}
@Override
public OutputStream getOutputStream() throws IOException {
return fallbackSocket.getOutputStream();
}
@Override
public void connect() throws IOException {
fallbackSocket.connect();
}
@Override
public void close() throws IOException {
fallbackSocket.close();
}
}
public static class FallbackException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public FallbackException(Exception e) {
super(e);
}
}
}
BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
if (btAdapter.isEnabled()) {
SharedPreferences prefs_btdev = getSharedPreferences("btdev", 0);
String btdevaddr=prefs_btdev.getString("btdevaddr","?");
if (btdevaddr != "?")
{
BluetoothDevice device = btAdapter.getRemoteDevice(btdevaddr);
UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // bluetooth serial port service
//UUID SERIAL_UUID = device.getUuids()[0].getUuid(); //if you don't know the UUID of the bluetooth device service, you can get it like this from android cache
BluetoothSocket socket = null;
try {
socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
} catch (Exception e) {Log.e("","Error creating socket");}
try {
socket.connect();
Log.e("","Connected");
} catch (IOException e) {
Log.e("",e.getMessage());
try {
Log.e("","trying fallback...");
socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
socket.connect();
Log.e("","Connected");
}
catch (Exception e2) {
Log.e("", "Couldn't establish Bluetooth connection!");
}
}
}
else
{
Log.e("","BT device not selected");
}
}
String checkType;
var listDevices = BluetoothAdapter.BondedDevices;
if (listDevices.Count > 0)
{
foreach (var btDevice in listDevices)
{
if(btDevice.Name == "MOCUTE-032_B52-CA7E")
{
checkType = btDevice.Type.ToString();
Console.WriteLine(checkType);
}
}
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device)
throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, mMyUuid);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}
return device.createRfcommSocketToServiceRecord(mMyUuid);
}
fun print(view: View, text: String) {
var adapter = BluetoothAdapter.getDefaultAdapter();
var pairedDevices = adapter.getBondedDevices()
var uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
if (pairedDevices.size > 0) {
for (device in pairedDevices) {
var s = device.name
if (device.getName().equals(printerName, ignoreCase = true)) {
Thread {
var socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
var clazz = socket.remoteDevice.javaClass
var paramTypes = arrayOf<Class<*>>(Integer.TYPE)
var m = clazz.getMethod("createRfcommSocket", *paramTypes)
var fallbackSocket = m.invoke(socket.remoteDevice, Integer.valueOf(1)) as BluetoothSocket
try {
fallbackSocket.connect()
var stream = fallbackSocket.outputStream
stream.write(text.toByteArray(Charset.forName("UTF-8")))
} catch (e: Exception) {
e.printStackTrace()
Snackbar.make(view, "An error occurred", Snackbar.LENGTH_SHORT).show()
}
}.start()
}
}
}
}
fun disconnect() {
bluetoothSocket.inputStream.close()
bluetoothSocket.outputStream.close()
bluetoothSocket.close()
}
// Register for broadcasts when a device is discovered
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, intentFilter);
val id: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // Any other GUID doesn't work.
val device: BluetoothDevice = bta!!.bondedDevices.first { z -> z.name == deviceName }
bts = device.createRfcommSocketToServiceRecord(id) // mPort is -1
bts?.connect()
// Start processing thread.
var did: Array<ParcelUuid?> = device.uuids