如何使用在与我的 onCreate() 不同的线程中启动的 lateinit 变量?



开发一个小应用程序,侦听来自通过蓝牙连接的Arduino设备的数据。Arduino在板上有一个由按钮控制的LED。当 LED 打开或关闭时,Arudino 会发送 1(on( 或 0(off( 以指示状态更改。我的应用在看到状态更改时应显示或隐藏图像。

它没有。

我相信问题是我的蓝牙插座是从在与主线程不同的线程上运行的私有内部类调用的。但是我对如何纠正这一点感到困惑。或者我的假设完全错误。

该应用程序确实通过蓝牙连接,我可以在 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
}
}

私有内部类,用于在请求时设置我的蓝牙套接字连接。

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调用if,但失败了,因为那时我还没有初始化蓝牙套接字。

这就是我挂断电话的地方。我不清楚何时或如何/何地进行启动。

这是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
}
}

如果我在内部类中调用读取蓝牙数据来自母舰(蓝牙插座(的应用程序,如上所示,它将运行,直到我按下 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

我可以理解为什么。但是我不清楚如何在不弄乱内部类的情况下正确启动它?我显然在这里感到困惑。:)

实际上,整个代码非常混乱,从根本上是不正确的。

  1. 如果你想从UI线程更改UI元素,没有必要在Create上运行代码,你可以调用runOnUiThreadAndroid函数,它将在UI线程上运行代码。但我不建议为此使用线程。为什么要为蓝牙连接启动单独的线程?我确实推荐 Kotlin 协程更好,以便更轻松地进行并发控制和线程切换。

  2. 同样非常奇怪的是,您从线程包装器更改外部值。

  3. 错误清楚地表明,bluetoothSocket未初始化(可能是您按断开连接的部分(。因此,可能根本没有调用您的 Thread,因为如果没有竞争条件,它应该不会影响从另一个线程创建的对象。

最新更新