17

Here is my code

class BookmarkViewModel(app: Application) : AndroidViewModel(app) {

    private val dao = BookmarkDb.get(app).bookmarkDao()

    companion object {
        private const val PAGE_SIZE = PagingConstants.PERPAGE

        /**
         * If placeholders are enabled, PagedList will report the full size but some items might
         * be null in onBind method (PagedListAdapter triggers a rebind when data is loaded).
         * <p>
         * If placeholders are disabled, onBind will never receive null but as more pages are
         * loaded, the scrollbars will jitter as new pages are loaded. You should probably disable
         * scrollbars if you disable placeholders.
         */
        private const val ENABLE_PLACEHOLDERS = true
    }

        val allBookmarks = LivePagedListBuilder(dao.allBookmarkByDatetime(), PagedList.Config.Builder()
                    .setPageSize(PAGE_SIZE)
                    .setEnablePlaceholders(ENABLE_PLACEHOLDERS)
                    .build()).build()

    fun insert(title: String, description: String, datetime: String) = ioThread {
        dao.insert(Bookmark(id = 0, title = title, description = description, datetime = datetime))
    }

    fun remove(bookmark: Bookmark) = ioThread {
        dao.delete(bookmark)
    }
}

This is from the google samples.. After I want to:

class BookmarkListFragment : FirstFragment() {

private var viewModel: BookmarkViewModel? = null
..

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        viewModel = ViewModelProviders.of(activity!!).get(BookmarkViewModel::class.java)
...

And here is the problem:

java.lang.RuntimeException: Cannot create an instance of class com.lacas.db.room.BookmarkViewModel

Can I use this in a fragment?

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.lacas.asd/com.lacas.asd.ui.activities.testtabs.TestTab2Activity}: java.lang.RuntimeException: Cannot create an instance of class com.lacas.asd.db.room.BookmarkViewModel at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2924) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2985) at android.app.ActivityThread.-wrap14(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1635) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6692) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358) Caused by: java.lang.RuntimeException: Cannot create an instance of class com.lacas.asd.db.room.BookmarkViewModel at android.arch.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:207) at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:134) at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:102) at com.lacas.asd.ui.activities.testtabs.TestTab2Activity$viewModel$2.invoke(TestTab2Activity.kt:34) at com.lacas.asd.ui.activities.testtabs.TestTab2Activity$viewModel$2.invoke(TestTab2Activity.kt:29) at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:154) at com.lacas.asd.ui.activities.testtabs.TestTab2Activity.getViewModel(TestTab2Activity.kt) at com.lacas.asd.ui.activities.testtabs.TestTab2Activity.onCreated(TestTab2Activity.kt:45) at com.lacas.asd.base.BasePermissionsActivity.onCreate(BasePermissionsActivity.kt:34) at android.app.Activity.performCreate(Activity.java:6912) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2877) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2985)  at android.app.ActivityThread.-wrap14(ActivityThread.java)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1635)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:154)  at android.app.ActivityThread.main(ActivityThread.java:6692)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)  Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Constructor.newInstance0(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:430) at android.arch.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:199) at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:134)  at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:102)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity$viewModel$2.invoke(TestTab2Activity.kt:34)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity$viewModel$2.invoke(TestTab2Activity.kt:29)  at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:154)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity.getViewModel(TestTab2Activity.kt)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity.onCreated(TestTab2Activity.kt:45)  at com.lacas.asd.base.BasePermissionsActivity.onCreate(BasePermissionsActivity.kt:34)  at android.app.Activity.performCreate(Activity.java:6912)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2877)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2985)  at android.app.ActivityThread.-wrap14(ActivityThread.java)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1635)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:154)  at android.app.ActivityThread.main(ActivityThread.java:6692)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358)  Caused by: java.lang.RuntimeException: cannot find implementation for com.lacas.asd.db.room.BookmarkDb. BookmarkDb_Impl does not exist at android.arch.persistence.room.Room.getGeneratedImplementation(Room.java:93) at android.arch.persistence.room.RoomDatabase$Builder.build(RoomDatabase.java:630) at com.lacas.asd.db.room.BookmarkDb$Companion.get(BookmarkDb.kt:29) at com.lacas.asd.db.room.BookmarkViewModel.(BookmarkViewModel.kt:14) at java.lang.reflect.Constructor.newInstance0(Native Method)  at java.lang.reflect.Constructor.newInstance(Constructor.java:430)  at android.arch.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:199)  at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:134)  at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:102)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity$viewModel$2.invoke(TestTab2Activity.kt:34)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity$viewModel$2.invoke(TestTab2Activity.kt:29)  at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:154)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity.getViewModel(TestTab2Activity.kt)  at com.lacas.asd.ui.activities.testtabs.TestTab2Activity.onCreated(TestTab2Activity.kt:45)  at com.lacas.asd.base.BasePermissionsActivity.onCreate(BasePermissionsActivity.kt:34)  at android.app.Activity.performCreate(Activity.java:6912)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2877)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2985)  at android.app.ActivityThread.-wrap14(ActivityThread.java)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1635)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:154)  at android.app.ActivityThread.main(ActivityThread.java:6692)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358) 

Mosius
  • 1,602
  • 23
  • 32
lacas
  • 13,928
  • 30
  • 109
  • 183

11 Answers11

14

As somebody said here:

Android room persistent: AppDatabase_Impl does not exist

the solution was:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"

implementation "androidx.room:room-runtime:$roomVersion"
kapt "androidx.room:room-compiler:$roomVersion"


implementation "androidx.paging:paging-runtime:$paging_version"
    
Horrorgoogle
  • 7,858
  • 11
  • 48
  • 81
lacas
  • 13,928
  • 30
  • 109
  • 183
  • 2
    In the most recent versions of Android Studio, adding `kapt` results in the build error: "Cause: app: Original kapt is deprecated." so you need to add `apply plugin: 'kotlin-kapt'` to your build.gradle. – Rosário Pereira Fernandes Jun 24 '18 at 22:26
8

Change viewModel = ViewModelProviders.of(activity!!).get(BookmarkViewModel::class.java)

to viewModel = ViewModelProviders.of(this).get(BookmarkViewModel::class.java)

Furthermore don't instantiate the viewModel to null. Change it to a lateinit var this way you don't have to instantiate immediately (but you are telling Kotlin that you will instantiate it before accessing it). You can do this like so: private lateinit var viewModel: BookMarkViewModel

EDIT The root of the problem was that the Room Dependencies where either not on the same version or annotationProcessor was used instead of kapt (kapt is required when using Kotlin)

Rene Ferrari
  • 4,096
  • 3
  • 22
  • 28
  • Tried with this and lateinit, the same... Also tried with: private val viewModel by lazy(LazyThreadSafetyMode.NONE) { ViewModelProviders.of(this).get(CheeseViewModel::class.java) } but allways the same... – lacas May 23 '18 at 10:20
  • I am using tabs and fragments.. And I want an instance in the last fragment/tab. Is it possible? Or just in activity? – lacas May 23 '18 at 10:22
  • Yes you can instantiate a `ViewModel` in a `Fragment`. In general `ViewModels` can be used inside `UIControllers`. Why do you use the `AndroidViewModel` and not just `ViewModel`? Is there any reason behind it? And is that `FirstFragment()` just a base `Fragment`? – Rene Ferrari May 23 '18 at 10:24
  • Yes FirstFragment is an abstract fragment with some abstract methods. I just copied the "paging sample" code from the google samples. :) But that is worked in that project, but in an activity. How to update this code to work with a ViewModel instead of AndroidViewModel ? – lacas May 23 '18 at 10:32
  • I have just made a basic project with a similar code to yours (`AndroidViewModel` + the way you instantiate it).Everything worked fine for me. Could you please post the whole Stacktrace (edit your answer or post it as a comment :3)? The mistake has to be somewhere else I believe.. OH and sorry stick to the `AndroidViewModel` since you need a reference to a `Context` :) – Rene Ferrari May 23 '18 at 10:38
  • U used in a tabStrip? in fragments? – lacas May 23 '18 at 10:52
  • No I hadn't used tabs but I don't believe that should make a difference since a Tab still contains the Fragment ;) – Rene Ferrari May 23 '18 at 10:54
  • Okay so looks like your `Database` is causing the issue. `Caused by: java.lang.RuntimeException: cannot find implementation for com.lacas.asd.db.room.BookmarkDb. BookmarkDb_Impl does not exist` – Rene Ferrari May 23 '18 at 11:17
  • 2
    Since you are working with Kotlin. At your dependencies for Room do you use `kapt` instead of `annotationProcessor` and do all your Room dependencies have the same version? If not change that and retry. If it still does not work I'd be really happy if you could provide me your Database creation code, since the mistake will probably be somewhere there. – Rene Ferrari May 23 '18 at 11:20
  • Thanks, finally working... I just pulled my hair out of this session :D – lacas May 23 '18 at 11:25
  • 1
    Yes :) I am sorry that it was so difficult to figure out but glad it works now :)! – Rene Ferrari May 23 '18 at 11:32
  • 1
    In the most recent versions of Android Studio, adding `kapt` results in the error: "Cause: app: Original kapt is deprecated." so you need to add `apply plugin: 'kotlin-kapt'` to your build.gradle. – Rosário Pereira Fernandes Jun 24 '18 at 22:26
  • The warning comes now: Deprecated: Use the constructors for ViewModelProvider directly. – Abhinav Saxena Sep 14 '21 at 13:35
  • Use ViewModelProvider instead of ViewModelProviders. Note the s at the end of the class name. – Abhinav Saxena Oct 09 '22 at 04:54
4

remove kapt "xxxx.xxx." if you still use that in you gradle.build since it'e been deprecated and add

apply plugin: 'kotlin-kapt'

at the end of your gradle.build for the app module. that fixed my problem in android studio 3.1

Jamal S
  • 1,649
  • 1
  • 19
  • 24
1

These 3 things worked for me:

  1. Adding/keeping both annotationProcessor and Kapt in the dependencies

annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" kapt "android.arch.lifecycle:compiler:$lifecycle_version"

annotationProcessor "android.arch.persistence.room:compiler:$room_version" kapt "android.arch.persistence.room:compiler:$room_version"

  1. Addingapply plugin: 'kotlin-kapt' at the top of build.gradle(app) and cleaning the project

  2. Re-installing the application

nb2998
  • 469
  • 1
  • 4
  • 13
1

in, my case I was added private set in DatabaseClass :| I removed it.

  private var INSTANCE: NoteDatabase? = null
            private set

to :

private var INSTANCE: NoteDatabase? = null

this problem take my 2 hours :|||

Sana Ebadi
  • 6,656
  • 2
  • 44
  • 44
1

I fixed the same problem by doing this.

Added the annotation.

class AlertViewModel
@Inject
constructor(private val userRepository: AlertRepository) : ViewModel(){
    val getList:LiveData<List<Alert>> get() =
        userRepository.getList.flowOn(Dispatchers.Main)
            .asLiveData(context = viewModelScope.coroutineContext)

    fun insert(user:Alert){
        viewModelScope.launch {
            userRepository.insert(user)
        }
    }
}

To

@HiltViewModel // Added this annotation
class AlertViewModel
@Inject
constructor(private val userRepository: AlertRepository) : ViewModel(){
    val getList:LiveData<List<Alert>> get() =
        userRepository.getList.flowOn(Dispatchers.Main)
            .asLiveData(context = viewModelScope.coroutineContext)

    fun insert(user:Alert){
        viewModelScope.launch {
            userRepository.insert(user)
        }
    }
}
Quick learner
  • 10,632
  • 4
  • 45
  • 55
1

Fragment or Activity should need @AndroidEntryPoint for instance if you use Dagger/Hil

yusufgltc
  • 51
  • 5
0

In my case, I had forgotten to annotate my DatabaseClass class with

@Database(entities = [Book::class], version = 1)
abstract class BookRoomDatabase: RoomDatabase() 
Dawit Abraham
  • 1,616
  • 15
  • 19
0

In my case this implementation solved my problem

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

before I implemented this only implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"

0

For anyone who found none of the above solutions worked:

Double-check if you allowed Room to execute in the main thread. In this case, the ViewModel is trying to construct in the main thread, while Room's builder rejects it.

eggsloveoats
  • 453
  • 4
  • 14
0

What was needed in my case. In build.gradle(:app), in the plugins block:

    id 'kotlin-kapt'

In the dependencies block:

implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'

In the class Fragment, where Fragment is your Fragment name:

    private lateinit var  viewModel : ViewModel

In OnViewCreated of your Fragment:

        viewModel = ViewModelProvider(this, ViewModelFactory()).get(ViewModel::class.java)

where ViewModel and ViewModelFactory are your ViewModel and ViewModelFactory names respectively.

Also, if you passed some variable in your ViewModel and ViewModelFactory, like private val repository: Repository or private val mydao:MyDao, make sure to add them to viewModel declaration this way:

next to lateinit viewModel at the beginning of the Fragment class:

    private val myRoomDatabase : MyRoomDatabase by lazy { MyRoomDatabase.getDatabase(requireContext()) }

then, still in OnViewCreated:

        viewModel = ViewModelProvider(this, ViewModelFactory(myRoomDatabase.myDao())).get(ViewModel::class.java)

or, even better, use indexing instead of

.get

    viewModel = ViewModelProvider(this, ItemListViewModelFactory(notesRoomDatabase.notesDao()))[ItemListViewModel::class.java]
Lucas
  • 458
  • 4
  • 6