简体   繁体   中英

Dagger MVVM - ViewModel injection is being null

I am trying to inject ViewModel in a fragment using Dagger2 but I am getting a NullPointerException . Following is the fragment and the stacktrace.

private CompositeDisposable disposable = new CompositeDisposable();
@Inject
MovieViewModel viewModel;

@BindView(R.id.movies_recycler_view)
RecyclerView recyclerView;

public MoviesFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setHasOptionsMenu(true);
}


@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.movies_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    switch (id) {
        case R.id.sort_by:
            topRatedSubscription();
            break;
        case R.id.sort_by_popular:
            moviesSubscription();
            break;
    }

    return super.onOptionsItemSelected(item);
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.movies_fragment_content, container, false);
    ButterKnife.bind(this, view);
    GridLayoutManager layoutManager = new GridLayoutManager(getContext(), COLUMN_COUNT);

    recyclerView.setHasFixedSize(true);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    adapter = new MoviesGridAdapter(getContext(), resultItems);
    recyclerView.setAdapter(adapter);

    return view;
}


private void moviesSubscription() {


    disposable.add(viewModel.discoverMovies(Constants.MOVIE_API_KEY)
            .observeOn(AndroidSchedulers.mainThread()
            ).subscribe(
                    response -> adapter.setMovies(response.getResults())
            ));

}

private void topRatedSubscription() {
}

@Override
public void onResume() {
    super.onResume();
    moviesSubscription();
}


Process: com.example.mchapagai, PID: 5986
                                                                         java.lang.RuntimeException: Unable to resume activity {com.example.mchapagai/com.example.activity.MoviesActivity}: java.lang.NullPointerException: Attempt to invoke interface method 'io.reactivex.Observable com.example.viewmodel.MovieViewModel.discoverMovies(java.lang.String)' on a null object reference
                                                                             at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3581)
                                                                             at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3621)
                                                                             at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2862)
                                                                             at android.app.ActivityThread.-wrap11(Unknown Source:0)
                                                                             at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
                                                                             at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                             at android.os.Looper.loop(Looper.java:164)
                                                                             at android.app.ActivityThread.main(ActivityThread.java:6494)
                                                                             at java.lang.reflect.Method.invoke(Native Method)
                                                                             at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
                                                                             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
                                                                          Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'io.reactivex.Observable com.example.viewmodel.MovieViewModel.discoverMovies(java.lang.String)' on a null object reference
                                                                             at com.example.fragment.MoviesFragment.moviesSubscription(MoviesFragment.java:119)
                                                                             at com.example.fragment.MoviesFragment.onResume(MoviesFragment.java:146)
                                                                             at android.support.v4.app.Fragment.performResume(Fragment.java:2308)
                                                                             at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1465)
                                                                             at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1750)
                                                                             at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1819)
                                                                             at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3227)
                                                                             at android.support.v4.app.FragmentManagerImpl.dispatchResume(FragmentManager.java:3191)
                                                                             at android.support.v4.app.FragmentController.dispatchResume(FragmentController.java:214)
                                                                             at android.support.v4.app.FragmentActivity.onResumeFragments(FragmentActivity.java:482)
                                                                             at android.support.v4.app.FragmentActivity.onPostResume(FragmentActivity.java:471)
                                                                             at android.support.v7.app.AppCompatActivity.onPostResume(AppCompatActivity.java:171)
                                                                             at android.app.Activity.performResume(Activity.java:7131)
                                                                             at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3556)
                                                                             at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3621) 
                                                                             at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2862) 
                                                                             at android.app.ActivityThread.-wrap11(Unknown Source:0) 
                                                                             at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589) 
                                                                             at android.os.Handler.dispatchMessage(Handler.java:106) 
                                                                             at android.os.Looper.loop(Looper.java:164) 
                                                                             at android.app.ActivityThread.main(ActivityThread.java:6494) 
                                                                             at java.lang.reflect.Method.invoke(Native Method) 
                                                                             at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
                                                                             at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

I have defined view model and viewmodelimpl as follows:

public interface MovieViewModel {
    Observable<MovieResponse> discoverMovies(String apiKey);
}

public class MovieViewModelImpl implements MovieViewModel {

    private MovieAPI movieAPI;

    @Inject
    MovieViewModelImpl(MovieAPI movieAPI) {
        this.movieAPI = movieAPI;
    }

    @Override
    public Observable<MovieResponse> discoverMovies(String apiKey) {
        return movieAPI.discoverMovies(apiKey);
    }
}

public interface MovieAPI {

    Observable<MovieResponse> discoverMovies(String apiKey);
}

Here is the movie service that has a Query to get the apiKey required to fetch the movies. The base url is being constructed using OkHttp3 and retrofit in a service factory class.

public interface MovieService {

@Headers("Accept:application/json")
@GET("movie/popular")
Observable<MovieResponse> discoverMovies(@Query("api_key") String apiKey);

}

I have a MovieViewModelModule which is injected in AppModule using @Module (includes = MovieViewModelModule.class) .

@Module(includes = {MovieAPIModule.class})
public class MovieViewModelModule {

    @Provides
    MovieViewModel provideMovieViewModel(MovieAPI movieAPI) {
        return new MovieViewModelImpl(movieAPI);
    }
}

Repo: https://github.com/mchapagai/Discover-Movies

You are missing code to actually inject fields in the fragment .

You need a Component interface to do that, let's say its name is MoviesComponent

  • Create interface ann annotate it with @Component
  • Create method inject(MoviesFragment fragment) in your interface
  • In your onCreateView(), use DaggerMoviesComponent.builder().build().inject(this) to inject the view model field, make sure to build the project for this class to be generated.

After that injection line, your view model field will be injected and you can use it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM