2017-04-26 73 views
1

我正在使用Dagger2构建应用程序。在试图将我的RecylerAdapter和LayoutManager转换为注入对象之前,我有依赖注入与Dagger2一起工作。当我尝试添加这些时,我会遇到各种各样的错误。我一直在阅读越来越多的关于Dagger2试图获得更好的理解,我修正了一些东西,但我仍然不知道为什么这不起作用。我收到了很多编译错误,我认为这与我的依赖关系有关,但在这一点上我感到茫然。我得到的错误是:Dagger2无法生成组件类

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] 

下面是我实现的依赖注入: 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; 
    } 
} 

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()); 
    } 
} 

我知道有很多的代码在这里。任何帮助表示赞赏!

回答

1

你的错误:

MainScreenRecyclerAdapter cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.

所以你必须广告@InjectMainScreenRecyclerAdapter。因为您在MainScreenModule中提供了此适配器。所以你的适配器应该是:

// TODO: Should I make the list contain Video/Result objects and pull the data from that? 
    @Inject 
    public MainScreenRecyclerAdapter(List<Result> dataset, Context context) { 
     myDataset = dataset; 
     myContext = context; 
    } 
+0

嗯,对。谢谢。尽管我还有其他错误。通过同样的逻辑,我需要一个带注释构造函数的GridLayoutManager类,对吗?但是我没有自己的GridLayoutManager类,我从支持库中使用它。 – intA