Reputation: 9862
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
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