javaandroidandroid-livedataandroid-viewmodelandroid-fusedlocation

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


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


Solution

  • Either:

    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.