Dagger2入门学习之MVP项目整合(上)

Dagger基础回顾

在前面一篇Dagger2基础传送门我们得出了一个结论。

  1. 若一个类(Xx)的构造函数被@inject标注,则该类编译时会产生Xx_Factory.
  2. 若一个类(Xx)的成员变量被@inject标注,则该类编译时会产生Xx_MembersInjector.
  3. @Component标注的接口(Xx)在编译时生成DaggerXx,负责将上面两个联系起来。

下面我们通过前面提到的这个例子再来回顾一下这个结论

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Created by 水寒 on 2017/11/16.
*/

public class Province {

@Inject
public City city;

@Inject
public Province(City city) {
this.city = city;
}

public String showAddress() {
return "四川省" + city.show();
}
}

Dagger2会在编译过程中生成对应的依赖项,这些依赖项在Android Studio该路径下,如图所示:

第一条结论:若一个类(Xx)的构造函数被@inject标注,则该类编译时会产生Xx_Factory.

1
2
3
4
@Inject
public Province(City city) {
this.city = city;
}

产生的对应的Province_Factory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class Province_Factory implements Factory<Province> {

private final MembersInjector<Province> provinceMembersInjector;

private final Provider<City> cityProvider;

public Province_Factory(
MembersInjector<Province> provinceMembersInjector, Provider<City> cityProvider) {
this.provinceMembersInjector = provinceMembersInjector;
this.cityProvider = cityProvider;
}

@Override
public Province get() {
return MembersInjectors.injectMembers(
provinceMembersInjector, new Province(cityProvider.get()));
}

public static Factory<Province> create(
MembersInjector<Province> provinceMembersInjector, Provider<City> cityProvider) {
return new Province_Factory(provinceMembersInjector, cityProvider);
}
}

上面代码看似比较多,其实做了一件很简单的事情,创建实例create方法(这里注入了cityProvider,通过get方法可以得到City实例)

我们接下来看看Province_Factory的构造函数,我们看到传进一个对应的City_MemberInjector和City的Provider.

1
2
3
public interface Provider<T> {
T get();
}

第二条结论:若一个类(Xx)的成员变量被@inject标注,则该类编译时会产生Xx_MembersInjector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class City_MembersInjector implements MembersInjector<City> {

private final Provider<Street> streetProvider;

public City_MembersInjector(Provider<Street> streetProvider) {
this.streetProvider = streetProvider;
}

public static MembersInjector<City> create(Provider<Street> streetProvider) {
return new City_MembersInjector(streetProvider);
}

@Override
public void injectMembers(City instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.street = streetProvider.get();
}
}

生成的City_MembersInjector中我们注意其中重写的MembersInjector接口的injectMembers方法,这里进行了实例真正的赋值操作(关联)。

第三条结论:@Component标注的接口(Xx)在编译时生成DaggerXx,负责将上面两个联系起来。

1
2
3
4
5
@Component
public interface MainActivityComponent {

void inject(MainActivity activity);
}

在生成的DaggerMainActivityComponent里面我们看看MainActivityComponet的inject方法实现

1
2
3
4
@Override
public void inject(MainActivity activity) {
mainActivityMembersInjector.injectMembers(activity);
}

通过injectMembers()完成真正目标对象实例化以及依赖操作

1
2
3
4
5
6
7
//MainActivity_MembersInjector.java  
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.province = provinceProvider.get();
}

我们再来看看DaggerMainActivityComponent中的initialize方法

1
2
3
4
5
6
7
8
9
10
11
12
private void initialize(final Builder builder) {

this.cityMembersInjector = City_MembersInjector.create(Street_Factory.create());

this.cityProvider = City_Factory.create(cityMembersInjector, Street_Factory.create());

this.provinceMembersInjector = Province_MembersInjector.create(cityProvider);

this.provinceProvider = Province_Factory.create(provinceMembersInjector, cityProvider);

this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provinceProvider);
}

可以看到inject()和initialize()刚好是相反的过程,直到找到依赖的源头完成源头对象的实例化,即这里的Street_Factory的get()方法的返回值;在Street实例化完成之后返回给City完成City实例化,City实例化完之后对自己的成员重新赋值了一遍,即产生依赖关系:

MVP模式

MVP 模式是 MVC 模式在 Android 上的一种变体,要介绍 MVP 就得先介绍 MVC。在 MVC 模式中,Activity 应该是属于 View 这一层。而实质上,它既承担了 View,同时也包含一些 Controller 的东西在里面。这对于开发与维护来说不太友好,耦合度大高了。把 Activity 的 View 和 Controller 抽离出来就变成了 View 和 Presenter,这就是 MVP 模式。

MVP模式的作用:

  • 分离了视图逻辑和业务逻辑,降低了耦合
  • Activity 只处理生命周期的任务,代码变得更加简洁
  • 视图逻辑和业务逻辑分别抽象到了 View 和 Presenter 的接口中去,提高代码的可阅读性
  • Presenter 被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
  • 把业务逻辑抽到 Presenter 中去,避免后台线程引用着 Activity 导致 Activity 的资源无法被系统回收从而引起内存泄露和 OOM

MVP模式将原来一个Activity可以解决的问题,分成了M、V、P三部分,这在项目中会产品大量的类,如果利用Dagger2来进行这些类的有效管理,是接下来我们要思考的问题。

MVP实例

Model 接口

1
2
3
4
public interface MainMvpModel extends IModel{

void requestAPI(ApiCallback callback);
}

Model 实现

1
2
3
4
5
6
7
public class MainMvpModelImpl implements MainMvpModel {

@Override
public void requestAPI(ApiCallback callback) {
Log.d("TEST", "请求接口");
}
}

View 接口

1
2
3
4
public interface MainMvpView {

void updateUI();
}

View 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MainMvpActivity extends AppCompatActivity implements MainMvpView{

private MainMvpPresenter mPresenter = new MainMvpPresenterImpl();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter.attachView(this);
mPresenter.loadData();
}

@Override
public void updateUI() {

}

@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView(this);
}
}

Presenter 接口

1
2
3
4
public interface MainMvpPresenter extends IPresenter{

void loadData();
}

Presenter 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MainMvpPresenterImpl implements MainMvpPresenter {

private MainMvpModel mModel;
private MainMvpView mView;


public MainMvpPresenterImpl(){
mModel = new MainMvpModelImpl();
}

@Override
public void loadData() {
mModel.requestAPI(new ApiCallback() {
@Override
public void callback(int code) {

}
});
}

@Override
public void attachView(IView view) {
mView = (MainMvpView) view;
}

@Override
public void detachView(IView view) {
mView = null;
}
}

上面是典型的MVP开发安卓项目的结构,我们看到Presenter中强制注入了View和Model.如果我们要修改View和Model的构造方法的时候就需要修改Presenter, 同样我们修改Presenter的构造方法的时候就需要在对应的Activity中做相应的修改。接下来我们使用Dagger2的注释来实现这种依赖关系。

Component和Module

在使用Dagger实现MVP结构前我们得先知道关于Module的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Module
public class MainModule {

private MainMvpView mView;

public MainModule(MainMvpView view){
mView = view;
}

@Provides
MainMvpView provideMainView() {
return mView;
}
}
1
2
3
4
5
@Component(modules = {MainModule.class})
public interface MainMvpActivityComponent {

void inject(MainMvpActivity activity);
}

这个@Module相当于一个额外的拓展,通过@Provides提供给一种直接的依赖关系,假如我们不能通过给构造函数添加@Inject,则可以通过该方式进行依赖注入。

1
2
3
4
DaggerMainMvpActivityComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);

现在我们来理一理这三者的关系

  • @Inject : 注入,被注解的构造方法会自动编译生成一个Factory工厂类提供该类对象。

  • @Component: 注入器,类似快递员,作用是将产生的对象注入到需要对象的容器中,供容器使用。

  • @Module: 模块,类似快递箱子,在Component接口中通过@Component(modules =
    xxxx.class),将容器需要的商品封装起来,统一交给快递员(Component),让快递员统一送到目标容器中。

全局类 AppComponent & AppModule

开发中,局部变量尚还好说,我们在没有Dagger的时候,用的时候new一个就可以了,类似于SharedPerferences这种对象我们就比较头疼,也没有必要每次都去context.getSharedPerferences()吧,但是我们有了Dagger,我们可以通过依赖注入的方式,仅仅初始化一次,之后每次想使用,只需要@Inject注解,就能直接使用了。

1. 创建AppModule,提供你想要提供的全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Module
public class AppModule {

private MyApplication application;

public AppModule(MyApplication application) {
this.application = application;
}

//提供全局的sp对象
@Provides
SharedPreferences provideSharedPreferences(){
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}

//提供全局的Application对象
@Provides
MyApplication provideApplication(){
return application;
}

//你还可以提供更多.......
}

2. 创建AppComponent,然后ctrl+F9编译

1
2
3
4
5
6
7
@Component(modules = AppModule.class)
public interface AppComponent {

SharedPreferences sharedPreferences();

MyApplication myApplication();
}

3. 编译成功后,修改Application类,把Component注入器注入你的Application中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyApplication extends Application{

@Override
public void onCreate() {
super.onCreate();
inject();
}

private void inject() {
AppComponent appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
ComponentHolder.setAppComponent(appComponent);
}
}

4. ComponentHolder类,方便获取appComponent对象

1
2
3
4
5
6
7
8
9
10
11
public class ComponentHolder {
private static AppComponent myAppComponent;

public static void setAppComponent(AppComponent component) {
myAppComponent = component;
}

public static AppComponent getAppComponent() {
return myAppComponent;
}
}

Activity的Component和Module

1
2
3
4
5
6
@Component(modules = MainModule.class, dependencies = AppComponent.class)
public interface MainMvpComponent {

void inject(MainMvpActivity activity);

}

请注意dependencies = AppComponent.class, 这句话意思是可以依赖全局的AppComponent,相当于一种继承关系。这样我们的MainMvpActivity中就可以使用SharePreference了(因为AppComponent注入器中已经有AppModule了)。

1
2
3
4
5
6
7
8
9
10
@Inject
SharedPreferences sp;

....

DaggerA02Component.builder()
.appComponent(ComponentHolder.getAppComponent())//添加appComponent注入器
.mainModule(new MainModule(this))
.build()
.inject(this);

关于Android中Dagger实现MVP下篇中再看。

关键的注释

Dagger中的关键注释,如果有点不清晰,没关系,后面会慢慢理解。

注解 用法
@Module Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)
@Provide 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
@Singleton 当前提供的对象将是单例模式 ,一般配合@Provides一起出现
@Component 用于接口,这个接口被Dagger2用于生成用于模块注入的代码
@Inject 在需要依赖的地方使用这个注解。(你用它告诉Dagger这个 构造方法,成员变量或者函数方法需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。)
@Scope Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。

参考:谷歌官方Demo https://google.github.io/dagger//android.html
参考:http://blog.csdn.net/mq2553299/article/details/73065745
参考:https://www.jianshu.com/p/39d1df6c877d