ViewModel has no zero argument constructor error - even when it has a zero argument constructor

Viewed 6109

I'm new to Android and Java and am trying to make a location-based app.

EDIT

I've made a much, much simpler test code and get the same error. Here's the java:

package com.example.viewmodeltest;

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

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    public class MyViewModel extends ViewModel {
        public int scoreTeamA = 0;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyViewModel locationViewModel = new ViewModelProvider(this).get(MyViewModel.class);
    }
}

I get the same error. Here are the dependencies in my app-level build.gradle:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    //dependencies for ViewModel, LiveData, etc.
    def lifecycle_version = "2.2.0"
    def arch_version = "2.1.0"

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
    // Lifecycles only (without ViewModel or LiveData)
    implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
    // Saved state module for ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
    // Annotation processor
    annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
}

ORIGINAL POST

I'm attempting to use ViewModel and LiveData to update the user location as I understand that's the best way to be lifecycle-aware. I have a default maps activity...

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback {...}

A class which extends LiveData to store the user location...

public class LocationLiveData extends LiveData<Location> {
        private final Context context;
        private FusedLocationProviderClient fusedLocationClient;
        private LocationRequest locationRequest;

        public LocationLiveData(Context context) {
            this.context = context;
            this.fusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
        }

        private void setLocationData(Location location) {
            Location value = new Location("SetInternal");
            value.setLatitude(location.getLatitude());
            value.setLongitude(location.getLongitude());
            setValue(value);
        }

        protected void createLocationRequest() {
            LocationRequest locationRequest = LocationRequest.create();
            locationRequest.setInterval(1000);
            locationRequest.setFastestInterval(500);
            locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        }

        private LocationCallback locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if (locationResult == null) {
                    return;
                }
                for (Location location : locationResult.getLocations()) {
                    setLocationData(location);
                }
            }
        };

        private void startLocationUpdates() {
            createLocationRequest();
            fusedLocationClient.requestLocationUpdates(locationRequest,
                    locationCallback,
                    Looper.getMainLooper());
        }

        @Override
        protected void onInactive() {
            super.onInactive();
            fusedLocationClient.removeLocationUpdates(locationCallback);
        }

        @Override
        protected void onActive() {
            super.onActive();
            fusedLocationClient.getLastLocation()
                    .addOnSuccessListener(new OnSuccessListener<Location>() {
                        @Override
                        public void onSuccess(Location location) {
                            if (location != null)
                                setValue(location);
                        }
                    });
            startLocationUpdates();
        }
    }

And a class which extends ViewModel to allow the main activity to reach the LocationLiveData.

 public class LocationViewModel extends ViewModel {
        private LocationLiveData locationLiveData;

        public LocationViewModel () {
            locationLiveData = new LocationLiveData(getApplicationContext());
        }

        public LocationLiveData getLocationLiveData() {
            return locationLiveData;
        }
    }

Then when I attempt to make an instance of locationViewModel in the onMapReady method:

LocationViewModel locationViewModel = new ViewModelProvider(this).get(LocationViewModel.class);

I get an error on that line:

Cannot create an instance of class com.example.MapsActivity$LocationViewModel

Caused by: java.lang.InstantiationException: java.lang.Class has no zero argument constructor

I get this error even if I take out the constructor entirely in locationViewModel, and also if I attempt to extend AndroidViewModel instead.

Any ideas? I've seen other similar queries but the answer has always been to take arguments out of the constructor - which I've already done!

Thanks a lot for any help

4 Answers

while using hilt I am facing the same issue when I forgot to add

@AndroidEntryPoint

above the activity declaration

Either:

  • Move MyViewModel to a separate Java file, or

  • Make MyViewModel be a static class

Right now, you have defined MyViewModel as an inner class of MainActivity. That will not work, as only an instance of MainActivity can create an instance of MyViewModel. In particular, ViewModelProvider cannot create an instance of MyViewModel.

When you write

public class MainActivity extends AppCompatActivity {

    public class MyViewModel extends ViewModel {
       public int scoreTeamA = 0;
    }
}

This makes MyViewModel an inner class of MainActivity, meaning it has an implicit reference to the outer class (as per the Java documentation) and cannot be constructed separately from MainActivity. This effectively means that in bytecode, its constructor always takes an instance of MainActivity - even if you don't write out that constructor.

You can make your inner class a static nested class by adding the static keyword. This removes the implicit reference to the outer class and makes it similar to any other top level class:

public class MainActivity extends AppCompatActivity {

    public static class MyViewModel extends ViewModel {
       public int scoreTeamA = 0;
    }
}

in my case did forget to add HasSupportFragmentInjector to my Activity !!!

public class MainActivity extends AppComaptActivity
        implements HasSupportFragmentInjector{


@Override
public void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;


    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
}
Related