我正在使用Dagger2构建应用程序。在尝试将RecylerAdapter和LayoutManager转换为注入的对象之前,我已经使用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());
    }
}


我意识到这里有很多代码。任何帮助表示赞赏!

最佳答案

您的错误:


  没有@Inject不能提供MainScreenRecyclerAdapter
  构造函数或@Provides或@Produces注释方法。


因此,您必须为@Inject投放MainScreenRecyclerAdapter。因为您在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;
    }

10-08 17:29