Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly use Dagger2 with the new Android Architecture Components

I'm trying to use the new Architecture Components, but I'm also still new to dagger and I'm missing stuff.

With the below code, I'm getting a NullPointerException, can't locate where. Also if there's something else I need to fix or improve, please suggest.

CODE : ViewModel

public class PostsVM extends ViewModel {
    private LiveData<StoryPost> post;
    private Repository          repository;

    @Inject
    public PostsVM(Repository repository) {
        this.repository = repository;
    }

    public void init() {
        if (this.post != null) {
            return;
        }
        post = repository.getPosts();
    }

    public LiveData<StoryPost> getPost() {
        return post;
    }
}

Repository

@Singleton
public class Repository {
    private final MutableLiveData<StoryPost> data = new MutableLiveData<>();

    public LiveData<StoryPost> getPosts() {
        //
        new GetUser(post.getUid()) {
            @Override
            public void onSuccess(@NonNull User user) {
                // this is where I setValue//
                data.setValue(post);
        }

        @Override
        public void onError() {

        }

        @Override
        public void userNotFound() {

        }
    };
    return data;
    }
}

Singleton Factory

@Singleton
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;

@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
    this.creators = creators;
}

@SuppressWarnings("unchecked")
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
    Provider<? extends ViewModel> creator = creators.get(modelClass);
    if (creator == null) {
        for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
            if (modelClass.isAssignableFrom(entry.getKey())) {
                creator = entry.getValue();
                break;
            }
        }
    }
    if (creator == null) {
        throw new IllegalArgumentException("unknown model class " + modelClass);
    }
    try {
        return (T) creator.get();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

}

DAO

@Dao
public interface PostDao {
    @Query("SELECT * FROM posts ORDER by time DESC")
    LiveData<List<StoryPost>> getAll();

    @Query("SELECT * FROM posts WHERE id = :id")
    LiveData<List<StoryPost>> getPost(String id);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    @NonNull
    void insert(StoryPost... posts);

    @Delete
    void delete(StoryPost post);

    @Update
    void update(StoryPost post);
}

Then in MainActivity:

@Inject public ViewModelFactory factory;
//...
//*onCreate*

PostsVM model = ViewModelProviders.of(this, factory).get(PostsVM.class);
model.init();
final Observer<StoryPost> observer = post -> storyAdapter.insert(post);
model.getPost().observe(this, observer);

Logcat :

... java.lang.NullPointerException: Attempt to invoke interface method 
'android.arch.lifecycle.ViewModel android.arch.lifecycle.ViewModelProvider
$Factory.create(java.lang.Class)' on a null object reference
                                                                              at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2479)
                                                                              at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2539)
                                                                              at android.app.ActivityThread.access$900(ActivityThread.java:168)
                                                                              at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1378)
                                                                              at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                              at android.os.Looper.loop(Looper.java:150)
                                                                              at android.app.ActivityThread.main(ActivityThread.java:5665)
                                                                              at java.lang.reflect.Method.invoke(Native Method)
                                                                             at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:799)
                                                                              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:689)
                                                                           Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'android.arch.lifecycle.ViewModel android.arch.lifecycle.ViewModelProvider$Factory.create(java.lang.Class)' on a null object reference
                                                                           at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:128)
                                                                              at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:96)
                                                                              at com.aollay.smartpaper.MainActivity.bindDatabase(MainActivity.java:238)
                                                                              at com.aollay.smartpaper.MainActivity.populateNews(MainActivity.java:233)
                                                                              at com.aollay.smartpaper.MainActivity.config(MainActivity.java:159)
                                                                              at com.aollay.smartpaper.MainActivity.onCreate(MainActivity.java:74)
                                                                              at android.app.Activity.performCreate(Activity.java:6372)
                                                                              at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
                                                                              at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2432)
like image 528
Relm Avatar asked Jun 20 '17 11:06

Relm


People also ask

What is Android Dagger2?

Dagger 2 is a compile-time android dependency injection framework that uses Java Specification Request 330 and Annotations. Some of the basic annotations that are used in dagger 2 are: @Module This annotation is used over the class which is used to construct objects and provide the dependencies.

What is Dagger2 used for?

Dagger is arguably the most used Dependency Injection, or DI, framework for Android. Many Android projects use Dagger to simplify building and providing dependencies across the app. It gives you the ability to create specific scopes, modules, and components, where each forms a piece of a puzzle: The dependency graph.


1 Answers

The issue is caused by the ViewModelFactory instance being null inside your MainActivity, as the NPE suggests. This itself is most probably caused by the fact that the ViewModelFactory is not being injected properly, thus remaining null. As Orest suggests inside the comments, you need to make sure that the MainActivity is properly injected from your AppModule:

MainActivity:

public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector
{
     @Override
     protected void onCreate(Bundle savedInstanceState)
     {
          AndroidInjection.inject(activity);
            super.onCreate(savedInstanceState);
     }

    // if your Activity also has Fragments which need to be injected

    @Inject
    DispatchingAndroidInjector<Fragment> androidInjector;

    @Override
    public DispatchingAndroidInjector<Fragment> supportFragmentInjector()
    {
        return androidInjector;
    }
}

You can take a look at most of the DI classes being used in a related question I posted earlier over at AndroidInjector<android.app.Activity> cannot be provided without an @Provides- or @Produces-annotated method and see if that setup helps you out.

like image 85
Bogdan Zurac Avatar answered Oct 26 '22 15:10

Bogdan Zurac