BindingAdapter LiveData第一个值始终为null

时间:2019-04-13 16:34:12

标签: android kotlin android-livedata android-binding-adapter

我正在尝试在LiveData函数中处理profilePicture: Bitmap值(BindingAdapter),但是第一个值始终为 null 。绑定适配器是具有ViewSwitcherProgressBar的{​​{1}}。

像这样从Firebase获取个人头像:

ImageView

由于第一个值为 null ,第二个为预期的位图对象,因此 val downloadPictureResult = MutableLiveData<Bitmap>() // ... fun downloadProfilePic(): LiveData<Bitmap?> { val imageRef = storage.getReference("images/$uid/profile/profile_picture.jpg") imageRef.getBytes(ONE_MEGABYTE).addOnCompleteListener { task -> if (task.isSuccessful) { //... downloadPictureResult.value = responseBitmap //... } else { downloadPictureResult.value = null Log.d(TAG, task.exception?.localizedMessage) } } return downloadPictureResult; } 被调用了两次。但是对我来说,更重要的是要理解为什么firstvalue是 null 的原因,因为view.showNext()方法将具有更多的逻辑。

BindingAdapter看起来像这样。

setProfilePicture
  

日志:

fun setProfilePicture(view: ViewSwitcher, profilePicture: Bitmap?) {
 Log.d("PPSS", profilePicture.toString())
    val imageView: ImageView = view[1] as ImageView
    imageView.setImageDrawable(view.context.getDrawable(R.drawable.account_circle_24dp))

    profilePicture.let { picture ->
        if (picture != null) {
            val rounded = RoundedBitmapDrawableFactory.create(view.resources, picture)
            rounded.isCircular = true
            imageView.setImageDrawable(rounded)
            view.showNext()
        } else {
            view.showNext()
        }
    }

3 个答案:

答案 0 :(得分:3)

定义LiveData时,即使其类型不可为空,其初始值也将为空:

val downloadPictureResult = MutableLiveData<Bitmap>()
// Here, downloadPictureResult.value is null

在您的情况下,LiveData返回的downloadProfilePic()的值为空,直到下载图片为止,该值将在回调addOnCompleteListener中异步发生:

fun downloadProfilePic(): LiveData<Bitmap?> {
    ...
    imageRef.getBytes(ONE_MEGABYTE).addOnCompleteListener { task ->
        ...
        // This happens asynchronously, likely after downloadProfilePic()
        // has already returned
        downloadPictureResult.value =  responseBitmap
        ...
    }

    return downloadPictureResult;
}

这就是为什么传递给适配器的第一个值为null的原因,因为downloadPictureResult.value首次返回downloadPictureResultdownloadProfilePic()仍然为null。

答案 1 :(得分:1)

让我们逐步了解关于LiveData在您的情况下的工作方式(通常)

  1. 您的方法downloadProfilePic()返回可空LiveData Bitmap作为返回类型。方法包含addOnCompleteListener异步代码,这意味着即使返回了LiveData值,它的执行也会发生。

这里:

fun downloadProfilePic(): LiveData<Bitmap?> {
    val imageRef = storage.getReference("images/$uid/profile/profile_picture.jpg")
    imageRef.getBytes(ONE_MEGABYTE).addOnCompleteListener { task ->
        if (task.isSuccessful) {
            //...
            downloadPictureResult.value =  responseBitmap
            //...
        } else {
            downloadPictureResult.value = null
            Log.d(TAG, task.exception?.localizedMessage)
        }
    }
    return downloadPictureResult;
}
  1. 作为方法的结果,您将返回downloadPictureResult,该方法已全局初始化为val downloadPictureResult = MutableLiveData<Bitmap>()可变活数据,以便我们以后可以对其进行修改。发生在addOnCompleteListener的回调中。

  2. 现在大概是,当视图(Activity/Fragment)被加载,因此您已经将ViewModel中的实时数据添加为 DataBinding 时,它将获得初始值在LiveData中,然后进一步观察。

  3. 因此,在此,您已在代码LiveData期间用Bitmap空值初始化了val downloadPictureResult = MutableLiveData<Bitmap>(),它返回了空值< / strong>,这是第一次。

  4. 然后从addOnCompleteListener方法进行回调,并在发生错误时最终为livedata分配值downloadPictureResult.value = responseBitmap null

  

这就是为什么在您的LiveData方法BindingAdapter中有两个对setProfilePicture()的调用。

注意:如果您不希望两个回调使您的回调是同步的而不是异步的,则可以执行简单的操作。

答案 2 :(得分:1)

正如其他答案告诉您的那样,第一个值将始终为null。但是,当您准备好使用观察器获取值时,然后在拥有图像后就可以取消订阅。

val pic = downloadProfilePic()
pic.observe(owner, object : Observer<BitMap?> {
    override fun onChanged(b: BitMap?){
        if(b != null){
            setProfilePicture(yourView, b)
            pic.removeObserver(this)
        }
    }
})