Search code examples
javasumandroid-roomviewmodelandroid-livedata

SUM in Room - Cannot create instance inside class ViewModel error- Java


I want to display a sum of favcount from NoteDao, so I make it in Livedata and ViewModel like this

@Dao
public interface NoteDao {

    @Query("SELECT * FROM notes ORDER BY id DESC")
    List<Note> getAllNotes();

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertNote(Note note);

    @Delete
    void deleteNote(Note note);

    @Query("SELECT SUM(fav_count) as favSum FROM notes")
    LiveData<String> getFavSum();

}

This is my entity "notes" that has the fav_count

@Entity(tableName = "notes")

 @ColumnInfo(name = "fav_count")

 private String favCount;
 public Note() {}
 public String getFavCount() { return favCount; }
 public void setFavCount(String favCount) { this.favCount = favCount;}

This is my repository

public class totalRepository {

   NoteDao noteDao;


    public LiveData<String> getTotal() {
        return noteDao.getFavSum();
    }
}

And this is my ViewModel

public class NoteViewModel extends ViewModel {
    public final totalRepository repository;

    public LiveData<String> getmTotal(){
        return repository.getTotal();
    }

    public NoteViewModel(totalRepository repository) {
        this.repository = repository;
    }
}

This is my activity where I want to display the data

public class Statistics extends AppCompatActivity {


    TextView syukrsum;
    String favSum = "0";



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_statistics);
       NoteViewModel noteViewModel;
        syukrsum = findViewById(R.id.syukrSum);
        noteViewModel = new ViewModelProvider(this).get(NoteViewModel.class);
        noteViewModel.getmTotal().observe(Statistics.this, new Observer<String>() {
            @Override
            public void onChanged(String string) {
                if (string == null) {
                    favSum = "0";
                    syukrsum.setText(0);
                } else {
                    favSum = string;
                    syukrsum.setText(string);
                }

            }
        });
        }
    }


When I open the Statistics activity, my app crashes and give the following errors:

2020-12-04 14:40:01.630 26822-26822/com.rhlab.syukr5 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.rhlab.syukr5, PID: 26822
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.rhlab.syukr5/com.rhlab.syukr5.Activities.Statistics}: java.lang.RuntimeException: Cannot create an instance of class com.rhlab.syukr5.NoteViewModel
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3170)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3307)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2036)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:224)
        at android.app.ActivityThread.main(ActivityThread.java:7081)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928)
     Caused by: java.lang.RuntimeException: Cannot create an instance of class com.rhlab.syukr5.NoteViewModel
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:221)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:278)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
        at com.rhlab.syukr5.Activities.Statistics.onCreate(**Statistics.java:31**)
        at android.app.Activity.performCreate(Activity.java:7258)
        at android.app.Activity.performCreate(Activity.java:7249)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3150)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3307) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2036) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:224) 
        at android.app.ActivityThread.main(ActivityThread.java:7081) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928) 
     Caused by: java.lang.InstantiationException: java.lang.Class<com.rhlab.syukr5.NoteViewModel> has no zero argument constructor
        at java.lang.Class.newInstance(Native Method)
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:278) 
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150) 
        **at com.rhlab.syukr5.Activities.Statistics.onCreate(Statistics.java:31)** 
        at android.app.Activity.performCreate(Activity.java:7258) 
        at android.app.Activity.performCreate(Activity.java:7249) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3150) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3307) 
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:113) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:71) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2036) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:224) 
        at android.app.ActivityThread.main(ActivityThread.java:7081) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:928) 

The error points out at Statistics.java line 31

noteViewModel = new ViewModelProvider(this).get(NoteViewModel.class);

QUESTION IS : What is my mistake? Sorry if my question is not clear, keep in mind that I am a novice, maybe I did make huge or stupid mistake, please bear with me and kindly explain the solution!


Solution

  • It is crashing because it is unable to create an instance of your Viewmodel. Why?

    Because by default it only create instance of your viewmodel, if it has zeo constructor parameter. But your viewmodel do have one constructor parameter.

    Your NoteViewModel, needs TotalRepository instance as a constructor parameter, which you're not telling it where to get from.

    Therefore the solution is create a ViewModelFactory.

    Here is sample code:

    import androidx.lifecycle.ViewModel;
    import androidx.lifecycle.ViewModelProvider;
    
    public class NoteViewModelFactory extends ViewModelProvider.NewInstanceFactory {
        private final TotalRepository totalRepository;
    
        public NoteViewModelFactory(TotalRepository totalRepository) {
            this.totalRepository = totalRepository;
        }
    
        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            return (T) new NoteViewModel(totalRepository);
        }
    }
    

    From your Fragment, you need to create viewModel like below:

    NoteDao noteDao = // Initialize your NoteDao object and pass it to Total Repository
        TotalRepository totalRepository = new TotalRepository(noteDao);
        NoteViewModelFactory noteViewModelFactory = new NoteViewModelFactory(totalRepository);
        NoteViewModel noteViewModel = ViewModelProviders.of(this, noteViewModelFactory).get(NoteViewModel.class);