现象
class KotlinLocalVariableActivity : BaseActivity() {
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val editText = EditText(this)
setContentView(editText)
var num = 0
editText.doOnTextChanged { text, start, before, count ->
scope.launch {
Log.d("hehe", "$num. text: $text")
delay(2000L)
Log.d("hehe", "$num. text: $text")
num++
}
}
}
}
无意中发现上述代码在两处打印日志的地方打印出来的值居然不一样,让我一度怀疑局部变量居然能发生变化?
原因
最后排查才发现,每次回调传过来的 text 居然是一个引用值,也就是说当 EditText 发生变化时,text 的值就已经变了,在上一次的回调中如果延时(代码里是 2s)后再去获取 text 的值,就已经是新值了。
想要不变,转成局部变量就可以了,如:
// toString() 不可少
val localText = text.toString()
Smart cast is impossible
测试过程中还发现了 Smart cast is impossible
的问题,也一起记录下.
class TestActivity : BaseActivity() {
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val editText = EditText(this)
setContentView(editText)
var str: String? = null
scope.launch {
str = ""
}
if (null != str) {
val len = str.length
LogUtils.d("len: $len")
}
}
}
image.png
由于协程可能运行在其它线程,所以协程内如果对局部变量进行了赋值,那么局部变量就可能发生变化,所以这里即时进行了 if (null != str)
的判断,由于线程问题,等运行到 str.length
的时候也可能发生空指针。
如果把协程内的 str = ""
注释掉,那么 IDE 则不会提示有问题了。
网友评论