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 类中的contextapplication 和由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 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 是的,但问题不是问题

以上是关于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

Android二手交易平台,dagger2+mvp+Bmob后台云搭建