2017-04-26 49 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