Android依赖注入– Dagger 2
在本教程中,我们将在android应用程序中讨论并实现依赖注入(DI)。
对于过去(可能在Spring)使用过DI的那些人,可以跳过以下部分来介绍Android Dependency Injection。
什么是Android依赖注入?
您不是自己创建对象,而是让别人为您创建对象。
让我们分析一下以上语句的含义。
想象以下情况:我们有一个Activity,并且希望将一些数据保存在SharedPreferences中。
在没有DI的情况下执行此操作将导致我们实例化,保存,从SharedPreferences中检索数据,所有这些都在Activity的样板代码内。
下图或者多或者少地描绘了某种东西。
没有DI
上面的方法可能会导致一些严重的问题,尤其是当您的代码库增大时。
您将遇到单元测试问题,太多样板代码,难以修改的当前代码库(有太多实例需要关注)(共享实例,作用域实例)。
其中Android Dependency Injection Pattern可以大大增强您的代码。
如依赖项中所述和下图所示,不是让每次在我们的Activity中实例化SharedPreferences,而是让它从另一个类中注入。
因此,我们的活动取决于SharedPreferences。
因此,SharedPreferences充当我们Activity的依赖项。
SharedPreferences从外部注入到我们的Activity中,而不是实例化。
为什么要使用Android依赖注入?
如果您是的老板,您希望自己做所有事情还是委派任务?
您是否愿意聘请新手,而不是已经知道任务的专家?
在理想情况下,以上两个问题的答案应该是后一种选择。
这就是依赖注入如此重要的原因。
与其每次在我们的活动中实例化SharedPreferences,数据库和网络库,我们都希望在其他地方创建它们的实例,并在需要时将它们注入到我们的活动中。
这些依赖关系通常在我们的类中使用构造函数,我们将很快看到。
什么是Dagger2?
Dagger库由Square的开发人员在2012年创建。
Dagger1用于创建类的实例并通过Reflections注入依赖项。
Dagger 2在第一个版本的基础上进行了改进,并与Google的开发人员团队合作,引入了一个更快,更完善的版本,而没有使用Reflections。
Dagger 2是一个编译时android依赖项注入框架,并使用Java Specification Request(JSR)330并使用注释处理器。
以下是Dagger 2中使用的基本注释:
@Module:此类用于构造最终将作为依赖项提供的对象的类。
@Provides:用于Module类中将返回对象的方法上。
@Inject:用于构造函数,字段或者方法,表示已请求依赖项。
@Component:Module类未直接向请求它的类提供依赖项。
为此,使用了一个Component接口,它充当@Module和@Inject之间的桥梁。@Singleton:这表明将仅创建依赖项对象的单个实例。
不用担心上面的东西会打扰您。
现在,我们将开发一个使用Dagger并提供SharedPreferences作为我们Activity中的依赖项的应用程序。
Android依赖注入示例项目
我们的应用程序将包含一个单一活动,其中我们将保存并从EditTexts中检索数据到SharedPreferences中。
首先,在build.gradle文件中添加以下依赖项。
compile 'com.google.dagger:dagger:2.10' annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
让我们分析一下如何使用Dagger和上述注释在我们的应用程序中引入依赖注入的功能。
要在另一个类中实例化SharedPreferences实例,我们需要传递当前上下文。
组件完成将东西传递给Module类的工作,并将依赖项注入到我们的Activity中。
Android Dagger 2代码
首先让我们看一下" activity_main.xml"布局。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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="com.theitroad.dagger2.MainActivity"> <EditText android:id="@+id/inUsername" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="8dp" android:hint="Username" <EditText android:id="@+id/inNumber" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/inUsername" android:layout_margin="8dp" android:inputType="number" android:hint="Number" <Button android:id="@+id/btnSave" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="SAVE" android:layout_below="@+id/inNumber" android:layout_toLeftOf="@+id/btnGet" android:layout_toStartOf="@+id/btnGet" android:layout_marginRight="8dp" android:layout_marginEnd="8dp" <Button android:id="@+id/btnGet" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="GET" android:layout_below="@+id/inNumber" android:layout_alignRight="@+id/inNumber" android:layout_alignEnd="@+id/inNumber" </RelativeLayout>
下面给出了SharedPrefModule.java类的代码。
package com.theitroad.dagger2; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; @Module public class SharedPrefModule { private Context context; public SharedPrefModule(Context context) { this.context = context; } @Singleton @Provides public Context provideContext() { return context; } @Singleton @Provides public SharedPreferences provideSharedPreferences(Context context) { return PreferenceManager.getDefaultSharedPreferences(context); } }
上下文是在构造函数中设置的。
返回依赖对象的方法通常以provider开头,因此命名为" provideSharedPreferences()"。
下面给出了MyComponent.java接口的代码。
package com.theitroad.dagger2; import javax.inject.Singleton; import dagger.Component; @Singleton @Component(modules = {SharedPrefModule.class}) public interface MyComponent { void inject(MainActivity activity); }
在@Component批注中提到了所有模块。
允许请求模块声明的依赖项的活动,服务或者片段(使用@Inject)应在此接口中使用单独的inject()
方法进行声明。
下面给出了MainActivity.java类的代码:
package com.theitroad.dagger2; import android.content.SharedPreferences; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import javax.inject.Inject; public class MainActivity extends AppCompatActivity implements View.OnClickListener { EditText inUsername, inNumber; Button btnSave, btnGet; private MyComponent myComponent; @Inject SharedPreferences sharedPreferences; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); myComponent = DaggerMyComponent.builder().sharedPrefModule(new SharedPrefModule(this)).build(); myComponent.inject(this); } private void initViews() { btnGet = findViewById(R.id.btnGet); btnSave = findViewById(R.id.btnSave); inUsername = findViewById(R.id.inUsername); inNumber = findViewById(R.id.inNumber); btnSave.setOnClickListener(this); btnGet.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnGet: inUsername.setText(sharedPreferences.getString("username", "default")); inNumber.setText(sharedPreferences.getString("number", "12345")); break; case R.id.btnSave: SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putString("username", inUsername.getText().toString().trim()); editor.putString("number", inNumber.getText().toString().trim()); editor.apply(); break; } } }
将Dagger绑定到我们的应用程序中的代码是:
myComponent = DaggerMyComponent.builder().sharedPrefModule(new SharedPrefModule(this)).build();
" Dagger"关键字位于组件类名称的前面。
如果组件类名称为AppComponent,则结果为DaggerAppComponent。
将模块类名称转换为驼峰格式,并通过传递上下文在模块内部对其进行初始化(最终,SharedPreferences要求使用它进行初始化!)。
在此阶段,Dagger可以生成文件,因此您需要重建项目以使其能够执行其工作("构建">"重建项目")。
在此之后可以使用SharedPreferences依赖项对象:myComponent.inject(this)
。
我们可以对SharedPreferences进行操作,而无需在Activity中对其进行初始化。
我们可以对Retrofit适配器等做同样的事情,以使我们的代码更简单,更易于使用/测试。
上述应用程序的输出如下:
代替@Singleton,我们也可以定义不同的范围,例如@ PerApp,@ PerActivity,以使实例创建的数量分别取决于Application/Activity的生存期。