【问题标题】:How can i inject my Retrofit DataService using Dagger2如何使用 Dagger2 注入我的 Retrofit DataService
【发布时间】:2020-02-27 12:12:19
【问题描述】:

在我的 MVP 架构中,我有改造实例

public class RetrofitInstance {
    private static Retrofit retrofit;
    private static final String BASE_URL = "http://api.openweathermap.org/data/2.5/";

    /**
     * Create an instance of Retrofit object
     * */
    public static Retrofit getRetrofitInstance() {
        if (retrofit == null) {
            retrofit = new retrofit2.Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

和它的数据服务

public interface GetNoticeDataService {
    @GET("weather?appid=0194877ecdcac230396a119c01d46100")
    Observable<NoticeList> getNoticeData(@Query("lat") double lat , @Query("lon") double lon );
}

我还有 DataInteractor,它使用 RxJava Observable 服务来调用 api

public class GetNoticeIntractorImpl implements MainContract.GetNoticeIntractor {
    private LatLng getloc(){
        return currentLocation;
    }
    @SuppressLint("CheckResult")
    @Override
    public void getNoticeArrayList(final OnFinishedListener onFinishedListener) {

        /** Create handle for the RetrofitInstance interface*/
        GetNoticeDataService service = RetrofitInstance.getRetrofitInstance().create(GetNoticeDataService.class);
        /** Using RxJava Observable response to handle retrofit api*/
        if(currentLocation!=null) {
            service.getNoticeData(getloc().latitude, getloc().longitude)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(items -> onFinishedListener.onFinished(items.getNoticeArrayList(), items.getMain(), items.getWind()), onFinishedListener::onFailure);

        }
    }

}

这里是主持人

public class MainPresenterImpl implements MainContract.presenter, MainContract.GetNoticeIntractor.OnFinishedListener {
 private MainContract.MainView mainView;
        private MainContract.GetNoticeIntractor getNoticeIntractor;
        @Inject
        public MainPresenterImpl(MainContract.MainView mainView, MainContract.GetNoticeIntractor getNoticeIntractor) {
            this.mainView = mainView;
            this.getNoticeIntractor = getNoticeIntractor;
        }

        @Override
        public void onDestroy() {

            mainView = null;

        }

        @Override
        public void onRefreshButtonClick() {

            if(mainView != null){
                mainView.showProgress();
            }
            getNoticeIntractor.getNoticeArrayList(this);
        }

        @Override
        public void requestDataFromServer() {
            getNoticeIntractor.getNoticeArrayList(this);
        }


        @Override
        public void onFinished(ArrayList<Notice> noticeArrayList, Main main, Wind wind) {
            if(mainView != null){
                mainView.setDataToRecyclerView(noticeArrayList,main,wind);
                mainView.hideProgress();
            }
        }



        @Override
        public void onFailure(Throwable t) {
            if(mainView != null){
                mainView.onResponseFailure(t);
                mainView.hideProgress();
            }
        }
    }

主合约

public interface MainContract {
    /**
     * Call when user interact with the view and other when view OnDestroy()
     * */
    interface presenter{

        void onDestroy();

        void onRefreshButtonClick();

        void requestDataFromServer();

    }

    /**
     * showProgress() and hideProgress() would be used for displaying and hiding the progressBar
     * while the setDataToRecyclerView and onResponseFailure is fetched from the GetNoticeInteractorImpl class
     **/
    interface MainView {

        void showProgress();

        void hideProgress();

        void setDataToRecyclerView(ArrayList<Notice> noticeArrayList, Main main, Wind wind);

        void onResponseFailure(Throwable throwable);

    }

    /**
     * Intractors are classes built for fetching data from your database, web services, or any other data source.
     **/
    interface GetNoticeIntractor {

        interface OnFinishedListener {
            void onFinished(ArrayList<Notice> noticeArrayList, Main main, Wind wind);
            void onFailure(Throwable t);
        }
        void getNoticeArrayList(OnFinishedListener onFinishedListener);

    }
}

MyActivity 适配器

public class NoticeAdapter extends RecyclerView.Adapter<NoticeAdapter.EmployeeViewHolder> {

    private static Wind wind;
    private static ArrayList<Notice> dataList;
    private static Main main;
    private Date currentTime = Calendar.getInstance().getTime();
    public static String date;
    private Context mContext;

    private RecyclerItemClickListener recyclerItemClickListener;
    public NoticeAdapter(ArrayList<Notice> dataList, Main main, Wind wind, RecyclerItemClickListener recyclerItemClickListener,Context context) {
        NoticeAdapter.dataList = dataList;
        NoticeAdapter.main = main;
        NoticeAdapter.wind = wind;
        this.recyclerItemClickListener = recyclerItemClickListener;
        this.mContext=context;
    }

    @NonNull
    @Override
    public EmployeeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view = layoutInflater.inflate(R.layout.single_view_row, parent, false);
        return new EmployeeViewHolder(view);
    }

    @SuppressLint("SetTextI18n")
    @Override
    public void onBindViewHolder(@NonNull EmployeeViewHolder holder, @SuppressLint("RecyclerView") final int position) {
        setDate(currentTime.toString().substring(0,currentTime.toString().length()-18));
        if(getAddressMap()!=null){holder.txtNoticeAddress.setText("Loc: "+getAddressMap());}else{holder.txtNoticeAddress.setText("Loc: Unknown location");}
        holder.imageIcon.setImageURI(Uri.parse("android.resource://com.locweather/drawable/i"+dataList.get(position).getIcon()));
        holder.txtNoticeWind.setText("Wind: "+roundUp(+wind.getSpeed())+"m/s, "+arrow());
        holder.txtNoticeTempMain.setText(roundUp(+main.getTemp())+"°C");
        holder.txtNoticeWeather.setText(dataList.get(position).getWeather()+" : "+dataList.get(position).getInfo());
        holder.txtNoticeTemp.setText("Feels: "+roundUp(+main.getFeelsLike())+"°C ");
        holder.txtNoticeTime.setText(date);
        holder.txtNoticeHumidity.setText("Humidity: "+main.getHumidity()+"%");
        holder.txtNoticePressure.setText("Pressure: "+main.getPressure()+"hPa");
        holder.itemView.setOnClickListener(v -> {
            recyclerItemClickListener.onItemClick();
            saveNoticeList(mContext,dataList); });
        holder.saveButton.setOnClickListener(v -> {
            recyclerItemClickListener.onItemClick();
            saveNoticeList(mContext,dataList); });
    }

    private static String getAddressMap() {
        return MapsActivity.addressMap;
    }

    private static void setDate(String date) {
        NoticeAdapter.date = date;
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }
    private static LatLng getloc(){
        return currentLocation;
    }

    class EmployeeViewHolder extends RecyclerView.ViewHolder {
        ImageView imageIcon;
        Button saveButton;
        TextView txtNoticeWeather, txtNoticeTempMain,txtNoticeTemp, txtNoticeHumidity,txtNoticeAddress,txtNoticePressure,txtNoticeWind,txtNoticeTime;

        EmployeeViewHolder(View itemView) {
            super(itemView);
            saveButton=itemView.findViewById(R.id.save_button);
            imageIcon=itemView.findViewById(R.id.image_icon);
            txtNoticeTime= itemView.findViewById(R.id.txt_time);
            txtNoticeWind= itemView.findViewById(R.id.txt_notice_wind);
            txtNoticeAddress=  itemView.findViewById(R.id.txt_notice_title);
            txtNoticeWeather =  itemView.findViewById(R.id.txt_notice_weather);
            txtNoticeTemp =  itemView.findViewById(R.id.txt_notice_temp);
            txtNoticeHumidity =  itemView.findViewById(R.id.txt_notice_humidity);
            txtNoticePressure =  itemView.findViewById(R.id.txt_notice_pressure);
            txtNoticeTempMain =  itemView.findViewById(R.id.txt_notice_temp_main);
        }
    }
    private static void saveNoticeList(Context context, List<Notice> noticeList) {
        if (context != null && noticeList != null) {
            WeatherData weatherData = new WeatherData(getAddressMap(), wind.getSpeed(), wind.getDeg(), dataList.get(0).getIcon(), dataList.get(0).getInfo(), dataList.get(0).getWeather(), main.getTemp(), main.getFeelsLike(), main.getHumidity(), main.getPressure(), date, getloc().latitude, getloc().longitude);
            WeatherDatabase.getInstance(context)
                    .weatherDao()
                    .save(weatherData);
        }
    }

如何使用 Dagger2 将此 DataService 注入我的交互器?我应该使用 Singleton 还是 Component 或其他东西? 我会很高兴得到任何帮助。

【问题讨论】:

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


    【解决方案1】:

    我假设您希望暂时保留您的 RetrofitInstance 单例,至少在您将所有 Retrofit 使用迁移到 Dagger 2 之前。

    为了注入一个接口,或者一个你无法控制其构造函数的类,你需要使用Module。这告诉 Dagger 在给定一些依赖关系的情况下如何创建该类或接口的实例:

    @Module
    public class WebServiceModule {
        @Singleton
        @Provides
        Retrofit provideRetrofit() {
            // This method tells Dagger all it needs to know about creating
            // a Retrofit instance. This will be replaced by something closer
            // to BWappsandmore's answer after RetrofitInstance is no longer needed.
            return RetrofitInstance.getRetrofitInstance();
        }
    
        @Singleton
        @Provides
        GetNoticeDataService provideGetNoticeDataService(Retrofit retrofit) {
            return retrofit.create(GetNoticeDataService.class);
        }
    

    然后可以将此模块包含在您的 Component 中,这将反过来创建它知道如何创建的任何对象,无论是从模块、传递到组件工厂方法的 @BindsInstance 值还是带有 @Inject 构造函数的类.由于您控制了交互器的构造函数,因此您可以简单地使用@Inject 对其进行注释:

    // @Reusable or @Singleton if you only need one interactor of this type.
    public class GetNoticeInteractorImpl implements MainContract.GetNoticeInteractor {
        private GetNoticeDataService service
    
        @Inject
        GetNoticeInteractorImpl(GetNoticeDataService service) {
            this.service = service;
        }
    
        private LatLng getloc(){
            return currentLocation;
        }
        @SuppressLint("CheckResult")
        @Override
        public void getNoticeArrayList(final OnFinishedListener onFinishedListener) {
    
            // Our service was injected in the constructor, so there
            // is no need to create it here.
    
            // You might also consider injecting your schedulers in the future
            // for unit testing.
            if(currentLocation!=null) {
                service.getNoticeData(getloc().latitude, getloc().longitude)
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                                .subscribe(items -> onFinishedListener.onFinished(items.getNoticeArrayList(), items.getMain(), items.getWind()), onFinishedListener::onFailure);
    
            }
        }
    
    }
    
    @Module
    abstract class AnotherModule { // or interface
        @Binds
        abstract MainContract.GetNoticeInteractor bindGetNoticeInteractor(GetNoticeInteractorImpl implementation);
    }
    

    当然,如果您最终无法访问您的交互器,这将毫无用处。由于您甚至没有创建Activity 类,因此您需要从Component 请求注入。在 Android 应用中,这通常是在 onCreate() 中的 Application 对象中创建的。

    @Component(modules = {WebServicesModule.class, AnotherModule.class})
    @Singleton
    public interface AppComponent {
        @Component.Factory
        interface Factory {
            AppComponent create(@BindsInstance Application application); // or whatever
        }
    
        void bindMainActivity(MainActivity activity);
    }
    
    // Probably not a singleton, but if you create an activity scope later, this might use that scope.
    class MainPresenter {
        @Inject
        public MainPresenter(MainContract.GetNoticeInteractor interactor) {
            this.getNoticeInteractor = interactor
        }
        // ...
    }
    
    class MainActivity {
        @Inject
        MainPresenter presenter;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            somehowGetComponent().inject(this);
            super(savedInstanceState);
            // ...
        }
        // ...
    }
    

    现在,由于您的MainActivity 需要MainPresenter,因此当您请求注入时,Dagger 会为您创建一个。这又需要GetNoticeInteractor,这需要GetNoticeInteractorImpl,等等,而Dagger 将在幕后处理所有这些。

    如果您的演示者的构造函数需要其他参数(例如视图),那么现在 MainActivity 请求直接注入演示者可能会更简单。最终,您将希望您的所有 inject(SomeClass target) 方法都引用活动、服务等,因为在您的 minSdkVersion 为 28 或更高之前,没有其他方法可以注入它们。

    【讨论】:

    • 是的,我正在使用 recyclerview 和它的活动适配器,我可以添加演示者和适配器以便更好地理解
    • 已经添加,如果可以的话,请检查并编辑我的情况,github.com/evgenyuliganets/LocWeather 如果你需要,这里是我的存储库
    • 什么是 .inject(this) 以及它在哪里声明的?
    【解决方案2】:
    @Module
    object WebServiceModule {
    
        @Singleton
        @Provides
        fun providesGetNoticeDataService(retrofit: Retrofit): GetNoticeDataService =
            retrofit.create<GetNoticeDataService>(GetNoticeDataService::class.java)
    
        @Provides
        fun providesGsonConverterFactory(): GsonConverterFactory = GsonConverterFactory.create()
    
        @Provides
        fun providesOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient =
            OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .addInterceptor(loggingInterceptor)
                .build()
    
        @Provides
        fun providesOkHttpLoggingInterceptor(): HttpLoggingInterceptor =
            HttpLoggingInterceptor().apply {
                level = HttpLoggingInterceptor.Level.BODY
            }
    
        @Provides
        fun provideRxJava2CallAdapterFactory(): RxJava2CallAdapterFactory =
            RxJava2CallAdapterFactory.create()
    
        @Provides
        fun providesRetrofit(
            client: OkHttpClient,
            converterFactory: GsonConverterFactory,
            adapterFactory: RxJava2CallAdapterFactory
        ): Retrofit = Retrofit.Builder()        
            .baseUrl(BASE_URL) 
            .addConverterFactory(converterFactory)
            .addCallAdapterFactory(adapterFactory)
            .client(client)
            .build()
    }
    

    【讨论】:

    • 是 kotlin 吗?我需要java的解决方案
    • @sippinman 它在 Kotlin 中,但我认为将其转换为 Java 或将其按原样包含在您的项目中很容易 - Android Studio 使之成为可能。
    • 你能把它转换并添加到这里吗?因为我从来没有使用过 kotlin,我不能在我的项目中反编译它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-28
    • 1970-01-01
    相关资源
    最近更新 更多