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

问题描述 投票:0回答:1

正在研究一个小型应用程序,该应用程序侦听来自通过蓝牙连接的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

我可以理解为什么。但是我不清楚如何适当地初始化它而不弄乱内部类吗?我在这里显然很困惑。 :)

android kotlin android-bluetooth
1个回答
0
投票

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

  1. 如果要从UI线程更改UI元素,则无需在onCreate上运行代码,只需调用runOnUiThread Android函数,它将在UI线程上运行代码。但我不建议为此使用线程。为什么要启动单独的线程进行蓝牙连接?我建议更好地使用Kotlin Coroutines,以更轻松地进行并发控制和线程切换。

  2. 同样,很奇怪,您从线程包装器更改了外部值。

  3. 错误明确指出,bluetoothSocket未初始化(可能是您按下断开连接的部分)。因此,可能根本没有调用您的线程,因为如果没有竞争条件,它就不会对从另一个线程创建对象产生太大影响。
© www.soinside.com 2019 - 2024. All rights reserved.