这是我第一次使用此库,甚至更多我下载了该项目并实现了新的依赖关系。首先,我无法解决DaggerApplicationCompoment
上的符号错误。有关错误的更多详细信息是:
error: [Dagger/MissingBinding] @javax.inject.Named("movieDB") retrofit2.Retrofit cannot be provided without an @Provides-annotated method.
@javax.inject.Named("movieDB") retrofit2.Retrofit is injected at
free.movies.freemovies.dagger.modules.HttpClientModule.provideFithubApi(restAdapter)
free.movies.freemovies.data.Api.TheMovieDbAPI is injected at
free.movies.freemovies.ui.main.MainFragment.mDbAPI
free.movies.freemovies.ui.main.MainFragment is injected at
free.movies.freemovies.dagger.components.ApplicationComponent.inject(free.movies.freemovies.ui.main.MainFragment)
这是模块类:
package free.movies.freemovies.dagger.modules;
import android.app.Application;
import java.io.File;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import dagger.Module;
import dagger.Provides;
import free.movies.freemovies.dagger.AppScope;
import free.movies.freemovies.data.Api.TheMovieDbAPI;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.moshi.MoshiConverterFactory;
/**
* Created by <a href="mailto:marcus@gabilheri.com">Marcus Gabilheri</a>
*
* @author Marcus Gabilheri
* @version 1.0
* @since 9/4/16.
*/
@Module
public class HttpClientModule {
private static final long DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
public static final String BACKDROP_URL = "http://image.tmdb.org/t/p/w1280";
public static final String POSTER_URL = "http://image.tmdb.org/t/p/w500";
public static final String API_URL = "https://api.themoviedb.org/3/";
public static final String NOW_PLAYING = "movie/now_playing";
public static final String LATEST = "movie/latest";
public static final String POPULAR = "movie/popular";
public static final String TOP_RATED = "movie/top_rated";
public static final String UPCOMING = "movie/upcoming";
public static final String MOVIE = "movie/";
public static final String PERSON = "person/";
public static final String DISCOVER = "discover/movie/";
public static final String SEARCH_MOVIE = "search/movie/";
public static final String TV = "tv/";
@Provides
@AppScope
public OkHttpClient provideOkHttpClient(Application app) {
File cacheDir = new File(app.getCacheDir(), "http");
return new OkHttpClient.Builder()
.readTimeout(1, TimeUnit.MINUTES)
.connectTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.cache(new okhttp3.Cache(cacheDir, DISK_CACHE_SIZE))
.build();
}
@Provides
@Named("TVDB") // Name is used in case a second Retrofit api is provided.
@AppScope
public Retrofit provideTVDBRestAdapter(MoshiConverterFactory moshiConverterFactory, OkHttpClient okHttpClient) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClient = okHttpClient.newBuilder()
.addInterceptor(interceptor)
.build();
return new Retrofit.Builder()
.baseUrl(API_URL)
.client(okHttpClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(moshiConverterFactory)
.build();
}
@Provides
public TheMovieDbAPI provideFithubApi(@Named("movieDB") Retrofit restAdapter) {
return restAdapter.create(TheMovieDbAPI.class);
}
@Provides
@AppScope
public MoshiConverterFactory provideMoshiConverterFactory() {
return MoshiConverterFactory.create();
}
}
还有我正在使用该模块的片段:
package free.movies.freemovies.ui.main;
import android.os.Bundle;
import android.util.SparseArray;
import androidx.core.content.ContextCompat;
import androidx.leanback.app.BrowseSupportFragment;
import androidx.leanback.widget.ArrayObjectAdapter;
import androidx.leanback.widget.HeaderItem;
import androidx.leanback.widget.ListRow;
import androidx.leanback.widget.ListRowPresenter;
import androidx.leanback.widget.OnItemViewSelectedListener;
import androidx.leanback.widget.Presenter;
import androidx.leanback.widget.Row;
import androidx.leanback.widget.RowPresenter;
import javax.inject.Inject;
import free.movies.freemovies.App;
import free.movies.freemovies.Config;
import free.movies.freemovies.R;
import free.movies.freemovies.dagger.modules.HttpClientModule;
import free.movies.freemovies.data.Api.TheMovieDbAPI;
import free.movies.freemovies.data.models.Movie;
import free.movies.freemovies.data.models.MovieResponse;
import free.movies.freemovies.ui.base.GlideBackgroundManager;
import free.movies.freemovies.ui.movies.MoviePresenter;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
/**
* Created by <a href="mailto:marcus@gabilheri.com">Marcus Gabilheri</a>
*
* @author Marcus Gabilheri
* @version 1.0
* @since 10/8/16.
*/
public class MainFragment extends BrowseSupportFragment implements OnItemViewSelectedListener {
@Inject
TheMovieDbAPI mDbAPI;
private GlideBackgroundManager mBackgroundManager;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private static final int NOW_PLAYING = 0;
private static final int TOP_RATED = 1;
private static final int POPULAR = 2;
private static final int UPCOMING = 3;
SparseArray<MovieRow> mRows;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
App.instance().appComponent().inject(this);
// The background manager allows us to manage a dimmed background that does not interfere with the rows
// It is the preferred way to set the background of a fragment
mBackgroundManager = new GlideBackgroundManager(getActivity());
// The brand color will be used as the background for the Headers fragment
setBrandColor(ContextCompat.getColor(getActivity(), R.color.primary_transparent));
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
// The TMDB logo on the right corner. It is necessary to show based on their API usage policy
setBadgeDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.powered_by));
createDataRows();
createRows();
prepareEntranceTransition();
fetchNowPlayingMovies();
fetchTopRatedMovies();
fetchPopularMovies();
fetchUpcomingMovies();
}
/**
* Creates the data rows objects
*/
private void createDataRows() {
mRows = new SparseArray<>();
MoviePresenter moviePresenter = new MoviePresenter();
mRows.put(NOW_PLAYING, new MovieRow()
.setId(NOW_PLAYING)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Now Playing")
.setPage(1)
);
mRows.put(TOP_RATED, new MovieRow()
.setId(TOP_RATED)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Top Rated")
.setPage(1)
);
mRows.put(POPULAR, new MovieRow()
.setId(POPULAR)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Popular")
.setPage(1)
);
mRows.put(UPCOMING, new MovieRow()
.setId(UPCOMING)
.setAdapter(new ArrayObjectAdapter(moviePresenter))
.setTitle("Upcoming")
.setPage(1)
);
}
/**
* Creates the rows and sets up the adapter of the fragment
*/
private void createRows() {
// Creates the RowsAdapter for the Fragment
// The ListRowPresenter tells to render ListRow objects
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
for (int i = 0; i < mRows.size(); i++) {
MovieRow row = mRows.get(i);
// Adds a new ListRow to the adapter. Each row will contain a collection of Movies
// That will be rendered using the MoviePresenter
HeaderItem headerItem = new HeaderItem(row.getId(), row.getTitle());
ListRow listRow = new ListRow(headerItem, row.getAdapter());
rowsAdapter.add(listRow);
}
// Sets this fragments Adapter.
// The setAdapter method is defined in the BrowseFragment of the Leanback Library
setAdapter(rowsAdapter);
setOnItemViewSelectedListener(this);
}
/**
* Fetches now playing movies from TMDB
*/
private void fetchNowPlayingMovies() {
Disposable disposable = mDbAPI.getNowPlayingMovies(Config.API_KEY_URL, mRows.get(NOW_PLAYING).getPage())
.subscribeOn(io.reactivex.schedulers.Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
@Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, NOW_PLAYING);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching now playing movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Fetches the popular movies from TMDB
*/
private void fetchPopularMovies() {
Disposable disposable = mDbAPI.getPopularMovies(Config.API_KEY_URL, mRows.get(POPULAR).getPage())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
@Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, POPULAR);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching popular movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Fetches the upcoming movies from TMDB
*/
private void fetchUpcomingMovies() {
Disposable disposable = mDbAPI.getUpcomingMovies(Config.API_KEY_URL, mRows.get(UPCOMING).getPage())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
@Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, UPCOMING);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching upcoming movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Fetches the top rated movies from TMDB
*/
private void fetchTopRatedMovies() {
Disposable disposable = mDbAPI.getTopRatedMovies(Config.API_KEY_URL, mRows.get(TOP_RATED).getPage())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MovieResponse>() {
@Override
public void accept(MovieResponse response) {
MainFragment.this.bindMovieResponse(response, TOP_RATED);
MainFragment.this.startEntranceTransition();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable e) {
Timber.e(e, "Error fetching top rated movies: %s", e.getMessage());
}
});
compositeDisposable.add(disposable);
}
/**
* Binds a movie response to it's adapter
* @param response
* The response from TMDB API
* @param id
* The ID / position of the row
*/
private void bindMovieResponse(MovieResponse response, int id) {
MovieRow row = mRows.get(id);
row.setPage(row.getPage() + 1);
for(Movie m : response.getResults()) {
if (m.getPosterPath() != null) { // Avoid showing movie without posters
row.getAdapter().add(m);
}
}
}
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) {
// Check if the item is a movie
if (item instanceof Movie) {
Movie movie = (Movie) item;
// Check if the movie has a backdrop
if(movie.getBackdropPath() != null) {
mBackgroundManager.loadImage(HttpClientModule.BACKDROP_URL + movie.getBackdropPath());
} else {
// If there is no backdrop for the movie we just use a default one
mBackgroundManager.setBackground(ContextCompat.getDrawable(getActivity(), R.drawable.material_bg));
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
compositeDisposable.dispose();
}
}
我看到人们有类似的问题,但是我不知道该怎么办才能解决这个问题。如果有人可以向我解释或提示我该怎么办。
答案 0 :(得分:0)
错误消息指出,没有模块为您的
提供@Named("movieDB") Retrofit restAdapter
@Inject
TheMovieDbAPI mDbAPI;
要注入,因为它是@Named("movieDB") Retrofit restAdapter
依赖项
答案 1 :(得分:0)
在查看@peterwhy引用的commit之后。似乎他们也忘记了在同一HttpClientModule
类中更新以下依赖项。
要修复,请更改以下内容:
@Provides
public TheMovieDbAPI provideFithubApi(@Named("movieDB") Retrofit restAdapter) {
return restAdapter.create(TheMovieDbAPI.class);
}
到
@Provides
public TheMovieDbAPI provideFithubApi(@Named("TVDB") Retrofit restAdapter) {
return restAdapter.create(TheMovieDbAPI.class);
}
在/dagger/modules/HttpClientModule.java
文件中。