简体   繁体   English

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

[英]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.实际上,整个代码非常混乱,而且根本上是错误的。

  1. 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 更好地实现更轻松的并发控制和更轻松的线程切换。

  2. Also it's very weird, that you change outside values from a Thread wrapper.此外,您从 Thread 包装器更改外部值也很奇怪。

  3. Error clearly states, that the 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.所以可能你的 Thread 根本没有被调用,因为如果没有竞争条件,它应该不会影响从另一个线程创建的对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何确定我的android runnable在辅助线程上启动? - How can I be sure that my android runnable started on a worker thread? 如何在Android中的onCreate()中启动多个线程而不使用for循环启动线程(IllegalThreadStateException)? - How Do I start multiple threads in Android in onCreate() without getting Thread Already started (IllegalThreadStateException) using for loop? lateinit 变量未在线程中初始化 - lateinit variable not being initialized in a thread 如何使用自定义类对象正确实现Lateinit以在android中的onCreate()中使用,避免出现“预期的属性获取器或设置器”错误 - How to implement lateinit properly with custom class objects for use in onCreate() in android avoiding 'Property getter or setter expected' error 我可以在哪里放置方法 isInitalized 来检查 lateinit 变量? - Where can I put the method isInitalized to check the lateinit variable? 我如何将变量从 OnCreate 传递给函数(查看视图) - How can i pass variable from OnCreate to function(View view) 如何在单独的线程上运行服务? - How can I run a service on a separate thread? 如何在 onCreate 方法之外初始化我的 RelativeLayoutButton? - How can I initialize my RelativeLayoutButton outside of onCreate method? 如何才能启动线程? - How can thread already be started? 如何将我的代码分成不同的文件,并在android中的onCreate方法中调用它们 - How do I separate my code into different files and call them within the onCreate method in android
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM