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

Dagger基础回顾

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

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

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

/**
 * 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该路径下,如图所示:

Dagger依赖生成文件目录

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

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

产生的对应的Province_Factory

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.

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

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

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,负责将上面两个联系起来。

@Component
public interface MainActivityComponent {

    void inject(MainActivity activity);
}

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

@Override
public void inject(MainActivity activity) {
    mainActivityMembersInjector.injectMembers(activity);
}

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

//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方法

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 接口

public interface MainMvpModel extends IModel{

    void requestAPI(ApiCallback callback);
}

Model 实现

public class MainMvpModelImpl implements MainMvpModel {

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

View 接口

public interface MainMvpView {

    void updateUI();
}

View 实现

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 接口

public interface MainMvpPresenter extends IPresenter{

    void loadData();
}

Presenter 实现

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的使用

@Module
public class MainModule {

    private MainMvpView mView;

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

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

    void inject(MainMvpActivity activity);
}

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

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,提供你想要提供的全局变量

@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编译

@Component(modules = AppModule.class)
public interface AppComponent {

    SharedPreferences sharedPreferences();

    MyApplication myApplication();
}

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

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对象

public class ComponentHolder {
    private static AppComponent myAppComponent;

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

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

Activity的Component和Module

@Component(modules = MainModule.class, dependencies = AppComponent.class)
public interface MainMvpComponent {

    void inject(MainMvpActivity activity);

}

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

@Inject
SharedPreferences sp;

....

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

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

关键的注释

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

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

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