MVVM: ViewModel является нулевым при попытке загрузить данные с использованием LiveData - PullRequest
0 голосов
/ 28 февраля 2020

Поэтому я пытаюсь создать простой интерфейс MVVM для загрузки поездок из базы данных, полученной с помощью ViewModel, в мой TripFragment. Тем не менее, я получаю эту ошибку, говоря, что мой TripViewModel имеет значение null:

Attempt to invoke virtual method 'void androidx.lifecycle.LiveData.observe(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Observer)' on a null object reference

Я не могу понять, почему он считает, что он является нулевым. Я считаю, что проблема в TripViewModel и имеет какое-то отношение к тому факту, что он наследуется от AndroidViewModel и что я передаю контекст приложения в конструкторе.

класс TripFragment: Fragment ()

private var tripViewModel: TripViewModel? = null
private var textViewTripName: TextView? = null

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val view =
        inflater.inflate(R.layout.fragment_trip, container, false)

    val recyclerView: RecyclerView = view.recycler_view
    recyclerView.layoutManager = LinearLayoutManager(context)
    recyclerView.setHasFixedSize(true)

    val adapter = TripAdapter()
    recyclerView.adapter = adapter

    tripViewModel = ViewModelProvider(this).get(TripViewModel(activity!!.application)::class.java)


    // This is the line where it crashes, it never executes past this
    tripViewModel!!.getAll().observe(viewLifecycleOwner, Observer<List<Trip>> {
        fun onChanged(trips: List<Trip>) {
            adapter.setTrips(trips)
            Log.d("TripFragment", "Went through observer")
        }
    })

    return view
}

класс TripViewModel (приложение: приложение): AndroidViewModel (приложение)

private var tripRepository: TripRepository = TripRepository(application)
private var allTrips: LiveData<List<Trip>> = getAll()

fun insert(trip: Trip) {
    tripRepository.insert(trip)
}

fun update(trip: Trip) {
    tripRepository.update(trip)
}

fun delete(trip: Trip) {
    tripRepository.delete(trip)
}

fun clear() {
    tripRepository.clear()
}

fun getAll(): LiveData<List<Trip>> {
    return allTrips
}

класс TripRepository (приложение: приложение)

private lateinit var tripDao: TripDao
private lateinit var allTrips: LiveData<List<Trip>>

init {
    CoroutineScope(IO).launch {
        tripDao = AppDatabase.getDatabase(application.applicationContext).tripDao()
        allTrips = tripDao.getAll()
    }
}

fun insert(trip: Trip) {
    CoroutineScope(IO).launch {
        tripDao.insert(trip)
    }
}

fun update(trip: Trip) {
    CoroutineScope(IO).launch {
        tripDao.update(trip)
    }
}

fun delete(trip: Trip) {
    CoroutineScope(IO).launch {
        tripDao.delete(trip)
    }
}

fun clear() {
    CoroutineScope(IO).launch {
        tripDao.clear()
    }
}

fun getAll(): LiveData<List<Trip>> {
    return allTrips
}

сущность отключения

@Entity
data class Trip(var title: String, var startDate: String, var endDate: String?) {

    @PrimaryKey(autoGenerate = true)
    var tid: Long = 0
}

РЕДАКТИРОВАТЬ: я напечатал несколько журналов отладки и указал на ошибку в этой строке в TripRepository.

init {
    CoroutineScope(IO).launch {
        // tripDao is never assigned properly, 
        tripDao = AppDatabase.getDatabase(application.applicationContext).tripDao()
        allTrips = tripDao.getAll()
    }
}

Строка tripDao = AppDatabase.getDatabase(application.applicationContext).tripDao() вызывает ошибку, которая превращает tripDao в нулевую переменную , Проблема связана с тем, как я выбираю базу данных, поэтому я прикрепил свой класс AppDatabase ниже.

@Database(entities = [Trip::class], version = 2, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {

    abstract fun tripDao(): TripDao

    companion object {
        // Singleton prevents multiple instances of database opening at the
        // same time.
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                Log.d("AppDatabase", "Returning existing database")
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "tripweaver_database"
                )
                    .fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                Log.d("AppDatabase", "Returning new database")
                return instance
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 01 марта 2020

Мне не удалось найти решение для исправления кода доступа к моей базе данных, поэтому я реструктурировал всю свою кодовую базу в соответствии с учебным пособием по компонентам архитектуры от Google на Codelabs. Конечно же, это помогло мне решить проблемы, которые у меня были. Ссылка для тех, кому нужен этот учебник в будущем: https://codelabs.developers.google.com/codelabs/android-room-with-a-view-kotlin/#13

0 голосов
/ 28 февраля 2020

В вашем классе TripViewModel у вас есть:

private var allTrips: LiveData<List<Trip>> = getAll()

fun getAll(): LiveData<List<Trip>> {
    return allTrips
}

Итак, у вас есть круговое расположение здесь. allTrips присваивается начальное значение того, что возвращает getAll(), но он вызывает getAll() до назначения allTrips. Итак, вы нашли способ присвоить нуль ненулевому значению!

Похоже, вы хотели поставить allTrips = tripRepository.getAll().

...