正在研究一个小型应用程序,该应用程序侦听来自通过蓝牙连接的Arduino设备的数据。 Arduino板上有一个由按钮控制的LED。当LED打开或关闭时,Arudino发送1(打开)或0(关闭)以指示状态更改。当我的应用程序看到状态更改应显示或隐藏图像时。
没有。
我相信问题是我的bluetoothSocket是从运行在与主线程不同的线程上的私有内部类调用的。但是我对如何纠正这个问题感到困惑。还是我的假设完全错误。
该应用程序确实通过蓝牙连接,我可以在LogCat中看到LED状态更改消息。
此功能正在监听那些LED状态变化:
private fun readBlueToothDataFromMothership(bluetoothSocket: android.bluetooth.BluetoothSocket) {
val bluetoothSocketInputStream = bluetoothSocket.inputStream
val buffer = ByteArray(256)
var bytes: Int
while (true) {
try {
bytes = bluetoothSocketInputStream.read(buffer)
val readMessage = String(buffer, 0, bytes)
Log.i(LOGTAG, readMessage)
if (readMessage == "1") {
imageView_mothership_LED_state.showOrHideImage(true)
} else {
imageView_mothership_LED_state.showOrHideImage(false)
}
} catch (e: IOException) {
e.printStackTrace()
break
}
}
}
这是showOrHideImage()函数,我试图在此处将if else语句向下移动。
private fun View.showOrHideImage(imageShow: Boolean) {
visibility = if (imageShow) {
View.VISIBLE
} else {
View.INVISIBLE
}
}
根据要求设置我的bluetoothSocket连接的私有内部类。
private inner class ConnectThread(device: BluetoothDevice): Thread() {
private var newSocket = device.createRfcommSocketToServiceRecord(uuid)
override fun run() {
try {
Log.d(LOGTAG, "Connecting bluetoothSocket")
handler.post {
connectedOrNotTextView.text = getString(R.string.connecting)
connectToDeviceButton.isEnabled = false
}
bluetoothSocket = newSocket
bluetoothSocket.connect()
Log.d(LOGTAG, "Socket connected")
handler.post {
connectedOrNotTextView.text = getString(R.string.connected)
connectToDeviceButton.isEnabled = false; disconnectButton.isEnabled = true
}
}catch (e1: Exception){
Log.e(LOGTAG, "Error connecting bluetoothSocket, $e1")
handler.post {
connectedOrNotTextView.text = getString(R.string.connection_failed)
connectToDeviceButton.isEnabled = true; disconnectButton.isEnabled = false
}
}
readBlueToothDataFromMothership(bluetoothSocket)
}
}
我从这里调用readBlueToothDataFromMothership()。但是我知道这是错误的。
我尝试从onCreate调用,但是失败,因为那时候我还没有初始化bluetoothSocket。
我在哪儿挂断电话。我不清楚何时,如何/在哪里进行初始化。
这是onCreate(),但没有调用readBlueToothDataFromMothership()
private lateinit var selectedBluetoothDevice: BluetoothDevice
private lateinit var bluetoothAdapter: BluetoothAdapter
private lateinit var bluetoothSocket: bluetoothSocket
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (!bluetoothAdapter.isEnabled) {
bluetoothAdapter.enable()
}
val pairedDevices: Set<BluetoothDevice> = bluetoothAdapter.bondedDevices
pairedDevices.forEach {
// MAC of test HC-05 Bluetooth shield. MAC of HC-05 on Motherboard: 00:14:03:06:1B:AE
if (it.address == "00:14:03:06:21:64") {
selectedBluetoothDevice = it
bluetoothNameTextView.text = selectedBluetoothDevice.name
bluetoothAddressTextView.text = selectedBluetoothDevice.address
}
else if (it.address == "00:00:00:00:00:00") { // CHANGE BLUETOOTH ADDRESS HERE
selectedBluetoothDevice = it
bluetoothNameTextView.text = selectedBluetoothDevice.name
bluetoothAddressTextView.text = selectedBluetoothDevice.address
}
}
AcceptThread().start()
handler = Handler()
connectToDeviceButton.setOnClickListener{
ConnectThread(selectedBluetoothDevice).start()
}
disconnectButton.setOnClickListener{
Log.d(LOGTAG, "Closing bluetoothSocket and connection")
bluetoothSocket.close()
connectedOrNotTextView.text = getString(R.string.notConnected)
connectToDeviceButton.isEnabled = true; disconnectButton.isEnabled = false
}
}
如果我通过上面的内部类中的readBlueToothDataFromMothership(bluetoothSocket)调用来运行应用程序,它将运行直到我按下Arduino的按钮4次。然后崩溃并显示以下内容:
2019-12-05 11:03:18.641 4025-5005/com.example.pigcatcher I/MainActivity: 0
2019-12-05 11:03:19.740 4025-5005/com.example.pigcatcher I/MainActivity: 1
2019-12-05 11:03:20.839 4025-5005/com.example.pigcatcher I/MainActivity: 0
2019-12-05 11:03:21.747 4025-5005/com.example.pigcatcher I/MainActivity: 1
2019-12-05 11:03:21.762 4025-5005/com.example.pigcatcher E/AndroidRuntime: FATAL EXCEPTION: Thread-6
Process: com.example.pigcatcher, PID: 4025
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a
view hierarchy can touch its views.
如果将调用移至onCreate(),则会立即崩溃,并显示以下内容:
Process: com.example.pigcatcher, PID: 29343
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.pigcatcher/com.example.pigcatcher.MainActivity}:
kotlin.UninitializedPropertyAccessException: lateinit property bluetoothSocket has not been
initialized
我可以理解为什么。但是我不清楚如何适当地初始化它而不弄乱内部类吗?我在这里显然很困惑。 :)
实际上,整个代码非常混乱,从根本上来说是不正确的。
如果要从UI线程更改UI元素,则无需在onCreate上运行代码,只需调用runOnUiThread
Android函数,它将在UI线程上运行代码。但我不建议为此使用线程。为什么要启动单独的线程进行蓝牙连接?我建议更好地使用Kotlin Coroutines,以更轻松地进行并发控制和线程切换。
同样,很奇怪,您从线程包装器更改了外部值。
bluetoothSocket
未初始化(可能是您按下断开连接的部分)。因此,可能根本没有调用您的线程,因为如果没有竞争条件,它就不会对从另一个线程创建对象产生太大影响。