К сожалению, пока не возникнет следующая ошибка. При возникновении этой ошибки приложение продолжает закрывать соединение все время после запроса соединения с устройством IOT.
Журнал ошибок:
Код: Выделить всё
2024-01-03 16:01:00.535 8401-8416 System com.mybluetooth.iot E Uncaught exception thrown by finalizer
2024-01-03 16:01:00.541 8401-8416 System com.mybluetooth.iot E java.io.IOException: socket not created
at android.net.LocalSocketImpl.shutdownInput(LocalSocketImpl.java:371)
at android.net.LocalSocket.shutdownInput(LocalSocket.java:238)
at android.bluetooth.BluetoothSocket.close(BluetoothSocket.java:780)
at android.bluetooth.BluetoothSocket.finalize(BluetoothSocket.java:371)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
at java.lang.Daemons$Daemon.run(Daemons.java:145)
at java.lang.Thread.run(Thread.java:1012)
2024-01-03 16:01:00.541 8401-8416 System com.mybluetooth.iot E Uncaught exception thrown by finalizer
2024-01-03 16:01:00.541 8401-8416 System com.mybluetooth.iot E java.io.IOException: socket not created
at android.net.LocalSocketImpl.shutdownInput(LocalSocketImpl.java:371)
at android.net.LocalSocket.shutdownInput(LocalSocket.java:238)
at android.bluetooth.BluetoothSocket.close(BluetoothSocket.java:780)
at android.bluetooth.BluetoothSocket.finalize(BluetoothSocket.java:371)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
at java.lang.Daemons$Daemon.run(Daemons.java:145)
at java.lang.Thread.run(Thread.java:1012)
2024-01-03 16:01:00.542 8401-8416 System com.mybluetooth.iot E Uncaught exception thrown by finalizer
2024-01-03 16:01:00.542 8401-8416 System com.mybluetooth.iot E java.io.IOException: socket not created
at android.net.LocalSocketImpl.shutdownInput(LocalSocketImpl.java:371)
at android.net.LocalSocket.shutdownInput(LocalSocket.java:238)
at android.bluetooth.BluetoothSocket.close(BluetoothSocket.java:780)
at android.bluetooth.BluetoothSocket.finalize(BluetoothSocket.java:371)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:339)
at java.lang.Daemons$FinalizerDaemon.processReference(Daemons.java:324)
at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300)
at java.lang.Daemons$Daemon.run(Daemons.java:145)
at java.lang.Thread.run(Thread.java:1012)
**Error log for making Re-connect attempt after turning ON the IOT device**
2024-01-03 17:21:33.547 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.572 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.582 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.591 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.600 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.606 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.613 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.626 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.634 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.643 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:33.655 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_DISCONNECTED
2024-01-03 17:21:34.273 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.282 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.282 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.282 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.282 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.283 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.284 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.284 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.284 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.285 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.285 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL_CONNECTED
2024-01-03 17:21:34.540 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: Disconnected State
2024-01-03 17:21:34.560 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL Event
2024-01-03 17:21:34.585 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: Disconnected State
2024-01-03 17:21:34.611 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: This is ACL Event
2024-01-03 17:21:34.612 18742-18742 BaseActivity com.mybluetooth.iot E Base Activity: Connected
2024-01-03 17:21:34.612 18742-18742 DashboardA...........Kt com.mybluetooth.iot E :: This is for re-connected
2024-01-03 17:21:34.636 18742-18777 BT IO com.mybluetooth.iot E readByteArrayStream: Connection Disconnected from finally
2024-01-03 17:21:34.636 18742-18777 BT IO com.mybluetooth.iot E All Connections closed
Код: Выделить всё
class BluetoothServiceProviderIO(val bluetoothSocket: BluetoothSocket?) {
private val inputStream: InputStream? = try {
bluetoothSocket?.inputStream
} catch (e: IOException) {
Log.e("BT IO", "Could not Open Input Stream")
e.printStackTrace()
throw SocketStreamException("Couldn't open InputStream socket")
}
private val outputStream: OutputStream? = try {
bluetoothSocket?.outputStream
} catch (e: IOException) {
Log.e("BT IO", "Could not Open OutputStream")
e.printStackTrace()
throw SocketStreamException("Couldn't open OutputStream socket")
}
/**
* Send array of bytes to bluetooth output stream.
*
* @param bytes data to send
* @return true if success, false if there was error occurred or disconnected
*/
fun send(data: ModelSendData): Boolean {
if (bluetoothSocket?.isConnected != true) return false
return try {
outputStream?.write(data.data)
outputStream?.flush()
true
} catch (e: IOException) {
e.printStackTrace()
false
}
}
@ExperimentalCoroutinesApi
fun readByteArrayStream(
delayMillis: Long = 10,
bufferCapacity: Int = 32
): Flow = channelFlow {
if (inputStream == null) {
Log.e("BT IO", "input Stream is null")
throw NullPointerException("inputStream is null. Perhaps bluetoothSocket is also null")
}
val buffer = ByteArray(bufferCapacity)
while (isActive) {
try {
if (inputStream.available() > 0) {
val numBytes = inputStream.read(buffer)
val readBytes = ByteArray(size = numBytes)
for (i in 0 until numBytes) {
readBytes[i] = buffer[i]
}
delay(delayMillis)
this.trySend(readBytes).isSuccess
}
} catch (e: IOException) {
Log.e("BT IO", "readByteArrayStream: Connection Disconnected from Catch")
closeConnections()
error("Couldn't read bytes from flow. Disconnected")
} finally {
if (bluetoothSocket?.isConnected != true) {
Log.e("BT IO", "readByteArrayStream: Connection Disconnected from finally")
closeConnections()
break
}
}
}
}.flowOn(Dispatchers.IO)
/**
* Close the streams and socket connection.
*/
fun closeConnections() {
try {
inputStream?.close()
outputStream?.close()
bluetoothSocket?.close()
Log.e("BT IO", "All Connections closed")
} catch (e: Exception) {
Log.e("BT IO", "Connections close failed ${e.printStackTrace()}")
}
}
}
Код: Выделить всё
@SuppressLint("MissingPermission")
class BluetoothServiceProvider(private val context: Context) {
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
@Volatile
var currentBluetoothSocket: BluetoothSocket? = null
private var bluetoothServiceProviderIO: BluetoothServiceProviderIO? = null
@SuppressLint("HardwareIds")
fun isBluetoothAvailable() =
!(bluetoothManager.adapter == null || TextUtils.isEmpty(bluetoothManager.adapter.address))
fun isBluetoothEnabled() = bluetoothManager.adapter.isEnabled
fun getIO(bluetoothSocket: BluetoothSocket): BluetoothServiceProviderIO {
if (bluetoothServiceProviderIO?.bluetoothSocket == bluetoothSocket) {
return bluetoothServiceProviderIO as BluetoothServiceProviderIO
}
currentBluetoothSocket = bluetoothSocket
bluetoothServiceProviderIO = BluetoothServiceProviderIO(bluetoothSocket)
return bluetoothServiceProviderIO as BluetoothServiceProviderIO
}
fun getIO(): BluetoothServiceProviderIO {
return getIO(currentBluetoothSocket!!)
}
@SuppressLint("MissingPermission")
fun enable() = bluetoothManager.adapter.isEnabled
fun disable() = !bluetoothManager.adapter.isEnabled
@SuppressLint("MissingPermission")
fun bondedDevices(): List = bluetoothManager.adapter.bondedDevices.toList()
fun startDiscovery() = bluetoothManager.adapter.startDiscovery()
fun isDiscovering() = bluetoothManager.adapter.isDiscovering
fun cancelDiscovery() = if (bluetoothManager.adapter.isDiscovering) {
bluetoothManager.adapter.cancelDiscovery()
true
} else {
false
}
@ExperimentalCoroutinesApi
fun discoverDevices(): Flow = callbackFlow {
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND).apply {
addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
}
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
BluetoothDevice.ACTION_FOUND -> {
val device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) as BluetoothDevice?
val rssi =
intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, MIN_VALUE).toInt()
device?.let { trySend(BluetoothDeviceWrapper(it, rssi)).isSuccess }
}
BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
}
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
}
}
}
}
context.registerReceiver(receiver, filter)
awaitClose {
context.unregisterReceiver(receiver)
}
}.flowOn(Dispatchers.IO)
@ExperimentalCoroutinesApi
fun checkDiscoveryStatus(): Flow = callbackFlow {
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND).apply {
addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
}
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
BluetoothDevice.ACTION_FOUND -> {
}
BluetoothAdapter.ACTION_DISCOVERY_STARTED -> {
trySend(BluetoothAdapter.ACTION_DISCOVERY_STARTED)
}
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
trySend(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
}
}
}
}
context.registerReceiver(receiver, filter)
awaitClose {
context.unregisterReceiver(receiver)
}
}.flowOn(Dispatchers.IO)
@ExperimentalCoroutinesApi
fun getDeviceBondState(): Flow = callbackFlow {
val filter = IntentFilter().apply {
addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
}
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
val mDevice: BluetoothDevice =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)!!
//3 cases:
//case1: bonded already
if (mDevice.bondState == BluetoothDevice.BOND_BONDED) {
Log.e("BluetoothServiceProvider", "BroadcastReceiver: BOND_BONDED.")
}
//case2: creating a bond
if (mDevice.bondState == BluetoothDevice.BOND_BONDING) {
Log.e("BluetoothServiceProvider", "BroadcastReceiver: BOND_BONDING.")
}
//case3: breaking a bond
if (mDevice.bondState == BluetoothDevice.BOND_NONE) {
Log.e("BluetoothServiceProvider", "BroadcastReceiver: BOND_NONE.")
}
trySend(mDevice.bondState).isSuccess
}
}
}
context.registerReceiver(receiver, filter)
awaitClose {
context.unregisterReceiver(receiver)
}
}.flowOn(Dispatchers.IO)
fun connectToDevice(bluetoothDevice: BluetoothDevice,
uuid: UUID,
secure: Boolean = true): Deferred =
CoroutineScope(Dispatchers.IO).async {
val bluetoothSocket =
if (secure) bluetoothDevice.createRfcommSocketToServiceRecord(uuid)
else bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid)
bluetoothSocket.apply {
currentBluetoothSocket = this@apply
connect()
}
}
@Throws(java.lang.Exception::class)
fun createBond(btDevice: BluetoothDevice?): Boolean {
val btClass = Class.forName("android.bluetooth.BluetoothDevice")
val createBondMethod = btClass.getMethod("createBond")
return createBondMethod.invoke(btDevice) as Boolean
}
@ExperimentalCoroutinesApi
fun aclEvents(): Flow = callbackFlow {
val filter = IntentFilter().apply {
addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)
addAction(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED)
}
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val device =
it.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) as? BluetoothDevice
trySend(AclEvent(it.action ?: "", device)).isSuccess
}
}
}
context.registerReceiver(receiver, filter)
awaitClose {
context.unregisterReceiver(receiver)
}
}.flowOn(Dispatchers.IO)
fun closeConnections() = bluetoothServiceProviderIO?.closeConnections()
}
Код: Выделить всё
class BTConnect(private val bluetoothServiceProvider: BluetoothServiceProvider) {
private val uuid: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
@SuppressLint("MissingPermission")
fun connect(device: BluetoothDevice) = flow {
var state: BtConnection? = null
try {
bluetoothServiceProvider.connectToDevice(device, uuid, true).await()
bluetoothServiceProvider.currentBluetoothSocket.apply {
state = if (bluetoothServiceProvider.currentBluetoothSocket!!.isConnected) {
BtConnection.BtConnectedState(socket = bluetoothServiceProvider.currentBluetoothSocket!!)
} else {
BtConnection.BtDisconnectedState
}
}
} catch (e: IOException) {
state = BtConnection.BtDisconnectedState
} catch (e: Exception) {
state = BtConnection.BtDisconnectedState
Log.e("BT Connect", "connect: Failed other than IO Exception $e")
}
emit(state)
}
}
Код: Выделить всё
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
class BaseActivity : AppCompatActivity() {
var connectionState = MutableLiveData()
private var progressDialog: CustomDialog? = null
lateinit var connectionStatus: Job
@Inject
lateinit var btConnect: BTConnect
@Inject
lateinit var bluetoothServiceProvider: BluetoothServiceProvider
@Inject
lateinit var preferenceHelper: PreferenceHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
setContentView(layoutResId)
window.insetsController?.hide(WindowInsets.Type.statusBars())
MyApplication.component.inject(this)
}
@SuppressLint("MissingPermission")
override fun onResume() {
isScreenVisible = true
super.onResume()
}
@SuppressLint("MissingPermission", "SetTextI18n")
open fun checkForBondedDevices(): BluetoothDevice? {
var device: BluetoothDevice? = null
if (bluetoothServiceProvider.bondedDevices().isEmpty()) {
device = null
} else {
for (i in bluetoothServiceProvider.bondedDevices()) {
if ((i.name == preferenceHelper.deviceName) && (i.address == preferenceHelper.deviceAddress)) {
device = i
}
}
}
return device
}
open fun checkSendStatus(device: BluetoothDevice) {
lifecycleScope.launch {
if (isDeviceConnected) isDeviceConnected = false
withContext(Dispatchers.Main) {
while (!isDeviceConnected && isScreenVisible) {
btConnect.connect(device).safeCollect {
delay(200)
if (it != null) {
checkBtConnectionState(it)
}
return@safeCollect
}
}
}
}
}
@SuppressLint("MissingPermission")
open suspend fun checkBtConnectionState(btConnection: BtConnection) {
connectionState.postValue(btConnection)
when (btConnection) {
is BtConnection.BtConnectingLoadingState -> {
isDeviceConnected = false
showLog("Connecting Loading State")
}
is BtConnection.BtConnectedState -> {
isDeviceConnected = true
showLog("Connected")
if (!firstTimeNotConnectCheck) {
onDeviceConnected()
} else {
onDeviceReconnected()
}
startCommunicationWithDevice()
}
is BtConnection.BtErrorConnectingState -> {
isDeviceConnected = false
showLog("Connecting State")
}
is BtConnection.BtDisconnectedState -> {
isDeviceConnected = false
showLog("Disconnected State")
onDeviceDisconnected()
}
}
}
open fun checkConnectionStateOfPairedDevice(device: BluetoothDevice) {
connectionStatus = lifecycleScope.launch {
withContext(Dispatchers.Main) {
bluetoothServiceProvider.aclEvents().safeCollect {
when (it.action) {
BluetoothDevice.ACTION_ACL_CONNECTED -> {
isDeviceConnected = true
}
BluetoothDevice.ACTION_ACL_DISCONNECTED -> {
isDeviceConnected = false
checkSendStatus(checkForBondedDevices()!!)
}
}
}
}
}
}
@SuppressLint("MissingPermission")
open suspend fun startCommunicationWithDevice() {
bluetoothServiceProvider.readByteArrayStream().safeCollect {
if (it.isNotEmpty()) {
processReceivedBytesForResponse(it)
} else {
showLog("Data not received")
}
}
}
open fun processReceivedBytesForResponse(byteArray: ByteArray) {
try {
if (byteArray.isNotEmpty()) {
// Process data
}
} catch (e: Exception) {
showLog("From Processed Received Data: $e")
}
}
override fun onPause() {
bluetoothServiceProvider.closeConnections()
isScreenVisible = false
super.onPause()
}
private fun showLog(s: String) {
Log.e(tag, "Base Activity: $s")
}
open fun showToast(msg: String) {
if (msg.isNotEmpty()) Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
open fun showProgressDialog() {
progressDialog = CustomDialog(this)
progressDialog!!.setMessage("Please wait...")
progressDialog!!.setProgressStyle(ProgressDialog.STYLE_SPINNER)
progressDialog!!.isIndeterminate = true
progressDialog!!.setCancelable(false)
progressDialog!!.show()
}
open fun hideProgressDialog() {
if (progressDialog != null) {
progressDialog!!.dismiss()
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
hideSystemUI()
}
}
abstract fun onDeviceConnected()
abstract fun onDeviceReconnected()
abstract fun onDeviceDisconnected()
}
Это один из моих справочных кодов для разработки эту кодовую базу. https://github.com/ThanosFisherman/BlueFlow
Подробнее здесь: https://stackoverflow.com/questions/777 ... -android-j
Мобильная версия