Сбой приложения при переходе к фрагменту, который использует базу данных RecyclerView и Room с LiveData - PullRequest
0 голосов
/ 24 апреля 2020

В настоящее время я создаю приложение с основным действием, которое содержит navHostFragment и 3 фрагмента, которые связаны нижней панелью навигации. Моя цель - чтобы каждый фрагмент использовал вид Recycler. Я использую базу данных Room с адаптером и LiveData для «данных». Когда я запускаю приложение, я хочу go к фрагменту Wallet и видеть вертикальный список textViews. Поскольку я новичок и только начинаю с этого, я просто хотел создать очень простую базу данных, содержащую только текст, а затем выложить ее в вертикальном формате. Ничего особенного пока нет. Любая помощь будет принята с благодарностью.

MainActivity

package com.example.android.pointmax


import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import timber.log.Timber

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Plant tree to enable Debugging with Timber
        Timber.plant(Timber.DebugTree())

        // Find the bottomNavigation bar
        val navView: BottomNavigationView = findViewById(R.id.nav_view)

        // Find the fragment that will host the different fragments
        val navController = findNavController(R.id.nav_host_fragment)

        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        val appBarConfiguration = AppBarConfiguration(
            setOf(
                R.id.navigation_home, R.id.navigation_wallet, R.id.navigation_recommended
            )
        )
        setupActionBarWithNavController(navController, appBarConfiguration)
        navView.setupWithNavController(navController)
    }

}

WalletFragment

package com.example.android.pointmax.ui.wallet

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.CardAdapter
import com.example.android.pointmax.R

class WalletFragment : Fragment() {
    private lateinit var viewManager: RecyclerView.LayoutManager
    private lateinit var viewAdapter: RecyclerView.Adapter<*>
    private lateinit var recyclerView: RecyclerView
    private lateinit var viewModel: WalletViewModel

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        val rootView = inflater.inflate(R.layout.fragment_wallet, container, false)
        recyclerView = rootView.findViewById(R.id.wallet_recyclerview)

        viewModel = ViewModelProvider(
            this
        ).get(WalletViewModel::class.java)

        val linearLayoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
        viewManager = linearLayoutManager

        // Observe the ViewModel
        viewModel.allCards.observe(viewLifecycleOwner, Observer { cards ->
            viewAdapter = CardAdapter(cards)
        })
        return rootView
    }
}

WalletViewModel

package com.example.android.pointmax.ui.wallet

import android.app.Application
import androidx.lifecycle.*
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CardRepository
import com.example.android.pointmax.database.CardRoomDatabase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class WalletViewModel(application: Application) : AndroidViewModel(application) {
    private val repository: CardRepository
    // Using LiveData and caching what getAlphabetizedWords returns has several benefits:
    // - We can put an observer on the data (instead of polling for changes) and only update the
    //   the UI when the data actually changes.
    // - Repository is completely separated from the UI through the ViewModel.
    val allCards: LiveData<List<Card>>

    init {
        val cardsDao = CardRoomDatabase.getDatabase(application, viewModelScope).cardDao()
        repository = CardRepository(cardsDao)
        allCards = repository.allCards
    }

    /**
     * Launching a new coroutine to insert the data in a non-blocking way
     */
    fun insert(card: Card) = viewModelScope.launch(Dispatchers.IO) {
        repository.insert(card)
    }
}

CardAdapter

package com.example.android.pointmax

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.database.Card


class CardAdapter internal constructor(
    private var cards: List<Card>
) : RecyclerView.Adapter<CardAdapter.CardViewHolder>() {

    inner class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val cardItemView: TextView = itemView.findViewById(R.id.textView)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
        val itemView = LayoutInflater.from(parent.context)
            .inflate(R.layout.recyclerview_item, parent, false)
        return CardViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
        val current = cards[position]
        holder.cardItemView.text = current.toString()
    }

    internal fun setWords(cards: List<Card>) {
        this.cards = cards
        notifyDataSetChanged()
    }

    override fun getItemCount() = cards.size
}

CardRepository

package com.example.android.pointmax.database

import androidx.lifecycle.LiveData

// Declares the DAO as a private property in the constructor. Pass in the DAO
// instead of the whole database, because you only need access to the DAO
class CardRepository(private val cardDao: CardDao) {

    // Room executes all queries on a separate thread.
    // Observed LiveData will notify the observer when the data has changed.
    val allCards: LiveData<List<Card>> = cardDao.getCards()

    suspend fun insert(card: Card) {
        cardDao.insert(card)
    }
}

CardRoomDatabase

package com.example.android.pointmax.database

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch


// Annotates class to be a Room Database with a table (entity) of the Word class
@Database(entities = arrayOf(Card::class), version = 1, exportSchema = false)
public abstract class CardRoomDatabase : RoomDatabase() {

    abstract fun cardDao(): CardDao

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

        fun getDatabase(
            context: Context,
            scope: CoroutineScope
        ): CardRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    CardRoomDatabase::class.java,
                    "card_database"
                ).addCallback(CardDatabaseCallback(scope)).build()
                INSTANCE = instance
                return instance
            }
        }

        private class CardDatabaseCallback(
            private val scope: CoroutineScope
        ) : RoomDatabase.Callback() {

            override fun onOpen(db: SupportSQLiteDatabase) {
                super.onOpen(db)
                INSTANCE?.let { database ->
                    scope.launch {
                        populateDatabase(database.cardDao())
                    }
                }
            }

            suspend fun populateDatabase(cardDao: CardDao) {
                // Delete all content here.
                cardDao.deleteAll()

                // Add sample words.
                var card = Card("Petal Credit Card")
                cardDao.insert(card)
                card = Card("Discover IT")
                cardDao.insert(card)
            }
        }
    }
}

Карта

package com.example.android.pointmax.database

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "card_table")
data class Card(
    @PrimaryKey
    @ColumnInfo(name = "cardName")
    var card: String
)

CardDao

package com.example.android.pointmax.database

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query


@Dao
interface CardDao {

    @Query("SELECT * from card_table")
    fun getCards(): LiveData<List<Card>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(card: Card)

    @Query("DELETE FROM card_table")
    suspend fun deleteAll()
}

Я могу запустить приложение, но как только Я go к фрагменту кошелька, приложение вылетает со следующим:

2020-04-23 19: 24: 18.676 5048-5048 / com.example. android .pointmax E / AndroidRuntime: FATAL EXCEPTION: main Процесс: com.example. android .pointmax, PID: 5048 java .lang.RuntimeException: Невозможно создать экземпляр класса com.example. android .pointmax.ui.wallet.WalletViewModel в androidx.lifecycle.ViewModelProvider $ AndroidViewModelFactory.create (ViewModelProvider. java: 275) в androidx.lifecycle.SavedStateViewModelFactory.create (SavedStateViewModelFactory. java : 106) в androidx.lifecycle.ViewModelProvider.get (ViewModelProvider. java: 185) в androidx.lifecycle.ViewModelProvider.get (ViewModelProvider. java: 150) в com.example. android .pointmax.ui. wallet.WalletFragment.onCreateView (WalletFragment.kt: 31) в androidx.fragment.app.Fragment.performCreateView (Fragment. java: 2698) в androidx.fragment.app.FragmentStateManager.createView (FragmentStateManager. java 320): на androidx.fragment.app.FragmentManager.moveToState (FragmentManager. java: 1187) на androidx.fragment.app.FragmentManager.addAddedFragments (FragmentManager. java: 2224) на androidx.fragment.app.FragmentManagerMo . java: 1997) в androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute (FragmentManager. java: 1953) в androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager. java: 1849ragment) в android app.FragmentManager $ 4.run (FragmentManager. java: 413) в android .os.Handler.handleCallback (Обработчик. java: 883) в * 10 69 * .os.Handler.dispatchMessage (Обработчик. java: 100) в android .os.Looper.l oop (Looper. java: 214) в android .app.ActivityThread.main ( ActivityThread. java: 7356) в java .lang.reflect.Method.invoke (собственный метод) в com. android .internal.os.RuntimeInit $ MethodAndArgsCaller.run (RuntimeInit. java: 492) в com. android .internal.os.ZygoteInit.main (ZygoteInit. java: 930) Вызывается: java .lang.reflect.InvocationTargetException в java .lang.reflect.Constructor.newInstance0 (собственный метод) в java .lang.reflect.Constructor.newInstance (Constructor. java: 343) в androidx.lifecycle.ViewModelProvider $ AndroidViewModelFactory.create (ViewModelProvider. java: 267) в androidx.lifecycle.SavedSactFateFateSateFateSateFateSateFateSateMatelateFrateStateMtateCateFateSateFrateStateMtateCateFateSateFrateStateMtateCateFateSateFrateStateMtateCateFateSateFateStateMateCateFateStateMateFateStateMateCateMateCateMateCateMateCodeMed . java: 106) в androidx.lifecycle.ViewModelProvider.get (ViewModelProvider. java: 185) в androidx.lifecycle.ViewModelProvider.get (ViewModelProvider. java: 150) в com.example. android. pointmax.ui.wallet.WalletFragment.onCreateView (WalletFragment.kt: 31) в androidx .fragment.app.Fragment.performCreateView (Fragment. java: 2698) в androidx.fragment.app. FragmentStateManager.createView (FragmentStateManager. java: 320) в androidx.fragment.app.FragmentManager.moveToState (FragmentManager. java: 1187) в androidx.fragment.app.FragmentManager.addAddedFragments (FragmentManager * 2224): 2224. в androidx.fragment.app.FragmentManager.executeOpsTogether (FragmentManager. java: 1997) в androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute (FragmentManager. java: 1953) в androidx.fragmentManagerFanager.exe) ( . java: 1849) в androidx.fragment.app.FragmentManager $ 4.run (FragmentManager. java: 413) в android .os.Handler.handleCallback (Обработчик. java: 883) в android .os.Handler.dispatchMessage (Обработчик. java: 100) в android .os.Looper.l oop (Looper. java: 214) в android .app.ActivityThread.main (ActivityThread. java: 7356) в java .lang.reflect.Method.invoke (собственный метод) в com. android .internal.os.RuntimeInit $ MethodAndArgsCaller.run (RuntimeInit. java: 492) в com. android .internal.os.ZygoteInit.main (Zygo teInit. java: 930) Причина: java .lang.RuntimeException: не удается найти реализацию для com.example. android .pointmax.database.CardRoomDatabase. CardRoomDatabase_Impl не существует в androidx.room.Room.getGeneratedImplementation (Room. java: 94) в androidx.room.RoomDatabase $ Builder.build (RoomDatabase. java: 952) в com.example. android .pointmax .database.CardRoomDatabase $ Companion.getDatabase (CardRoomDatabase.kt: 37) в com.example. android .pointmax.ui.wallet.WalletViewModel. (WalletViewModel.kt: 20)

1 Ответ

0 голосов
/ 25 апреля 2020

Самая важная часть вашей трассировки стека находится здесь

`Caused by: java.lang.RuntimeException: cannot find implementation for com.example.android.pointmax.database.CardRoomDatabase. CardRoomDatabase_Impl does not exist at androidx.room.Room.getGeneratedImplementation(Room.java:94) at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:952) at com.example.android.pointmax.database.CardRoomDatabase$Companion.getDatabase(CardRoomDatabase.kt:37) at com.example.android.pointmax.ui.wallet.WalletViewModel.(WalletViewModel.kt:20)`

Таким образом, проблема в том, что Room не может сгенерировать класс CardRoomDatabase_Impl (реализация вашего абстрактного класса CardRoomDatabase). Поскольку вы правильно используете аннотацию Room, единственная причина вашей проблемы, которую я могу догадаться, - вы не включили процессор аннотаций в свой build.gradle (уровень приложения). Проверьте, находится ли он в разделе зависимостей:

kapt "androidx.room:room-compiler:2.2.5"
...