Search code examples
javaandroidsqliteandroid-architecture-components

Cannot observe null data - MVVM with Livedata


I've create simple app using MVVM pattern with LiveData to observe data. The list I display is displayed in a fragment. Everything seems okay to me, as I compared my code to other my samples which work and I found nothing to be wrong.

When activity is started, I swap fragments and in onActivityCreated in line mViewModel.getAllShops().observe(this, new Observer<List<Shop>>() { //<--error I get following error:

java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.lifecycle.LiveData.observe(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Observer)' on a null object reference

my schema is:
- Entity - Shop.class
- Dao - ShopDao.class
- Reposityory - ShopRepository.class
- ViewModel - ShopListViewModel
- Fragment which displays data in RecyclerView - ShopList.java

Shop.class:

@Entity(tableName = "shops")
public class Shop {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    public int id;

    @ColumnInfo(name = "shop_name")
    public String shopName;

    @ColumnInfo(name = "shop_description")
    public String shopDescription;

    @ColumnInfo(name = "radius")
    public int radius;

    @ColumnInfo(name = "latitude")
    public String latitude;

    @ColumnInfo(name = "longitude")
    public String longitude;

    @ColumnInfo(name = "isFavourite")
    public int isFavouriteShop;

    public int getId() {
        return id;
    }

    public String getShopName() {
        return shopName;
    }

    public String getShopDescription() {
        return shopDescription;
    }

    public int getRadius() {
        return radius;
    }

    public String getLatitude() {
        return latitude;
    }

    public String getLongitude() {
        return longitude;
    }

    public int getIsFavouriteShop() {
        return isFavouriteShop;
    }



    @Ignore
    public Shop(String shopName, String shopDescription, int radius, String latitude, String longitude, int isFavouriteShop) {
        this.shopName = shopName;
        this.shopDescription = shopDescription;
        this.radius = radius;
        this.latitude = latitude;
        this.longitude = longitude;
        this.isFavouriteShop = isFavouriteShop;
    }

    public Shop(int id, String shopName, String shopDescription, int radius, String latitude, String longitude, int isFavouriteShop) {
        this.id = id;
        this.shopName = shopName;
        this.shopDescription = shopDescription;
        this.radius = radius;
        this.latitude = latitude;
        this.longitude = longitude;
        this.isFavouriteShop = isFavouriteShop;
    }
}

ShopDao.class:

@Dao
public interface ShopDao {

    @Query("SELECT * FROM shops")
    LiveData<List<Shop>> getAllShops();
}

Repository:

public class ShopRepository {

    private ShopDao dao;
    private LiveData<List<Shop>> mAllShops;

    public ShopRepository(Application application){
        dao = ShopDatabase.getInstance(application).dao();
        mAllShops = dao.getAllShops();
    }

    public LiveData<List<Shop>> getAllShops() {
        return mAllShops;
    }

ViewModel:

public class ShopListViewModel extends ViewModel {


    private LiveData<List<Shop>> mAllShops;
    private ShopRepository mRepository;

    public ShopListViewModel(Application application) {
        mRepository = new ShopRepository(application);
        mAllShops = mRepository.getAllShops();

    }

    public ShopListViewModel() {
    }

    public LiveData<List<Shop>> getAllShops() {
        return mAllShops;
    }
}

called from fragment from onActivityCreated :

@Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mViewModel = ViewModelProviders.of(this).get(ShopListViewModel.class);
        mViewModel.getAllShops().observe(this, new Observer<List<Shop>>() { //<--error
            @Override
            public void onChanged(List<Shop> shops) {
                adapter.setList(shops);
            }
        });
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
        recyclerView.setAdapter(adapter);
    }

plus database:

@Database(entities = {Shop.class}, version = 1, exportSchema = false)
public abstract class ShopDatabase extends RoomDatabase {

    public abstract ShopDao dao();

    private static volatile ShopDatabase INSTANCE;

    public static ShopDatabase getInstance(final Context context){
        if (INSTANCE == null){
            synchronized (ShopDatabase.class){
                if (INSTANCE == null){
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            ShopDatabase.class,
                            "shop_database.db")
                            .build();
                }
            }
        }
        return INSTANCE;
    }

}

My gradle dependencies:

    implementation 'androidx.room:room-runtime:2.1.0-alpha03'
    annotationProcessor 'androidx.room:room-compiler:2.1.0-alpha03'

    // ViewModel and LiveData
    implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0-alpha01'
    annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.1.0-alpha01'

My project is migrated to Android X so I used those.

I can add items to database with any problem, they are saved and avaible.

Can anyone of you see any mystake I have done?
Thanks in advance!


Solution

  • The mAllShops variable is initialized in the constructor which takes in a parameter of type Application. But that constructor is never called. When you get your ViewModel in your Fragment, it by default calls the parameterless constructor. To get it to call your parameterized constructor, you need to create a ViewModelProvider.Factory.

    Try reading the "ViewModel with non-default constructor" section of this