Dagger2+Retrofit 更改 URL 与子组件:没有 @Provides- 或 @Produces-annotated 方法无法提供

Dagger2+Retrofit change URL with Subcomponent : cannot be provided without an @Provides- or @Produces-annotated method

我是 Dagger 的新手。我在运行时使用 更改 URL。

我还使用了三个模块和三个组件,如下所示:

注意:我有两个组件 (ApplicationComponent,ActivityComponent) 和一个子组件 (UrlComponent) 此外,我使用 @Singletone@PerUrl@PerActivty 作为范围。

当我想 Inject RestApi 进入每个 Activity 我遇到这个错误:

 error: com.example.testdagger2.RestApi cannot be provided without an @Provides- or @Produces-annotated method.
com.example.dagger2.RestApi is injected at
com.example.dagger2.RestApiHelper.restApi
com.example.dagger2.RestApiHelper is injected at
com.example.dagger2.MainActivity.restApiHelper
com.example.testdagger2.MainActivity is injected at
com.example.testdagger2.di.component.ActivityComponent.inject(mainActivity)

在我必须在运行时更改 URL 之前,我有两个组件和模块(appCompoent 和 activtyComponent),所有提供程序(如 Retrofit 和 RestApi ,...)都在 applicationModule 和该程序运行良好。

ApplicationComponent.java

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {

    void inject(ExampleApplication exampleApplication);

    @ApplicationContext
    Context context();

    Application application();

    UrlComponent plus(UrlModule component);
}

ApplicationModule.java

@Module
public class ApplicationModule {

    private Application mApplication;

    public ApplicationModule(Application application) {
        this.mApplication = application;
    }

        @Provides
        @ApplicationContext
        Context provideContext() {
            return mApplication;
        }

        @Provides
        Application provideApplication() {
            return mApplication;
        }
    }

UrlComponent.java

@PerUrl
@Subcomponent(modules = UrlModule.class)
public interface UrlComponent {

}

UrlModule.java

@Module
public class UrlModule {
    private String url;

    public UrlModule(String url) {
        this.url = url;
    }

    @Provides
    @PerUrl
    OkHttpClient provideOkhttpClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .build();
    }

    @Provides
    @PerUrl
    Retrofit provideRetrofit(String baseURL, OkHttpClient client) {

        return new Retrofit.Builder()
                .baseUrl(baseURL)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();
    }

    @Provides
    RestApiHelper provideRestApiHelper(RestApiHelper restApiManager) {
        return restApiManager;
    }

    @Provides
    public RestApi provideApiService() {
        return provideRetrofit(url, provideOkhttpClient())
                .create(RestApi.class);

    }
}

ActivityComponent.java

@PerActivity
@Component(modules = ActivityModule.class,dependencies = ApplicationComponent.class)
public interface ActivityComponent {

    void inject(MainActivity mainActivity);
  .
  .
  .

}

ActivityModule.java

@Module
public class ActivityModule {

    private AppCompatActivity mActivity;

    public ActivityModule(AppCompatActivity mActivity) {
        this.mActivity = mActivity;
    }


    @Provides
    @ActivityContext
    Context provideContext() {
        return mActivity;
    }

    @Provides
    AppCompatActivity provideActivity() {
        return mActivity;
    }

   .
   .
   .

}

RestApi.java

public interface RestApi {

    Single<Object> getquestionlist(@Query("page") int page);

    Single<Object> getCategoryList();
}

RestApiHelper.java

public class RestApiHelper implements RestApi {

    @Inject
    RestApi restApi;

    @Inject
    public RestApiHelper(RestApi restApi) {
        this.restApi = restApi;
    }

    @Override
    public Single<Object> getquestionlist(int page) {
        return restApi.getquestionlist(1);
    }

    @Override
    public Single<Object> getCategoryList() {
        return restApi.getCategoryList();
    }

ExampleApplication.java

public class ExampleApplication extends Application {


    @Inject
    ApplicationComponent appComponent;


    @Override
    public void onCreate() {
        super.onCreate();

        appComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this)).build();
        appComponent.inject(this);
        appComponent.plus(new UrlModule("www/test/en"));


    }

    public ApplicationComponent getAppComponent() {
        return appComponent;
    }


}

BaseActivity.java

public class BaseActivity extends AppCompatActivity {


    ActivityComponent activityComponent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //activityComponent=DaggerActivityComponent.builder().applicationModule()

        activityComponent= DaggerActivityComponent.builder()
                .applicationComponent(((ExampleApplication)getApplication()).getAppComponent())
                .build();

    }

    public ActivityComponent getActivityComponent() {
        return activityComponent;
    }
}

MainActivity.jaqva

public class MainActivity extends BaseActivity {


    @Inject
    RestApiHelper restApiHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


    ActivityComponent component= getActivityComponent();
    component.inject(this);

        restApiHelper.getquestionlist(1).subscribe(new Consumer<Object>() {
        @Override
        public void accept(Object o) throws Exception {

        }
    });

现在我有两个问题:

1-不应该将 Urlmodule 中内置的所有 Provider 添加到 ApplicationModule(UrlModule is Subcomepoent of the ApplicationComponent )

 @PerActivity
    @Component(modules = ActivityModule.class,dependencies = ApplicationComponent.class)
    public interface ActivityComponent {

并且由于 ApplicationComponent 是一个 Activity 组件依赖项,所有这些 Providers 是否也可以在 Activity 组件中使用?

2-作为一个基本问题,问题出在哪里?

首先你提供RestApi的方法是错误的。 我应该像

    @Provides
    public RestApi provideApiService(Retrofit retrofit) {
        return retrofit.create(RestApi.class);

    }

Shouldn't all the Providers built into the Urlmodule be added to ApplicationModule(UrlModule is Subcomepoent of the ApplicationComponent )

当您声明组件依赖项时,依赖组件只能使用在 dependencies 的组件 class 内声明的公开依赖项,在您的情况下,这些是 contextapplication 在您的 ApplicationComponent class 中,由 ApplicationModule 提供。

用于验证(测试):

1) 在您的应用程序模块中添加一个新的 provides

// inside ApplicationModule
@Provides
Student getNum() {
    return new Student("aa");
}

2) 创建并使用 ActivityComponent 对象将 Student 注入任何 class。这将导致缺少供应。它可以通过在 ApplicationComponent class 中公开 Student 来修复:

Student getStu();

2-And as a basic question, where is the problem?

具有以下实现:

  1. Dagger 错误配置的依赖关系图,如上所述
  2. 缺少 Rxjava 实施和改造实施
  3. 缺少 url 提供等

要解决上述问题,请按照以下步骤操作:

  1. Dagger misconfigured dependancy graph, as explained above

a) MainActivity 需要 UrlComponent 而不是 AppComponent 提供的 RestApiHelper 所以首先使用 UrlComponent 作为依赖而不是 AppComponent

@PerActivity
@Component(modules = ActivityModule.class, dependencies = {UrlComponent.class})
public interface ActivityComponent {

    void inject(MainActivity mainActivity);

}

b) 现在公开 UrlComponent

中的依赖项
@PerUrl
@Subcomponent(modules = UrlModule.class)
public interface UrlComponent {
    RestApiHelper getRetrofit();
}

UrlModule.class 的实施已解决问题(如上所述)

import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import java.util.concurrent.TimeUnit;    
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

@Module
public class UrlModule {
    private String url;

    public UrlModule(String url) {
        this.url = url;
    }

    @Provides
    @PerUrl
    OkHttpClient provideOkhttpClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .build();
    }

    @Provides
    String provideUrl() {
        return url;
    }

    @Provides
    @PerUrl
    Retrofit provideRetrofit(String baseURL, OkHttpClient client) {

        return new Retrofit.Builder()
                .baseUrl(baseURL)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();
    }


    @Provides
    RestApi provideApiService(Retrofit retrofit) {
        return retrofit.create(RestApi.class);
    }
}

c) 在应用程序 class 中构建 Url appComponenturlComponent 供以后用作

public class ExampleApplication extends Application {

    // note, I remove the inject, you were doing it for testing etc
    private ApplicationComponent appComponent;

    private UrlComponent urlComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        appComponent = DaggerApplicationComponent.builder()
                .applicationModule(new ApplicationModule(this)).build();

        urlComponent = appComponent.plus(new UrlModule("https://jsonplaceholder.typicode.com/"));


    }

    public ApplicationComponent getAppComponent() {
        return appComponent;
    }

    public UrlComponent getUrlComponent() {
        return urlComponent;
    }


}

现在将 BaseActivity 中的 activityComponent 构建为

activityComponent= DaggerActivityComponent.builder()
                .activityModule(new ActivityModule(this))
                .urlComponent(((ExampleApplication)getApplication()).getUrlComponent())
                .build();

并在您的 MainActivity 中使用它作为

public class MainActivity extends BaseActivity {


    @Inject
    RestApiHelper restApiHelper;
    Disposable disposable;
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        ActivityComponent component = getActivityComponent();
        component.inject(this);

        disposable = restApiHelper.getquestionlist(1)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(Object o) throws Exception {
                        Log.d(TAG, "accept: "+ o.toString());    
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        throwable.printStackTrace();
                    }
                });
    }

    @Override
    protected void onDestroy() {
        disposable.dispose(); // best practice
        super.onDestroy();
    }
}

就是这样。

注意:确保您的 baseUrl 值和 retrofit 对象设置正确并且具有互联网应用程序权限等。您可以进一步优化您的代码lambdas 等,因为我保留了大部分代码以供理解。