12

I am trying to create an instance of my AndroidViewModel in MainActivity. When I do this I get the following error has no zero argument constructor

Here is my RecipeViewModel

package com.example.kookrecepten;

import android.app.Application;

import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

import java.util.List;

public class RecipeViewModel extends AndroidViewModel {
    private RecipeRepository recipeRepository;
    private LiveData<List<Recipe>> allRecipes;

    public RecipeViewModel(Application application) {
        super(application);
        recipeRepository = new RecipeRepository(application);
        allRecipes = recipeRepository.getAllRecipes();
    }

    public void insert(Recipe recipe) {
        recipeRepository.insert(recipe);
    }

    public void update(Recipe recipe) {
        recipeRepository.update(recipe);
    }

    public void delete(Recipe recipe) {
        recipeRepository.delete(recipe);
    }

    public void deleteAll() {
        recipeRepository.deleteAllRecipes();
    }

    public LiveData<List<Recipe>> getAllRecipes() {
        return allRecipes;
    }
}

Now correct me if I'm wrong but AndroidViewModel needs the Application context in the constructor and ViewModel doesn't. So I have no idea why android asks for a zero arguments constructor.

Here is my Main activity where I ask for an instance.

package com.example.kookrecepten;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;

import com.example.kookrecepten.databinding.ActivityMainBinding;

import java.util.List;


public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private RecipeViewModel recipeViewModel;

    protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        View view = binding.getRoot();
        setContentView(view);

        //Get an instance of the RecipeViewModel.
        recipeViewModel = new ViewModelProvider(this).get(RecipeViewModel.class);
        recipeViewModel.getAllRecipes().observe(this, new Observer<List<Recipe>>() {
            @Override
            public void onChanged(List<Recipe> recipes) {
                //Update recycle view

                Toast.makeText(MainActivity.this, "triggered", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

And this are my implementations.

def lifecycle_version = "2.2.0"
def room_version = "2.2.5"

//LifeCycle Components
    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
    // Annotation processor
    implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

    //Room Components
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
Ruben
  • 515
  • 1
  • 5
  • 19

4 Answers4

56

if you're using hilt, you probably forgot to annotate your activity with @AndroidEntryPoint

iamkdblue
  • 3,448
  • 2
  • 25
  • 43
  • 2
    Thanks after hours, I got your post and I realize i forgot to mention @AndroidEntryPoint. – iamkdblue Jul 19 '20 at 13:26
  • In my case I have added @AndroidEntryPoint to my base activity and it was crashing, setting it on each activity worked. But in my dagger project I have did AndroidInjection only on baseactivity – Cyph3rCod3r Aug 13 '20 at 09:49
  • 4
    I got the same "has no zero argument constructor" error because I had not added kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02' to my build.gradle file – Comsci Nov 02 '20 at 13:55
  • @user3433200 after 2 hours, I came here to say the same and then saw that I had overlooked your comment earlier... – Matt Robertson Dec 19 '20 at 00:47
17

apparently if I change

recipeViewModel = new ViewModelProvider(this).get(RecipeViewModel.class);

to this

recipeViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication())).get(RecipeViewModel.class);

It works. I don't know why this solution works could someone explain?

Ruben
  • 515
  • 1
  • 5
  • 19
  • This is caused by a version mismatch between activity-ktx and lifecycle-ktx. It makes your `AppCompatActivity` not implement `HasDefaultViewModelProviderFactory`, and this results in this odd behavior. You should not need to manually grab the "original default" as this way your default is not a `SavedStateViewModelFactory`. – EpicPandaForce Jul 19 '20 at 13:32
  • @EpicPandaForce, so, concretely what is the solution, since I have the same issue ? I just discovered ViewModel and I'm not guru yet ;-) – gduh Aug 13 '20 at 15:03
  • 1
    @gduh use the first one, not the second one, but update your AndroidX dependencies to the latest - and explicitly add androidx.fragment and androidx.activity and androidx.core's latest non-alpha versions, along with lifecycle being at least 2.2.0. – EpicPandaForce Aug 13 '20 at 15:21
  • @EpicPandaForce I have already check all your recommandations, but using AndroidViewModel, the app still crashes with the same message "has no zero argument constructor". For now, I have no other solution than to use the second one. – gduh Aug 14 '20 at 09:58
  • @gduh feel free to create a new question, make sure to add the exception stack trace, the ViewModel, and the build.gradle dependencies. Also important to know if you're using Hilt, because that can mess with it too with some missing deps. – EpicPandaForce Aug 14 '20 at 15:51
  • @Ruben thank you so much bro you really saved my life – Michael Nov 06 '20 at 23:35
  • because this will initialize the default constructor of AndroidViewModel in which application context is recived, i think if you init the default const by normal code than the first one may also work (not sure). – Mubashir Murtaza Feb 22 '21 at 06:58
1

I had a look to Android Source code. If your activity was extending the class Activity, instead of AppCompatActivity you wouldn't have seen this issue. The class Activity provides an implementation for the interface HasDefaultViewModelProviderFactory which provides a correct factory for classes extending AndroidViewModel. Unfortunately, AppCompatActivity still doesn't implement that interface, and then, no correct factory is available. So, either you provide the factory as you did, or you might implement by yourself the interface HasDefaultViewModelProviderFactory.

Omar BELKHODJA
  • 1,622
  • 1
  • 12
  • 18
0

Can you try with the old, deprecated

recipeViewModel = ViewModelProviders.of(this).get(RecipeViewModel.class);

This definitely used to work in our applications. If you're the adventureous type, you can also use the debugger and step into the get() method of the ViewModelProvider to check how it attempts to get an instance of the viewmodel.

Ridcully
  • 23,362
  • 7
  • 71
  • 86