【问题标题】:lateinit property not assigned value soon enough?lateinit 属性没有很快赋值?
【发布时间】:2020-09-20 18:04:15
【问题描述】:

我的代码在 10,000 次中运行 9,999 次。但是,大约每 10,000 次,应用程序崩溃到 UninitializedPropertyAccessException 这是有问题的,因为大约有 20,000~30,000 台 Android 设备在生产中使用我的代码。

看起来,lateinit 变量的实际赋值有时并没有很快发生(因此导致上述异常)。

有没有其他人遇到过类似的问题?你的解决方案是什么?

TcpService.kt

类 TcpService:服务(){
    private lateinit var mTcpClient: TcpClient

    override fun onCreate() {
        registerReceiver(object: BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                when(intent.action) {
                    // This will cause an Uninitialized Property Exception 1 in 10,000 times
                    ACTION_SEND_MESSAGE -> mTcpClient.sendMessageAsync(intent.getStringExtra(EXTRA_NEW_MESSAGE)
                }
            }
        }, IntentFilter(ACTION_SEND_MESSAGE)
        })
    }

    override fun onStart() {
        mTcpClient = mTcpClient()
    }

    // ...

}

TcpClient.kt

class TcpClient() {

    init {
        // ...
        sendBroadcast(Intent(ACTION_SEND_MESSAGE).putExtra(EXTRA_NEW_MESSAGE, "Message Contents")
    }

    // ...

}

【问题讨论】:

  • 并发广播发生在TcpClient对象初始化之前。
  • 为什么不在onCreate()中创建接收者之前移动实例化?
  • @Tenfour04 这似乎是显而易见的解决方案,但这篇文章是真实代码的极其简化的版本,我无法在接收者之前实例化 TcpClient
  • @Alirezaa 感谢您指出这一点——这可能是我做错了。我已经重构了我的代码,使其在 init 块中没有广播。只有时间会证明这是否是正确的解决方案,但我很乐观:)
  • 在TcpClient 上使用start() 方法并将sendBroadcast 位放在那里会很糟糕吗?您在这里所做的是在构造函数(包括init 块)完成运行之前调用实例上的方法 - 通常通过广播系统存在异步延迟,但我想有时它只是直接立即运行?通过将其作为您在mTcpClient = mTcpClient() 之后调用的单独步骤,该字段肯定会被分配

标签: kotlin kotlin-lateinit


【解决方案1】:

啊,它发生在我身上一次,我浪费了一整天然后我解决了它(仍然不知道如何)但是你为什么不让mTcpClient null 然后 null 检查:

 `when(intent.action) {
 // This will cause an Uninitialized Property Exception 1 in 10,000 times
                ACTION_SEND_MESSAGE ->{
if(mTcpClient != null){
mTcpClient.sendMessageAsync(intent.getStringExtra(EXTRA_NEW_MESSAGE)  
}else{
mTcpClient=mTcpClient()
mTcpClient.sendMessageAsync(intent.getStringExtra(EXTRA_NEW_MESSAGE)
}
  }
    }`

抱歉,我不是专业人士

【讨论】:

    【解决方案2】:

    您可以在您的lateInit 属性上使用isInitialized 方法,而不是先将属性设置为空,然后再进行空检查(这在某些情况下可能很危险):

        when (intent.action) {
            if (::mTcpClient.isInitialized) { ACTION_SEND_MESSAGE ->
              mTcpClient.sendMessageAsync(intent.getStringExtra(EXTRA_NEW_MESSAGE)
            }
        }
    

    【讨论】:

    • 我会说使用lateinit 变量并询问它是否已初始化是一种不好的做法或代码异味。最好使用可选类型和空检查。
    • 就我个人而言,我认为isInitialized 比让非空字段可以为空更干净,这样您就可以延迟其初始化,尤其是在像 Kotlin 这样的东西中,您必须围绕该可空性编写所有代码(或者在上面加上!!,这绝对是一种代码味道)
    • 在这种情况下你可以设置一个initialised = true标志!
    猜你喜欢
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多