Dagger2+Retrofit 使用 Subcomponent 更改 URL:如果没有 @Provides- 或 @Produces-annotated 方法,则无法提供
Posted
技术标签:
【中文标题】Dagger2+Retrofit 使用 Subcomponent 更改 URL:如果没有 @Provides- 或 @Produces-annotated 方法,则无法提供【英文标题】:Dagger2+Retrofit change URL with Subcomponent : cannot be provided without an @Provides- or @Produces-annotated method 【发布时间】: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
。
【参考方案1】:
是否应该将所有内置于 Urlmodule 的 Providers 添加到 ApplicationModule(UrlModule 是 应用组件)
当您声明组件依赖项时,依赖组件只能使用在dependencies
的组件类中声明的公开依赖项,在您的情况下,这些是您的ApplicationComponent
类中的context
和application
和由ApplicationModule
提供。
用于验证(测试):
1) 在你的应用模块中添加一个新的 offer
// inside ApplicationModule
@Provides
Student getNum()
return new Student("aa");
2) 创建并使用ActivityComponent
对象在任何类中注入Student
。这将导致缺少提供。可以通过在ApplicationComponent
类中将Student
暴露为:
Student getStu();
2-作为一个基本问题,问题出在哪里?
使用以下实现:
-
Dagger 依赖关系图配置错误,如上所述
缺少
Rxjava
实施和改造实施
缺少 url 提供等
要解决上述问题,请按照以下步骤操作:
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 appComponent
和 urlComponent
以供以后使用
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 是的,但问题不是问题以上是关于Dagger2+Retrofit 使用 Subcomponent 更改 URL:如果没有 @Provides- 或 @Produces-annotated 方法,则无法提供的主要内容,如果未能解决你的问题,请参考以下文章
[Android] 转-RxJava+MVP+Retrofit+Dagger2+Okhttp大杂烩
Android--带你一点点封装项目 MVP+BaseActivity+Retrofit+Dagger+RxJava
MVP+Dagger2+Rxjava+Retrofit+GreenDao 开发的小应用,包括新闻图片视频3个大模块,代码封装良好
MVP+Dagger2+Rxjava+Retrofit+GreenDao 开发的小应用,包含新闻图片视频3个大模块,代码封装良好
一款最流行的MVPArms MVP快速集成框架Retoift,Okhttp,RxCache,Gson,RxLifeCycle, Dagger2,Rxjava,ImageLoader