[英]How can I use a lateinit variable that is started in a separate thread than my onCreate()?
Working on a small app that listens for data from an Arduino device that it is connected to via bluetooth.正在开发一个小应用程序,该应用程序侦听来自通过蓝牙连接的 Arduino 设备的数据。 The Arduino has an LED on board that is controlled by a button.
Arduino 板上有一个 LED,由按钮控制。 When the LED is turned on or off the Arudino sends a 1(on) or 0(off) to indicate a state change.
当 LED 打开或关闭时,Arudino 会发送 1(开)或 0(关)来指示状态变化。 My app when it sees that state change should display or hide an image.
我的应用程序在看到状态更改时应显示或隐藏图像。
It does not.它不是。
I believe the problem is that my bluetoothSocket is called from a private inner class that is running on a different thread than the main thread.我相信问题在于我的 bluetoothSocket 是从运行在与主线程不同的线程上的私有内部类调用的。 But I am stumped as to how to correct this.
但我对如何纠正这个问题感到困惑。 Or I am completely wrong in my assumption.
或者我的假设完全错误。
The app does connect via bluetooth and I can see the LED state change messages in LogCat.该应用程序确实通过蓝牙连接,我可以在 LogCat 中看到 LED 状态更改消息。
This function is listening for those LED state changes:此功能正在侦听这些 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
}
}
}
This is the showOrHideImage() function that I tried to use moving the if else statement down here.这是我尝试使用的 showOrHideImage() 函数,将 if else 语句移到此处。
private fun View.showOrHideImage(imageShow: Boolean) {
visibility = if (imageShow) {
View.VISIBLE
} else {
View.INVISIBLE
}
}
Private inner class that sets up my bluetoothSocket connect when requested.在请求时设置我的 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)
}
}
I'm calling readBlueToothDataFromMothership() from here.我从这里调用 readBlueToothDataFromMothership() 。 But I know this is wrong.
但我知道这是错误的。
I've tried to call if from onCreate but that fails since at that point I have not initialized bluetoothSocket.我试图从 onCreate 调用 if 但失败了,因为那时我还没有初始化 bluetoothSocket。
Which is where I get hung up.这就是我挂断电话的地方。 I am unclear when or how/where to do the initilization.
我不清楚何时或如何/在哪里进行初始化。
This is the onCreate(), but without the call to readBlueToothDataFromMothership()这是 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
}
}
If I run the app with the call to readBlueToothDataFromMothership(bluetoothSocket) in the inner Class as it is above, it will run until I have pressed the Arduino's button 4 times.如果我在内部类中调用 readBlueToothDataFromMothership(bluetoothSocket) 来运行应用程序,它会一直运行,直到我按下 Arduino 的按钮 4 次。 Then crashes with the following:
然后崩溃如下:
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.
If I move the call up to onCreate() it crashes immediately with the following:如果我将调用向上移动到 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
Which I can understand why.我可以理解为什么。 But I am unclear on how to initilize this properly without messing up the inner class?
但是我不清楚如何在不弄乱内部类的情况下正确地初始化它? I am obviously am confused here.
我显然在这里感到困惑。 :)
:)
Actually the whole code is quite confusing and fundamentally incorrect.实际上,整个代码非常混乱,而且根本上是错误的。
If you want to change UI elements from UI thread, it's not necessary to run the code onCreate, you can just call runOnUiThread
Android function and it will run the code on UI thread.如果您想从 UI 线程更改 UI 元素,则无需运行 onCreate 代码,您只需调用
runOnUiThread
Android 函数,它将在 UI 线程上运行代码。 But I am not recommending using threads for it.但我不建议使用线程。 Why launching a separate thread for bluetooth connection?
为什么要为蓝牙连接启动一个单独的线程? I do recommend Kotlin Coroutines better for easier concurrency control and easier thread switch.
我确实推荐 Kotlin Coroutines 更好地实现更轻松的并发控制和更轻松的线程切换。
Also it's very weird, that you change outside values from a Thread wrapper.此外,您从 Thread 包装器更改外部值也很奇怪。
bluetoothSocket
is not initialized (and probably this is the part where you press disconnect).bluetoothSocket
未初始化(这可能是您按下断开连接的部分)。 So probably your Thread is not called at all, because it should not affect that much that the object is created from another thread if there are no race conditioning.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.