iknow
iknow

Reputation: 9862

Database query throws KotlinNullPointerException while trying get LiveData

I am trying to create room database in kotlin/android but I am getting KotlinNullPointerException when I try te get LiveData from the database. I have two tables Car and Refueling and from one fragment I can get Car data without any problems but in a second fragment, I get errors while trying to get Refueling LiveData. I couldn't find any differences between these two fragments/DAO. Below is a simplified code.

Refueling data class:

@Entity(tableName = "refueling_table", foreignKeys = [ForeignKey(entity = Car::class,
    parentColumns = arrayOf("carID"),
    childColumns = arrayOf("carID"),
    onDelete = ForeignKey.CASCADE)]
)
data class Refueling(
    @PrimaryKey(autoGenerate = true)
    val refuelingID: Long = 0L,

    @ColumnInfo(name = "carID")
    val carID: Long = 0L,
)

RefuelingDAO:

@Dao
interface RefuelingDAO
{
    @Query("SELECT * FROM refueling_table ORDER BY refuelingID DESC")
    fun getAll(): LiveData<List<Refueling>>
}

App Database:

@Database(entities = [Car::class, Refueling::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase()
{
    abstract val carDAO: CarDAO
    abstract val refuelingDAO: RefuelingDAO

    companion object
    {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase
        {
            synchronized(this)
            {
                var instance = INSTANCE
                if (instance == null)
                {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        "app_database"
                    ).fallbackToDestructiveMigration().build()
                    INSTANCE = instance
                }
                return instance
            }
        }
    }
}

Creating ViewModel in fragment:

val arguments = CarFragmentArgs.fromBundle(requireArguments())
val application = requireNotNull(this.activity).application
val dataSource = AppDatabase.getInstance(application).refuelingDAO
val viewModelFactory = CarFragmentVMFactory(dataSource, arguments.carID, application)

viewModel = ViewModelProvider(this, viewModelFactory).get(CarFragmentVM::class.java)

binding.carViewModel = viewModel
binding.lifecycleOwner = this
binding.buttonTest.setOnClickListener {
    viewModel.listAll()
}

ViewModel:

class CarFragmentVM(
    private val database: RefuelingDAO,
    private val carID: Long,
    application: Application
) :
        AndroidViewModel(application)
{
    val refueling = database.getAll()
   
    fun listAll()
    {
        refueling.value!!.forEach { \\ IN THIS PLACE I GET ERROR
            Log.d(it)
        }
    }
}

When I click on the 'buttonTest' I got KotlinNullPointerException. As I said I have the same code in another fragment (I just can't find any differences) and it works fine but this doesn't. Can anyone see where is my problem or give any clue where should I look for it?

Logcat:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.fuelmanager, PID: 22828
    kotlin.KotlinNullPointerException
        at com.example.fuelmanager.ui.car.CarFragmentVM.listAll(CarFragmentVM.kt:44)
        at com.example.fuelmanager.ui.car.CarFragment$onCreateView$1.onClick(CarFragment.kt:44)
        at android.view.View.performClick(View.java:7125)
        at android.view.View.performClickInternal(View.java:7102)
        at android.view.View.access$3500(View.java:801)
        at android.view.View$PerformClick.run(View.java:27336)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

Line 44 is

refueling.value!!.forEach { \\ IN THIS PLACE I GET ERROR
            Log.d(it)
        }

Upvotes: 3

Views: 279

Answers (1)

CommonsWare
CommonsWare

Reputation: 1006604

getAll() is returning a LiveData. The actual database query is being done on a background thread. That will take some time to complete. In the meantime, value is null. You, however, are assuming that it will complete immediately. So, you try using value immediately, and you crash with a NullPointerException.

Please observe() the LiveData.

Upvotes: 5

Related Questions