【问题标题】:Dagger2+Retrofit change URL with Subcomponent : cannot be provided without an @Provides- or @Produces-annotated methodDagger2+Retrofit 使用 Subcomponent 更改 URL:如果没有 @Provides- 或 @Produces-annotated 方法,则无法提供
【发布时间】:2020-05-20 12:42:58
【问题描述】:

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

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

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

当我想Inject RestApi 进入每个活动时,我遇到了这个错误:

 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是一个ActivityComponent依赖,那么所有这些Providers也可以在ActivityComponent中使用吗?

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

【问题讨论】:

  • 删除 RestApiHelper 是多余的。只需使用RestApi

标签: java android retrofit dagger-2 rx-android


【解决方案1】:

是否应该将所有内置于 Urlmodule 的 Providers 添加到 ApplicationModule(UrlModule 是 应用组件)

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

用于验证(测试):

1) 在你的应用模块中添加一个新的 offer

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

2) 创建并使用ActivityComponent 对象在任何类中注入Student。这将导致缺少提供。可以通过在ApplicationComponent 类中将Student 暴露为:

Student getStu();

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

使用以下实现:

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

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

  1. Dagger 依赖关系图配置错误,如上所述

a) MainActivity 需要 RestApiHelper 提供的 UrlComponent 而不是 AppComponent 所以首先使用 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) 在 Application 类中构建 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 对象设置正确并具有互联网应用权限等。您可以使用 lambda 等进一步优化您的代码,因为我保留了大部分代码因为它是为了理解。

【讨论】:

  • 你成就了我的一天。谢谢你
  • 现在作为另一个问题:如何将 Application 模块提供程序注入 MainActivity ? @Pavneet_Singh
  • @hamidrezahaajhoseini 只需在 UrlComponent 中添加 Application getApplication(); (和其他),或者您可以将应用程序模块添加到活动组件中。我很高兴能帮上忙,祝编码愉快!
【解决方案2】:

首先,您提供 RestApi 的方法是错误的。 我应该是这样的

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

    }

【讨论】:

  • @pPaul Ost 是的,但问题不是问题
猜你喜欢
  • 2017-08-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多