导航到使用RecyclerView and Room数据库和LiveData的片段时,应用程序崩溃

问题描述 投票:0回答:1

我目前正在构建一个具有主要活动的应用程序,该应用程序托管一个navHostFragment,以及3个通过底部导航栏连接的片段。我的目标是让每个片段都使用“回收者视图”。我正在使用带有适配器和LiveData的Room数据库作为“数据”。启动应用程序时,我想转到电子钱包片段,并查看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()
}

我可以启动应用程序,但是一旦我进入电子钱包片段,应用程序就会崩溃,并显示以下内容:

2020-04-23 19:24:18.676 5048-5048 / com.example.android.pointmax E / AndroidRuntime:FATAL EXCEPTION:main流程:com.example.android.pointmax,PID:5048java.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.FragmentManager.executeOpsTogether(FragmentManager.java:1997)在androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)在androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)在androidx.fragment.app.FragmentManager $ 4.run(FragmentManager.java:413)在android.os.Handler.handleCallback(Handler.java:883)在android.os.Handler.dispatchMessage(Handler.java:100)在android.os.Looper.loop(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.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.FragmentManager.executeOpsTogether(FragmentManager.java:1997)在androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)在androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)在androidx.fragment.app.FragmentManager $ 4.run(FragmentManager.java:413)在android.os.Handler.handleCallback(Handler.java:883)在android.os.Handler.dispatchMessage(Handler.java:100)在android.os.Looper.loop(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.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)

kotlin mvvm android-recyclerview viewmodel android-livedata
1个回答
0
投票

堆栈跟踪的最重要部分在这里

`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(应用程序级别)中包含注解处理器。检查它是否在dependencies-section中:

kapt "androidx.room:room-compiler:2.2.5"
© www.soinside.com 2019 - 2024. All rights reserved.