Android Dagger 2 +Retrofit+ RecyclerView
这是在我们的Android应用程序中使用Retrofit和RecyclerView实现Dagger 2的一站式完整教程。
Dagger 2是一个依赖项注入库,对于干净的代码和体系结构至关重要。
有关Dependency Injection和Dagger 2的基本概述,请在继续之前参考本教程。
什么是依赖注入?
简而言之,Dependency Injection无需使用new关键字自己创建实例,而是从外部为您提供这些实例。
在最坏的情况下,依赖注入是我们的朋友。
由于没有紧密耦合,因此它有助于快速重构代码并有助于测试单个事物。
它提高了可读性和可维护性。
不管依赖注入的知识如何,在面向对象编程中的某些时候,您肯定已经使用了它。
以下示例肯定会让您想起它。
public class A {
public static void main(String[] args){
B b = new B();
C c = new C(b);
D d = new D(b, c);
E e = new E(d, b);
A a = new A(e, b);
}
}
A类是受抚养人。
E和B类是注入的依赖项。
如您在上面的代码中看到的,我们不是在构造函数中初始化类,而是分别进行处理。
虽然我们上面还有样板代码。
有关Dagger 2的重要注意事项
它在编译时检查并绘制依赖对象图。
我们需要使用注释:@ Module,@ Provides,@ Inject,@ Component,@ Scope。
对于两个冲突的依赖项,例如Application具有不同的Context,而Activity具有不同的。
Dagger 2需要@Qualifier我们的@Named注释来区分。在接口上设置了@Component。
它充当桥梁,用于向Java类提供在@Module中指定的依赖项。
该依赖关系将在我们的Java类中使用@Inject检索。Dagger 2无法注入私有字段。
通过示例可以最好地理解Dagger 2。
已经有一段时间了,我们试图创建一个Android应用程序,使用RecyclerView和Retrofit尽可能最好地解释这些概念。
我们将使用StarWars API。
项目结构
我们为依赖注入组件,模块,作用域和限定符创建了一个单独的软件包di。
活动进入ui包。
API方法和POJO类分别位于retrofit和pojo包中。
"适配器"包含RecyclerViewAdapter类。
该项目包含两个活动– MainActivity和DetailActivity。
在应用程序的" build.gradle"中添加以下库依赖项。
implementation 'com.android.support:design:27.1.0'
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
implementation group: 'com.squareup.retrofit2', name: 'converter-gson', version: '2.3.0'
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.10.0'
implementation group: 'com.squareup.okhttp3', name: 'logging-interceptor', version: '3.9.0'
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
implementation(group: 'com.squareup.retrofit2', name: 'retrofit', version: '2.3.0') {
exclude module: 'okhttp'
}
implementation 'com.google.dagger:dagger-android:2.11'
implementation 'com.google.dagger:dagger-android-support:2.11'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.13'
确保已在AndroidManifest文件中添加了Internet权限。
依赖注入packagescope
范围定义了所有这些组件的使用位置。
在此应用程序中,它们是两个:ActivityScope和ApplicationScope。
ActivityScope.java
package com.theitroad.dagger2retrofitrecyclerview.di.scopes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.CLASS)
public @interface ActivityScope {
}
ApplicationScope.java
package com.theitroad.dagger2retrofitrecyclerview.di.scopes;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.CLASS)
public @interface ApplicationScope {
}
@ActivityScope和@ApplicationScope将在以后的组件上使用。
限定词
因此,上下文可以是活动或者应用程序。
Dagger2如何区分它们?使用限定词。
下面定义了ActivityContext.java和ApplicationContext.java类。
package com.theitroad.dagger2retrofitrecyclerview.di.qualifier;
import javax.inject.Qualifier;
@Qualifier
public @interface ActivityContext {
}
package com.theitroad.dagger2retrofitrecyclerview.di.qualifier;
import javax.inject.Qualifier;
@Qualifier
public @interface ApplicationContext {
}
现在,无论其中使用Context的模块中,都必须根据用例使用@ApplicationContext或者@ActivityContext对其进行注释。
模组
模块是将通过组件向依赖项提供依赖项的模块。
让我们先计划一下依赖图。
因此," APIInterface"不依赖任何内容。
ApplicationComponent将容纳Retrofit和AppContext模块。
MainActivity将保存适配器和活动上下文模块以及ApplicationComponent的依赖项。
DetailActivityComponent不包含任何其自身的模块。
它仅使用ApplicationComponent中存在的那些。
上下文模块
package com.theitroad.dagger2retrofitrecyclerview.di.module;
import android.content.Context;
import com.theitroad.dagger2retrofitrecyclerview.di.qualifier.ApplicationContext;
import com.theitroad.dagger2retrofitrecyclerview.di.scopes.ApplicationScope;
import dagger.Module;
import dagger.Provides;
@Module
public class ContextModule {
private Context context;
public ContextModule(Context context) {
this.context = context;
}
@Provides
@ApplicationScope
@ApplicationContext
public Context provideContext() {
return context;
}
}
我们为上下文指定了应用程序级别范围和限定符。
改造模块
package com.theitroad.dagger2retrofitrecyclerview.di.module;
import com.theitroad.dagger2retrofitrecyclerview.di.scopes.ApplicationScope;
import com.theitroad.dagger2retrofitrecyclerview.retrofit.APIInterface;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
public class RetrofitModule {
@Provides
@ApplicationScope
APIInterface getApiInterface(Retrofit retroFit) {
return retroFit.create(APIInterface.class);
}
@Provides
@ApplicationScope
Retrofit getRetrofit(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl("https://swapi.co/api/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
}
@Provides
@ApplicationScope
OkHttpClient getOkHttpCleint(HttpLoggingInterceptor httpLoggingInterceptor) {
return new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.build();
}
@Provides
@ApplicationScope
HttpLoggingInterceptor getHttpLoggingInterceptor() {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return httpLoggingInterceptor;
}
}
@Provides表示将从该方法向其依赖项提供依赖项。
对于翻新,我们使用ApplicationScope。
MainActivityContextModule
package com.theitroad.dagger2retrofitrecyclerview.di.module;
import android.content.Context;
import com.theitroad.dagger2retrofitrecyclerview.di.qualifier.ActivityContext;
import com.theitroad.dagger2retrofitrecyclerview.di.scopes.ActivityScope;
import com.theitroad.dagger2retrofitrecyclerview.ui.MainActivity;
import dagger.Module;
import dagger.Provides;
@Module
public class MainActivityContextModule {
private MainActivity mainActivity;
public Context context;
public MainActivityContextModule(MainActivity mainActivity) {
this.mainActivity = mainActivity;
context = mainActivity;
}
@Provides
@ActivityScope
public MainActivity providesMainActivity() {
return mainActivity;
}
@Provides
@ActivityScope
@ActivityContext
public Context provideContext() {
return context;
}
}
上面的模块用于提供活动上下文和活动实例。
适配器模块
AdapterModule的代码如下。
package com.theitroad.dagger2retrofitrecyclerview.di.module;
import com.theitroad.dagger2retrofitrecyclerview.adapter.RecyclerViewAdapter;
import com.theitroad.dagger2retrofitrecyclerview.di.scopes.ActivityScope;
import com.theitroad.dagger2retrofitrecyclerview.ui.MainActivity;
import dagger.Module;
import dagger.Provides;
@Module(includes = {MainActivityContextModule.class})
public class AdapterModule {
@Provides
@ActivityScope
public RecyclerViewAdapter getStarWarsPeopleLIst(RecyclerViewAdapter.ClickListener clickListener) {
return new RecyclerViewAdapter(clickListener);
}
@Provides
@ActivityScope
public RecyclerViewAdapter.ClickListener getClickListener(MainActivity mainActivity) {
return mainActivity;
}
}
它用于根据POJO数据创建RecyclerViewAdapter。
另外,ClickListener是RecyclerViewAdapter类中定义的接口,用于从Activity本身触发click listener回调方法。
由于我们在定义中加入了" MainActivityContextModule",因此注入了MainActivity依赖项。
让我们看一下APIInterface和POJO类。
下面给出了APIInterface.java类的代码。
package com.theitroad.dagger2retrofitrecyclerview.retrofit;
import com.theitroad.dagger2retrofitrecyclerview.pojo.Film;
import com.theitroad.dagger2retrofitrecyclerview.pojo.StarWars;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
import retrofit2.http.Url;
public interface APIInterface {
@GET("people/?")
Call<StarWars> getPeople(@Query("format") String format);
@GET
Call<Film> getFilmData(@Url String url, @Query("format") String format);
}
@Url用于在Retrofit中进行动态URL调用。
该URL将在运行时指定。
POJO类是从.jsonschema2pojo创建的。
StarWars.java
package com.theitroad.dagger2retrofitrecyclerview.pojo;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class StarWars {
@SerializedName("results")
public List<People> results = null;
public class People {
@SerializedName("name")
public String name;
@SerializedName("height")
public String height;
@SerializedName("mass")
public String mass;
@SerializedName("birth_year")
public String birthYear;
@SerializedName("gender")
public String gender;
@SerializedName("homeworld")
public String homeworld;
@SerializedName("films")
public List<String> films = null;
}
}
电影.java
package com.theitroad.dagger2retrofitrecyclerview.pojo;
import com.google.gson.annotations.SerializedName;
public class Film {
@SerializedName("title")
public String title;
@SerializedName("director")
public String director;
}
我们只是解析将在应用程序中使用的值。
组件
在组件中,我们将包含模块。
我们需要公开将在"活动/应用程序"中使用的字段。
ApplicationComponent.java
package com.theitroad.dagger2retrofitrecyclerview.di.component;
import android.content.Context;
import com.theitroad.dagger2retrofitrecyclerview.MyApplication;
import com.theitroad.dagger2retrofitrecyclerview.di.module.ContextModule;
import com.theitroad.dagger2retrofitrecyclerview.di.module.RetrofitModule;
import com.theitroad.dagger2retrofitrecyclerview.di.qualifier.ApplicationContext;
import com.theitroad.dagger2retrofitrecyclerview.di.scopes.ApplicationScope;
import com.theitroad.dagger2retrofitrecyclerview.retrofit.APIInterface;
import dagger.Component;
@ApplicationScope
@Component(modules = {ContextModule.class, RetrofitModule.class})
public interface ApplicationComponent {
public APIInterface getApiInterface();
@ApplicationContext
public Context getContext();
public void injectApplication(MyApplication myApplication);
}
Dagger2将自动生成一个名为Dagger%ComponentName%的类。
例如。
DaggerApplicationComponent。injectApplication用于允许在我们的活动/应用程序中使用@Inject字段。
MainActivityComponent.java
package com.theitroad.dagger2retrofitrecyclerview.di.component;
import android.content.Context;
import com.theitroad.dagger2retrofitrecyclerview.di.module.AdapterModule;
import com.theitroad.dagger2retrofitrecyclerview.di.qualifier.ActivityContext;
import com.theitroad.dagger2retrofitrecyclerview.di.scopes.ActivityScope;
import com.theitroad.dagger2retrofitrecyclerview.ui.MainActivity;
import dagger.Component;
@ActivityScope
@Component(modules = AdapterModule.class, dependencies = ApplicationComponent.class)
public interface MainActivityComponent {
@ActivityContext
Context getContext();
void injectMainActivity(MainActivity mainActivity);
}
上面的组件也可以访问ApplicationComponent依赖项。
DetailActivityComponent.java
package com.theitroad.dagger2retrofitrecyclerview.di.component;
import com.theitroad.dagger2retrofitrecyclerview.di.scopes.ActivityScope;
import com.theitroad.dagger2retrofitrecyclerview.ui.DetailActivity;
import dagger.Component;
@Component(dependencies = ApplicationComponent.class)
@ActivityScope
public interface DetailActivityComponent {
void inject(DetailActivity detailActivity);
}
现在是时候在活动和应用程序中使用di了。
我们先来看一下布局代码。
布局
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
</android.support.constraint.ConstraintLayout>
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.DetailActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Loading..."
android:textColor="@android:color/black"
android:textSize="28sp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
</android.support.constraint.ConstraintLayout>
recycler_view_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/txtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<TextView
android:id="@+id/txtBirthYear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/txtName"
</android.support.constraint.ConstraintLayout>
在我们的类中使用依赖注入之前,请重新构建项目。
这样做是为了生成Dagger Component类。
下面给出了MyApplication.java类的代码。
package com.theitroad.dagger2retrofitrecyclerview;
import android.app.Activity;
import android.app.Application;
import com.theitroad.dagger2retrofitrecyclerview.di.component.ApplicationComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.component.DaggerApplicationComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.module.ContextModule;
public class MyApplication extends Application {
ApplicationComponent applicationComponent;
@Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent.builder().contextModule(new ContextModule(this)).build();
applicationComponent.injectApplication(this);
}
public static MyApplication get(Activity activity){
return (MyApplication) activity.getApplication();
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
}
DaggerApplicationComponent.builder()。
contextModule(new ContextModule(this))。
build();用于构建组件中存在的模块。
getApplicationComponent将用于返回我们活动中的ApplicationComponent。
不要忘记在我们的AndroidManifest.xml文件中添加上述应用程序。
UI包
下面给出MainActivity.java类的代码。
package com.theitroad.dagger2retrofitrecyclerview.ui;
import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;
import com.theitroad.dagger2retrofitrecyclerview.MyApplication;
import com.theitroad.dagger2retrofitrecyclerview.R;
import com.theitroad.dagger2retrofitrecyclerview.adapter.RecyclerViewAdapter;
import com.theitroad.dagger2retrofitrecyclerview.di.component.ApplicationComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.component.DaggerMainActivityComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.component.MainActivityComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.module.MainActivityContextModule;
import com.theitroad.dagger2retrofitrecyclerview.di.qualifier.ActivityContext;
import com.theitroad.dagger2retrofitrecyclerview.di.qualifier.ApplicationContext;
import com.theitroad.dagger2retrofitrecyclerview.pojo.StarWars;
import com.theitroad.dagger2retrofitrecyclerview.retrofit.APIInterface;
import java.util.List;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity implements RecyclerViewAdapter.ClickListener {
private RecyclerView recyclerView;
MainActivityComponent mainActivityComponent;
@Inject
public RecyclerViewAdapter recyclerViewAdapter;
@Inject
public APIInterface apiInterface;
@Inject
@ApplicationContext
public Context mContext;
@Inject
@ActivityContext
public Context activityContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
ApplicationComponent applicationComponent = MyApplication.get(this).getApplicationComponent();
mainActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityContextModule(new MainActivityContextModule(this))
.applicationComponent(applicationComponent)
.build();
mainActivityComponent.injectMainActivity(this);
recyclerView.setAdapter(recyclerViewAdapter);
apiInterface.getPeople("json").enqueue(new Callback<StarWars>() {
@Override
public void onResponse(Call<StarWars> call, Response<StarWars> response) {
populateRecyclerView(response.body().results);
}
@Override
public void onFailure(Call<StarWars> call, Throwable t) {
}
});
}
private void populateRecyclerView(List<StarWars.People> response) {
recyclerViewAdapter.setData(response);
}
@Override
public void launchIntent(String url) {
Toast.makeText(mContext, "RecyclerView Row selected", Toast.LENGTH_SHORT).show();
startActivity(new Intent(activityContext, DetailActivity.class).putExtra("url", url));
}
}
一旦发生这种情况:mainActivityComponent.injectMainActivity(this);,@ Inject出现的字段将被自动注入。
其余的工作是在进行Retrofit调用并在RecyclerViewAdapter中设置数据。
该类实现RecyclerViewAdapter.ClickListener接口回调,该回调将在每单击RecyclerView行时触发launchIntent方法。
请注意,需要使用我们之前定义的相关限定符来指定注入的上下文。
下面给出了RecyclerViewAdapter的代码。
package com.theitroad.dagger2retrofitrecyclerview.adapter;
import android.support.constraint.ConstraintLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.theitroad.dagger2retrofitrecyclerview.R;
import com.theitroad.dagger2retrofitrecyclerview.pojo.StarWars;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<StarWars.People> data;
private RecyclerViewAdapter.ClickListener clickListener;
@Inject
public RecyclerViewAdapter(ClickListener clickListener) {
this.clickListener = clickListener;
data = new ArrayList<>();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_list_row, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.txtName.setText(data.get(position).name);
holder.txtBirthYear.setText(data.get(position).birthYear);
}
@Override
public int getItemCount() {
return data.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView txtName;
private TextView txtBirthYear;
private ConstraintLayout constraintLayoutContainer;
ViewHolder(View itemView) {
super(itemView);
txtName = itemView.findViewById(R.id.txtName);
txtBirthYear = itemView.findViewById(R.id.txtBirthYear);
constraintLayoutContainer = itemView.findViewById(R.id.constraintLayout);
constraintLayoutContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clickListener.launchIntent(data.get(getAdapterPosition()).films.get(0));
}
});
}
}
public interface ClickListener {
void launchIntent(String filmName);
}
public void setData(List<StarWars.People> data) {
this.data.addAll(data);
notifyDataSetChanged();
}
}
下面给出了DetailActivity.java类的代码:
package com.theitroad.dagger2retrofitrecyclerview.ui;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.theitroad.dagger2retrofitrecyclerview.MyApplication;
import com.theitroad.dagger2retrofitrecyclerview.R;
import com.theitroad.dagger2retrofitrecyclerview.di.component.ApplicationComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.component.DaggerDetailActivityComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.component.DetailActivityComponent;
import com.theitroad.dagger2retrofitrecyclerview.di.qualifier.ApplicationContext;
import com.theitroad.dagger2retrofitrecyclerview.pojo.Film;
import com.theitroad.dagger2retrofitrecyclerview.retrofit.APIInterface;
import javax.inject.Inject;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class DetailActivity extends AppCompatActivity {
DetailActivityComponent detailActivityComponent;
@Inject
public APIInterface apiInterface;
@Inject
@ApplicationContext
public Context mContext;
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
textView = findViewById(R.id.textView);
String url = getIntent().getStringExtra("url");
ApplicationComponent applicationComponent = MyApplication.get(this).getApplicationComponent();
detailActivityComponent = DaggerDetailActivityComponent.builder()
.applicationComponent(applicationComponent)
.build();
detailActivityComponent.inject(this);
apiInterface.getFilmData(url, "json").enqueue(new Callback<Film>() {
@Override
public void onResponse(Call<Film> call, Response<Film> response) {
Film films = response.body();
String text = "Film name:\n" + films.title + "\nDirector:\n" + films.director;
textView.setText(text);
}
@Override
public void onFailure(Call<Film> call, Throwable t) {
}
});
}
}
再次使用inject方法来注入所有依赖项字段。
改型向指定的动态URL发出请求,并在TextView中显示响应。

