Home > Net >  Can a Kotlin entity with a constructor that has default non null values become null at runtime?
Can a Kotlin entity with a constructor that has default non null values become null at runtime?

Time:08-30

I have spent the last couple of weeks trying to rewrite an android app from a java to kotlin, from custom fragment navigation to navigation component and all other Jetpack bells and whistles.

Now I've encountered several bugs through this process but there's this specific one. I have a kotlin class with a default constructor as shown below

@Entity(tableName = Globals.FIREBASE_ITEM_NODE)
@Parcelize
class Item(
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0 ,
    var imageUri: String = "",
    var isRead: Boolean = false,
    var expanded: Boolean = false,
    var favourite: Boolean = false,
    var isSaved: Boolean = false,
    var englishWord: String = "",
    var topic: String = "",
    var audioUri: String = "",
    var rutooroWord: String = "",
    var firebaseImageNode: String = "",
) : Parcelable

This is because I fetch data from firebase rtdb and cache it in room. I then collect a flow of this data submit it to a List Adapter and use databinding to bind it to my views.

the item Viewholder

    inner class ItemViewHolder(private val b: RvLangItemBinding) : RecyclerView.ViewHolder(b.root) {
        fun bind(position: Int) {
            if (getItem(position) !is Item) return
            b.item = getItem(position) as Item

            if ((getItem(position) as Item).expanded) createPalette(
                (getItem(position) as Item).imageUri,
                b.parent,
                b.tvEnglish,
                b.tvRutooro
            )
            else b.parent.setBackgroundColor(
                itemView.context.resources.getColor(
                    R.color.transparent,
                    itemView.context.theme
                )
            )

            b.root.setOnClickListener {
                (getItem(position) as Item).expanded = !(getItem(position) as Item).expanded
                notifyItemChanged(position)
                if (prevPosition != INITIAL_POSITION && prevPosition != position) {
                    (getItem(prevPosition) as Item).expanded = false
                    notifyItemChanged(prevPosition)
                }
                prevPosition = position
                if ((getItem(position) as Item).expanded && (getItem(position) as Item).audioUri.isNotEmpty())
                    AudioUtil.playAudioFile(
                        itemView.context,
                        Uri.parse((getItem(position) as Item).audioUri)
                    )
            }

            b.favourite.setOnLikeListener(object : OnLikeListener {
                override fun liked(likeButton: LikeButton) {
                    (getItem(position) as Item).favourite = true
                    onItem.update(getItem(position) as Item)
                }

                override fun unLiked(likeButton: LikeButton) {
                    (getItem(position) as Item).favourite = false
                    onItem.update(getItem(position) as Item)
                }
            })

            b.audioButton.setOnClickListener {
                if ((getItem(position) as Item).audioUri.isNotEmpty())
                    AudioUtil.playAudioFile(
                        itemView.context,
                        Uri.parse((getItem(position) as Item).audioUri)
                    )
            }
        }
    }

and this is the xml for the item

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="android.view.View" />

        <variable
            name="item"
            type="com.allez.san.learnrutooro.models.Item" />
    </data>

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="18dp"
        android:layout_marginTop="12dp"
        android:layout_marginEnd="18dp"
        android:elevation="4dp"
        android:padding="8dp"
        app:cardCornerRadius="4dp">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@ id/parent"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <com.google.android.material.textview.MaterialTextView
                android:id="@ id/tv_english"
                style="@style/TextAppearance.Material3.TitleMedium"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="16dp"
                android:text="@{item.englishWord}"
                android:textSize="16sp"
                app:layout_constraintEnd_toStartOf="@id/favourite"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                tools:text="Good Morning" />

            <com.like.LikeButton
                android:id="@ id/favourite"
                android:layout_width="32dp"
                android:layout_height="32dp"
                android:layout_marginEnd="4dp"
                app:icon_size="25dp"
                app:icon_type="star"
                app:liked="@{item.favourite}"
                app:layout_constraintBottom_toBottomOf="@ id/tv_english"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="@ id/tv_english"
                app:like_drawable="@drawable/ic_star_green"
                app:unlike_drawable="@drawable/ic_star_white" />


            <RelativeLayout
                android:id="@ id/relativeLayout"
                android:layout_width="0dp"
                android:layout_height="1dp"
                android:layout_marginStart="8dp"
                android:layout_marginEnd="4dp"
                android:background="@android:color/darker_gray"
                app:layout_constraintBottom_toBottomOf="@id/downArrow"
                app:layout_constraintEnd_toStartOf="@id/downArrow"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="@id/downArrow" />

            <com.google.android.material.imageview.ShapeableImageView
                android:id="@ id/downArrow"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="8dp"
                android:src="@drawable/ic_arrow_drop_down"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@id/favourite" />


            <com.google.android.material.textview.MaterialTextView
                android:id="@ id/tv_rutooro"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginBottom="16dp"
                android:text="@{item.rutooroWord}"
                android:textSize="16sp"
                android:visibility="@{item.expanded? View.VISIBLE:View.GONE}"
                app:layout_constraintBottom_toTopOf="@id/item_image"
                app:layout_goneMarginBottom="16dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/downArrow"
                tools:text="oraire ota" />

            <com.google.android.material.imageview.ShapeableImageView
                android:id="@ id/item_image"
                setImage="@{item.imageUri}"
                setImageItemVisibility="@{item}"
                android:layout_width="270dp"
                android:layout_height="200dp"
                android:layout_marginStart="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginBottom="16dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/tv_rutooro"
                app:layout_goneMarginBottom="16dp"
                tools:src="@drawable/lr_logo_light" />

            <com.google.android.material.imageview.ShapeableImageView
                android:id="@ id/audio_button"
                android:layout_width="wrap_content"
                android:layout_height="32dp"
                android:layout_marginBottom="16dp"
                android:layout_marginEnd="16dp"
                android:src="@drawable/ic_audio"
                android:visibility="@{item.expanded? View.VISIBLE:View.GONE}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@id/downArrow"
                app:layout_constraintVertical_bias="0.0" />
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.cardview.widget.CardView>
</layout>

and these are my binding adapters

@BindingAdapter("setImage")
fun setImage(view:ImageView, uri: String)=
      Glide.with(view).load(uri).into(view)


@BindingAdapter("setImageItemVisibility")
fun setItemImageVisibility(view:ImageView, item: Item){
    view.visibility = if(item.expanded && item.imageUri.isNotEmpty()) View.VISIBLE else View.GONE
}

and this is the error I've been getting.

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.allez.san.myapplication, PID: 26721
    java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter item
        at com.allez.san.learnrutooro.utils.BindingUtilsKt.setItemImageVisibility(Unknown Source:7)
        at com.allez.san.learnrutooro.databinding.RvLangItemBindingImpl.executeBindings(RvLangItemBindingImpl.java:152)
        at androidx.databinding.ViewDataBinding.executeBindingsInternal(ViewDataBinding.java:512)
        at androidx.databinding.ViewDataBinding.executePendingBindings(ViewDataBinding.java:484)
        at androidx.databinding.ViewDataBinding$7.run(ViewDataBinding.java:218)
        at androidx.databinding.ViewDataBinding$8.doFrame(ViewDataBinding.java:320)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1106)
        at android.view.Choreographer.doCallbacks(Choreographer.java:866)
        at android.view.Choreographer.doFrame(Choreographer.java:792)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1092)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:226)
        at android.os.Looper.loop(Looper.java:313)
        at android.app.ActivityThread.main(ActivityThread.java:8669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
I/Process: Sending signal. PID: 26721 SIG: 9

How is this possible and why??? I've been at it for a while now. reading as much as I could about the subject but no luck yet. Any help will be appreciated. thanx in advance.

CodePudding user response:

try to change the fields type to nullable and run it again, if you did not have the same error then the problem is that you defined entity all its fields are non-nullable, so when you are calling @Query("SELECT * FROM items") fun getAllItems(): Flow<List<Item>> you are trying to give a null value from database to one of the fields.

CodePudding user response:

I think the problem was with my binding adapters

@BindingAdapter("setImage")
fun setImage(view: ImageView, uri: String) =
    Glide.with(view).load(uri).into(view)


@BindingAdapter("setImageItemVisibility")
fun setItemImageVisibility(view: ImageView, item: Item) {
    view.visibility = if (item.expanded && item.imageUri.isNotEmpty()) View.VISIBLE else View.GONE
}

Because when i switched to directly binding the image and setting the image visibility in the item viewholder everything is working just fine.

Glide.with(itemView).load((getItem(position) as Item).imageUri).into(b.itemImage)
b.itemImage.visibility = if ((getItem(position) as Item).expanded && (getItem(position) as Item).imageUri.isNotEmpty()) View.VISIBLE else View.GONE
            

I don't think this is a solution to that error coz I still don't know why I was getting it and I'd like to still use databinding to set the image and its visibility. If anyone has an explanation as to why this was happening I'd really appreciate it.

  • Related