Ich baue eine App mit Dagger2. Ich hatte die Abhängigkeitsinjektion mit Dagger2 zu arbeiten, bevor ich versuchte, meinen RecylerAdapter und LayoutManager in injizierte Objekte zu konvertieren. Wenn ich versuche, diese hinzuzufügen, bekomme ich alle möglichen Fehler. Ich habe mehr und mehr auf Dagger2 gelesen und versucht, ein besseres Verständnis davon zu bekommen. Ich habe einige Sachen repariert, bin mir aber immer noch nicht sicher, warum das nicht funktioniert. Ich bekomme viele Kompilierungsfehler, ich denke, das hat etwas mit meinen Abhängigkeiten zu tun, aber ich bin zu diesem Zeitpunkt ratlos. Die Fehler Ich erhalte sind:Dagger2 kann keine Komponentenklassen generieren
D:\Development\Android\Projects\GiantBombForAndroid\app\src\main\java\com\app\int_a\giantbombforandroid\main\App.java
Error:(5, 61) error: cannot find symbol class DaggerNetComponent
D:\Development\Android\Projects\GiantBombForAndroid\app\src\main\java\com\app\int_a\giantbombforandroid\main\mainscreen\MainActivity.java
Error:(11, 61) error: cannot find symbol class DaggerMainScreenComponent
D:\Development\Android\Projects\GiantBombForAndroid\app\src\main\java\com\app\int_a\giantbombforandroid\main\data\component\MainScreenComponent.java
Error:(16, 10) error: java.util.List<com.app.int_a.giantbombforandroid.main.model.Result> cannot be provided without an @Provides-annotated method.
com.app.int_a.giantbombforandroid.main.mainscreen.MainActivity.recyclerAdapter
[injected field of type: com.app.int_a.giantbombforandroid.main.mainscreen.MainScreenRecyclerAdapter recyclerAdapter]
com.app.int_a.giantbombforandroid.main.data.module.MainScreenModule.provideMainScreenRecyclerAdapter(java.util.List<com.app.int_a.giantbombforandroid.main.model.Result> videoList, android.content.Context context)
[parameter: java.util.List<com.app.int_a.giantbombforandroid.main.model.Result> videoList]
D:\Development\Android\Projects\GiantBombForAndroid\app\src\main\java\com\app\int_a\giantbombforandroid\main\data\component\NetComponent.java
Error:(23, 10) error: com.app.int_a.giantbombforandroid.main.mainscreen.MainScreenRecyclerAdapter cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
com.app.int_a.giantbombforandroid.main.mainscreen.MainActivity.recyclerAdapter
[injected field of type: com.app.int_a.giantbombforandroid.main.mainscreen.MainScreenRecyclerAdapter recyclerAdapter]
Hier ist, wie ich die Dependency Injection implementiert haben: MainScreenComponent.java
@CustomScope
@Component(dependencies = NetComponent.class, modules = MainScreenModule.class)
public interface MainScreenComponent {
void inject(MainActivity activity);
}
NetComponent.java
@Singleton
@Component(modules = {AppModule.class, NetModule.class})
public interface NetComponent {
// downstream components need these exposed with the return type
// method name does not really matter
Retrofit retrofit();
void inject(MainActivity activity);
}
AppModule.java
@Module
public class AppModule {
Application application;
public AppModule(Application application){
this.application = application;
}
@Provides
@Singleton
Application provideApplication(){
return application;
}
}
91.363.210 MainScreenModule.java
@Module
public class MainScreenModule {
private final MainScreenContract.View view;
private final Context context;
private final List<Result> videoList;
private final int numColumns;
public MainScreenModule(MainScreenContract.View view, Context context, List<Result> videoList, int numColumns){
this.view = view;
this.context = context;
this.numColumns = numColumns;
this.videoList = videoList;
}
@Provides
@CustomScope
MainScreenContract.View providesMainScreenContractView(){
return view;
}
@Provides
@CustomScope
MainScreenRecyclerAdapter provideMainScreenRecyclerAdapter(List<Result> videoList, Context context){
return new MainScreenRecyclerAdapter(videoList, context);
}
@Provides
@CustomScope
GridLayoutManager provideGridLayoutManager(Context context, int columns){
return new GridLayoutManager(context, columns);
}
}
NetModule.java
@Module
public class NetModule {
// Maybe one day this will be a view object to contain a video?
// Maybe it will become a dependency and will be injected via
// another module? Let Dagger find a view object and create it
public NetModule(){
}
@Provides
@Singleton
SharedPreferences providesSharedPreferences(Application application){
return PreferenceManager.getDefaultSharedPreferences(application);
}
@Provides
@Singleton
Cache provideHttpCache(Application application){
int cacheSize = 10 * 1024 * 1024;
Cache cache = new Cache(application.getCacheDir(), cacheSize);
return cache;
}
@Provides
@Singleton
Gson provideGson(){
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return gsonBuilder.create();
}
@Provides
@Singleton
OkHttpClient provideOkhttpClient (Cache cache){
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.cache(cache);
// Adds GiantBomb.com api key to request
// Adds json parameter because all requests will expect json
client.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
HttpUrl originalHttpUrl = original.url();
HttpUrl url = originalHttpUrl.newBuilder()
.addQueryParameter("api_key", BuildConfig.GIANTBOMB_API_KEY)
.addQueryParameter("format","json")
.build();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.url(url);
Timber.d("URL:" + url);
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
return client.build();
}
@Provides
@Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient){
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(Constants.BASE_URL)
.client(okHttpClient)
.build();
return retrofit;
}
}
App.java
public class App extends Application {
private NetComponent netComponent;
@Override
public void onCreate(){
super.onCreate();
netComponent = DaggerNetComponent.builder()
.appModule(new AppModule(this))
.netModule(new NetModule())
.build();
}
public NetComponent getNetComponent(){
return netComponent;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements MainScreenContract.View {
ArrayList<Result> list = new ArrayList<>();
// Objects for RecyclerView
@BindView(R.id.my_recycler_list)
RecyclerView recyclerView;
@Inject
MainScreenRecyclerAdapter recyclerAdapter;
@Inject
MainScreenPresenter mainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Timber.plant(new Timber.DebugTree() {
// Add the line number to the tag
@Override
protected String createStackElementTag(StackTraceElement element) {
return super.createStackElementTag(element) + ':' + element.getLineNumber();
}
});
//Call the method in MainPresenter to make Network Request
mainPresenter.loadVideo();
DaggerMainScreenComponent.builder()
.netComponent(((App) getApplicationContext()).getNetComponent())
.mainScreenModule(new MainScreenModule(this, this.getApplicationContext(), list, 2))
.build().inject(this);
Timber.d("Array size: " + list.size());
}
@Override
public void showVideos(Video video){
// Loop through the posts, get the title of the post, and add it to our list object
for(int i = 0; i < video.getResults().size(); i++){
Result currentVideo = video.getResults().get(i);
// Filter out Premium videos since these would require authentication
if(currentVideo.getVideoType() != null && !currentVideo.getVideoType().equals("Premium")) {
list.add(currentVideo);
Timber.d("List item " + i + " = " + list.get(list.size()-1));
}
}
// RecyclerView implementation
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
recyclerAdapter = new MainScreenRecyclerAdapter(list, this.getApplicationContext());
recyclerView.setAdapter(recyclerAdapter);
// set to true because all images will be the same size
recyclerView.setHasFixedSize(true);
}
@Override
public void showError(String message){
// Show error message text as a Toast message
Toast.makeText(getApplicationContext(), "Error" + message, Toast.LENGTH_SHORT).show();
Timber.e("Error: " + message);
}
@Override
public void showComplete(){
// Show completed Toast message
Toast.makeText(getApplicationContext(), "Complete", Toast.LENGTH_SHORT).show();
}
}
MainScreenRecyclerAdapter.java
public class MainScreenRecyclerAdapter extends RecyclerView.Adapter {
private List<Result> myDataset;
private Context myContext;
// TODO: Should I make the list contain Video/Result objects and pull the data from that?
public MainScreenRecyclerAdapter(List<Result> dataset, Context context) {
myDataset = dataset;
myContext = context;
}
// Create new views
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(myContext)
.inflate(R.layout.thumbnail_view, parent, false);
final RecyclerView.ViewHolder viewHolder = new VideoViewHolder(v);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Timber.d("Stub for VideoViewHolder onClick() method");
}
});
return viewHolder;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((VideoViewHolder) holder).bind(myDataset, position, myContext);
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return myDataset.size();
}
}
VideoViewHolder.java
public class VideoViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.thumbnail)
public ImageView thumbnailView;
@BindView(R.id.video_title_view)
public TextView videoTitle;
public VideoViewHolder(View v) {
super(v);
ButterKnife.bind(this, v);
}
public void bind(List<Result> myDataset, int position, Context myContext){
// - get element from dataset at this position
// - replace the contents of hte view with that element
Result currentVideo = myDataset.get(position);
String imageUrl =currentVideo.getImage().getMediumUrl();
Timber.d("Image URL: " + imageUrl);
Picasso.with(myContext).load(imageUrl).into(thumbnailView);
videoTitle.setText(currentVideo.getName());
}
}
Ich weiß, eine Menge Code gibt es hier. Jede Hilfe wird geschätzt!
Huh, richtig. Vielen Dank. Obwohl ich noch die anderen Fehler habe. Mit derselben Logik würde ich auch eine GridLayoutManager-Klasse mit einem annotierten Konstruktor benötigen, richtig? Ich habe jedoch keine eigene GridLayoutManager-Klasse, sondern verwende sie aus der Support-Bibliothek. – intA