Dagger2 快速上手使用与实现原理分析(一)

版权声明:转载请标明出处 https://blog.csdn.net/panyongjie2577 https://blog.csdn.net/panyongjie2577/article/details/88621179

参考资料

– 深入浅出Dagger2 : 从入门到爱不释手
https://www.jianshu.com/p/626b2087e2b1

Android_Dagger2篇——从小白最易上手的角度 + 最新dagger.android
https://www.jianshu.com/p/22c397354997

概述

使用过spring mvc、ButterKnife或者ARouter的人一定对@AutoWired、@BindView这样注入标签爱不释手,这极大的减少了用来初始化的代码。
那么在MVP架构下,有没有什么标签能够让Presenter也来实现自动装载的效果了,其实是有的,使用Dagger来实现组件注入,然后就可以使用@Inject注入标签自动装载Presenter对象了。

Dagger2是什么

Dagger2是Dagger的升级版,是一个依赖注入框架,第一代由大名鼎鼎的Square公司共享出来,第二代则是由谷歌接手后推出的,现在由Google接手维护.

依赖注入是面向对象编程的一种设计原则,其目的是为了降低程序耦合,这个耦合就是类之间的依赖引起的.

举个例子:

public class ClassA{
    private ClassB b
    public ClassA(ClassB b){
	    this.b = b    
    }
}

这里ClassA的构造函数里传了一个参数ClassB,随着后续业务增加也许又需要传入ClassC,ClassD.试想一下如果一个工程中有5个文件使用了ClassA那是不是要改5个文件?
这既不符合开闭原则, 也太不软工了.这个时候大杀器Dagger2就该出场了.

public class ClassA{
  @inject 
  private ClassB b 
  public ClassA(){
  }
}

环境配置

app下的build.gradle文件中添加如下配置

implementation 'com.google.dagger:dagger:2.5'
annotationProcessor "com.google.dagger:dagger-compiler:2.5"

项目中还使用到了ButterKnife, 所以还需要添加如下配置

classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1' 
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

无参无module注入示例

创建Presenter

public class PayPresenter {
    @Inject
    public PayPresenter(){}

    public boolean isSupportAlipay(){
        return true;
    }
}

创建Compenont接口

@Component
public interface AliPayComponent {
    void inject(AliPayActivity aliPayActivity);
}

进行注入

public class AliPayActivity extends AppCompatActivity {
    @Inject
    PayPresenter payPresenter;

    @BindViews({R.id.aliPayButton})
    List<Button> buttonList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_alipay);

		//将当前的Activity注入到组件中
        DaggerAliPayComponent.builder().build().inject(this);
    }
    
    @OnClick({R.id.aliPayButton})
    public void onViewClick(View view){
        switch (view.getId()){
            case R.id.aliPayButton:
				if(payPresenter.isSupportAlipay()){
                    Toast.makeText(AliPayActivity.this,
		                    "本应用支持支付宝支付",
                            Toast.LENGTH_LONG).show();
                }
                break;
        }
    }
}

注入编写过程

  • 第一步,在需要注入的类的构造函数上使用@Inject注解来进行标记,告诉dagger这个类是需要执行注入操作的。

  • 第二步,创建一个Component接口类,在接口类上使用@Component注解来标记,告诉dagger这接口类是一个组件容器,是需要使用apt进行自动生成代码的。然后在接口类的内部提供了一个inject方法来指明主要在哪里进行注入操作。

  • 第三步,在目标类AlipayActivity上使用@Inject注解来标记要实现自动装载效果的PayPresenter payPresenter;成员变量。

  • 第四步,build一下项目,build成功后,可以在以下路径app\build\generated\source\apt\debug\cheery\mvp\demo\module
    看到新生成的java类DaggerLoginComponent.java

  • 第五步,在目标类AlipayActivity上使用DaggerAliPayComponent.builder().build().inject(this);来进行注入容器操作。注入完成后,就自动实现了payPresenter的自动装载效果。

实现流程分析

@Inject关键字起作用的过程一共分为以下几步:

  • 第一步, 对DaggerLoginComponent进行了构建,返回了一个空的Builder对象。
DaggerAliPayComponent.Builder builder = 
			DaggerAliPayComponent.builder();
  • 第二步, 实现LoginCompent接口对象的实例化
AliPayComponent alipayComponent = builder.build();

通过查看DaggerAliPayComponent.java的源代码可以看到实现过程

public final class DaggerAliPayComponent implements AliPayComponent{
  private DaggerAliPayComponent (Builder builder) {}

  public static final class Builder {
    private LoginModule loginModule;
    
    public AliPayComponent build() {
      return new DaggerAliPayComponent (this);
    }
  }
}
  • 第三步, 将Activity注入到组件中
alipayComponent.inject(this);

通过查看DaggerAliPayComponent.java的源代码可以看到实现过程

public final class DaggerAliPayComponent implements AliPayComponent {
  @Override
  public void inject(AliPayActivity aliPayActivity) {
    injectAliPayActivity(aliPayActivity);
  }

  private AliPayActivity 
       injectAliPayActivity(AliPayActivity instance) {
       
    AliPayActivity_MembersInjector.injectPayPresenter(
						    instance, new PayPresenter());
    return instance;
  }
}

首先这里先调用了inject方法进行注入操作,接着调用了injectAliPayActivity方法来实现真正的注入操作。

其次,在injectAliPayActivity方法中用到了另外一个新的源码类AliPayActivity_MembersInjector调用injectPayPresenter方法来实现赋值操作,将参数中的PayPresenter对象
赋值给AliPayActivity对象的payPresenter属性对象。

public static void injectPayPresenter(AliPayActivity instance, 
				  PayPresenter payPresenter) {
  instance.payPresenter = payPresenter;
}

完成赋值操作后,就可以在AliPayActivity直接使用payPresenter属性来进行业务操作了。

有参数注入示例

无Module

注入实现代码

public class BankCard {
    @Inject
    public BankCard(){}
}

public class BankCardFactory {
    BankCard bankCard;
    @Inject
    public BankCardFactory(BankCard bankCard){
        this.bankCard = bankCard;
    }

    //进行支付操作
    public boolean pay(double money){
        return true;
    }
}

@Component
public interface BankCardComponent {
    void inject(AliPayActivity aliPayActivity);
}

使用注入类

public class AliPayActivity extends AppCompatActivity {
    @Inject
    BankCardFactory bankCardFactory;

    @BindViews({R.id.aliPayButton})
    List<Button> buttonList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_alipay);

		//将当前的Activity注入到组件中
        DaggerBankCardComponent.create().inject(this);
    }
    
    @OnClick({R.id.aliPayButton})
    public void onViewClick(View view){
        switch (view.getId()){
            case R.id.aliPayButton:
				if(bankCardFactory.pay(1000)) {
                    Toast.makeText(AliPayActivity.this,
                            "支付成功",
                            Toast.LENGTH_LONG).show();
                }
                break;
        }
    }
}

实现过程分析

和上面无参构建注入的实现过程不同的是,BankCardFactory构造函数中传入了BankCard类型的参数并且使用了@InjectBankCard类的构造函数也使用了@Inject函数
Component实现组件注解操作完成后,开始调用DaggerBankCardComponent.java中的getBankCardFactory()方法类进行自动创建BankCardBankCardFactory对象,然后将其绑定到AlipayActivity对象的bankCardFactory属性上完成自动构造。

private BankCardFactory getBankCardFactory() {
  return new BankCardFactory(new BankCard());
}

@Override
public void inject(AliPayActivity aliPayActivity) {
  injectAliPayActivity(aliPayActivity);
}

private AliPayActivity injectAliPayActivity(
                         AliPayActivity instance) {
  AliPayActivity_MembersInjector.
     injectBankCardFactory(instance, getBankCardFactory());
  return instance;
}

有Module

创建IView接口

/**
 * 视图基本操作接口
 *
 * */
public interface IBaseView<T> {
    /**
    * 是否已经添加到父视图中
    * */
    public boolean isAddedToParentView();

    /**
     * 获取当前的上下文
     * */
    public Context getCurrentContext();
}

创建Module类

//使用 模块注解 自动注解一个模块类
@(android)Module
public class LoginModule {
    private IBaseView baseView;

    @Inject
    public LoginModule(IBaseView iBaseView)
    {
        baseView = iBaseView;
    }

    @Provides
    public IBaseView provideIBaseView()
    {
        return this.baseView;
    }
}

创建Component类

// 使用 组件@Component 注解 自动注解一个 组件类
@Component(modules = LoginModule.class)
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
}

build一下项目,LoginComponent 组件在经过dagger的注解编译后,通过apt技术生成了一个新的JAVA类DaggerLoginComponent,这个新生成的类将是我们实现注解的关键, 类的内容你是可以点击进行查看的但是不支持编辑。

在这里插入图片描述

创建Presenter类

public class LoginPresenter implements IBasePresenter {

    private IBaseView mView;

    @Inject
    public LoginPresenter(IBaseView view){
        mView = view;
    }

    public void login() {
        if(null == mView){
            Log.i("Debug",  "mView is null");
            return;
        }

        Context context = mView.getCurrentContext();
        Toast.makeText(context, "登录成功!!", 
				        Toast.LENGTH_LONG).show();
    }
}

创建Activity类

public class LoginActivity extends AppCompatActivity implements IBaseView {

    @BindView(R.id.loginButton)
    Button loginButton;

    @Inject
    LoginPresenter loginPresenter;

    Unbinder unbinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        unbinder = ButterKnife.bind(this);

       DaggerLoginComponent.builder()
                .loginModule(new LoginModule(this)).
                build().inject(this);
    }

    @OnClick(R.id.loginButton)
    public void onViewClicked() {
        loginPresenter.login();
    }

    @Override
    public void onDestroy(){
        super.onDestroy();

        if(null != unbinder){
            unbinder.unbind();
        }
    }

    @Override
    public boolean isAddedToParentView() {
        return true;
    }

    @Override
    public Context getCurrentContext() {
        return getBaseContext();
    }
}

至此,我们就可以使用@Inject来实现LoginPresenter的自动装载了, 点击登录按钮,发现确实正常调用了LoginPresenter中的login方法,说明我们的LoginPresenter确实实现了自动装载,自动初始化的效果。

实现流程分析

那么问题来了,LoginPresenter的构造函数中还包含有一个IBaseView接口参数,这个接口参数是怎么传递给LoginPresenter的?

在这里插入图片描述

上面这幅图概况的描述了dagger工作的整个流程,我们这里没有使用Scope作用域关键字,所以可以先忽略它。在Actvitiy中我们通过以下代码实现了 组件的注入操作

DaggerLoginComponent.builder()
    .loginModule(new LoginModule(this)).
    build().inject(this)

这行链式编程代码太长了,看起来不容易明白它到底做了什么工作 。
我们将上面的代码分为4句,然后来看它们每一步都做了什么工作。

流程分析
我们根据上面的调用过程原理图知道,@Inject关键字起作用的过程一共分为以下几步:

  • 第一步, 对DaggerLoginComponent进行了构建,返回了一个空的Builder对象。
DaggerLoginComponent.Builder builder = DaggerLoginComponent.builder();
  • 第二步, 将LoginModule对象存储在builder对象中。
DaggerLoginComponent.Builder builder1 = 
		builder.loginModule(new LoginModule(this));

可以通过查看DaggerLoginComponent.java的源代码看到实现过程

public final class DaggerLoginComponent implements LoginComponent {  
  public static final class Builder {
    private LoginModule loginModule;

    private Builder() {}

    public Builder loginModule(LoginModule loginModule) {
      this.loginModule = Preconditions.checkNotNull(loginModule);
      return this;
    }
  }
}

这行代码,在空的Builder中加载了一个LoginModule对象,这个地方是关键的地方,这里传入了IBaseView接口的实现对象this,也就是LoginActivity对象。

  • 第三步, 实现LoginCompent接口对象的实例化
LoginComponent loginComponent = builder1.build();

可以通过查看DaggerLoginComponent.java的源代码可以看到实现过程

public final class DaggerLoginComponent implements LoginComponent {

  private LoginModule loginModule;

  private DaggerLoginComponent(Builder builder) {
    initialize(builder);
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.loginModule = builder.loginModule;
  }
  
  public static final class Builder {
    private LoginModule loginModule;
    
    public LoginComponent build() {
      if (loginModule == null) {
        throw new IllegalStateException(
           LoginModule.class.getCanonicalName()
            + " must be set");
      }
      return new DaggerLoginComponent(this);
    }
  }
}
  • 第四步, 将Activity注入到组件中
loginComponent.inject(this);

可以通过查看DaggerLoginComponent.java的源代码可以看到实现过程

public final class DaggerLoginComponent implements LoginComponent {
  private LoginModule loginModule;

  private DaggerLoginComponent(Builder builder) {
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  private LoginPresenter getLoginPresenter() {
    return new LoginPresenter(
        LoginModule_ProvideIBaseViewFactory.proxyProvideIBaseView(loginModule));
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.loginModule = builder.loginModule;
  }

  @Override
  public void inject(LoginActivity loginActivity) {
    injectLoginActivity(loginActivity);
  }

  private LoginActivity injectLoginActivity(LoginActivity instance) {
    LoginActivity_MembersInjector.
       injectLoginPresenter(instance, getLoginPresenter());
    return instance;
  }
}

首先这里先调用了inject方法进行注入操作,接着调用了injectLoginActivity方法来实现真正的注入操作。

其次,在injectLoginActivity方法中通过getLoginPresenter()来获取一个LoginPresenter对象。
getLoginPresenter()是通过新的源码类LoginModule_ProvideIBaseViewFactory来调用proxyProvideIBaseView(LoginModule instance) 方法来获取loginModule上提供的IBaseView接口实现对象最终实现LoginPresenter的初始化操作

public static IBaseView proxyProvideIBaseView(LoginModule instance) {
  return Preconditions.checkNotNull(instance.provideIBaseView(), 
         "Cannot return null from a non-@Nullable @Provides method");
}

最后用到了另外一个新的源码类LoginActivity_MembersInjector调用injectLoginPresenter方法来实现赋值操作,将参数中的LoginPresenter对象
赋值给LoginActivity对象的loginPresenter属性对象。

public static void injectLoginPresenter(LoginActivity instance, LoginPresenter loginPresenter) {
  instance.loginPresenter = loginPresenter;
}

完成赋值操作后,就可以在LoginActivity直接使用loginPresenter属性来进行业务操作了。

解耦和复用示例

上面的示例虽然实现了PresenterActivity之间的解耦合操作,但是在Component中还是在inject的时候指明了类型。这里还是存在一定的耦合性,如何将耦合性降低到最低,我们可以采用 基类注入的方式来实现。

创建自定义作用域

@Scope   //自定义一个作用域注解标签
@Retention(RetentionPolicy.RUNTIME) //在运行时中检测作用域范围
	public @interface BaseScope {
}

创建Module类

@Module
public class SecondFragmentModule {
    private SecondFragmentContract.View mView;

    @Inject
    public SecondFragmentModule(SecondFragmentContract.View view){
        mView = view;
    }

    //这里指定Module提供者的作用域
    @BaseScope
    //将View的实现者返回提供出来
    @Provides
    public SecondFragmentContract.View provideSecondFragmentContactView(){
        return mView;
    }
}

创建Component类

@BaseScope
@Component(modules = SecondFragmentModule.class)
public interface ScondFragmentComponent {
    //这里为什么不能使用Fragment来指定类型?
    //reply: 因为Fragment无法在源码内部实现inject注入操作

    //解决方案:
    // 第一步: 定义一个BaseFragment来实现IView接口,在BaseFragment中通过@Inject来注入SecondPresenter
    // 第二部: 在自定义Fragment类中继承BaseFragment,重写IView接口方法来实现回调
    void inject(BaseFragment fragment);
}

创建Contract接口类

public interface SecondFragmentContract {
    //定义视图操作行为
    interface View extends IBaseView<Presenter>{
        public void onFirstLoadDataArrived(List list);
        public void onLoadMoreDataArrived(List list);
        public void onRefreshError();
        public void onLoadMoreError();
    }

    //定义代理人操作行为
    interface Presenter{
        public void refershData(int dataType);
        public void loadMoreData(int dataType);
    }
}

创建Present类

public class SecondPresenter implements SecondFragmentContract.Presenter {

    private SecondFragmentContract.View mView;

    private static final String BASE_URL = "https://api.apiopen.top";
    private int page = 1;
    Retrofit retrofit;
    PoetryAPI poetryAPI;

    @Inject
    public SecondPresenter(SecondFragmentContract.View view){
        mView = view;
        retrofit = new Retrofit.Builder()
                       .baseUrl(BASE_URL)
                       .addConverterFactory(GsonConverterFactory.create())
                       .build();
    }

    @Override
    public void refershData(int dataType) {
        if(null == mView){
            Log.i("Debug",  "mView is null");
            mView.onRefreshError();
            return;
        }

        getPoetryList(page);
        page++;
    }

    @Override
    public void loadMoreData(int dataType) {
        if(null == mView){
            Log.i("Debug",  "mView is null");
            mView.onLoadMoreError();
            return;
        }

        getPoetryList(page);
        page++;
    }

    private void getPoetryList(final int page){
        poetryAPI = retrofit.create(PoetryAPI.class);
        Call<PoetryList> call = poetryAPI.getPoetryList(page, 10);

        call.enqueue(new Callback<PoetryList>() {
            @Override
            public void onResponse(Response<PoetryList> response, Retrofit retrofit) {
                PoetryList poetryList =  response.body();
                if(poetryList.getResult().size() == 0){
                    mView.onLoadMoreError();
                }else{
                    if(page == 1){
						mView.onFirstLoadDataArrived(
						   poetryList.getResult());
                    } else {
                        mView.onLoadMoreDataArrived(
                           poetryList.getResult());
                    }
                }
            }

            @Override
            public void onFailure(Throwable t) {
                Log.i("getPoetryList", t.getMessage());
                if(page == 1){
                    mView.onRefreshError();
                }else{
                    mView.onLoadMoreError();
                }
            }
        });
    }
}

创建BaseFragment类

public class BaseFragment extends Fragment  implements SecondFragmentContract.View {
    @Inject
    SecondPresenter secondPresenter;

    @Override
    public void onFirstLoadDataArrived(List list) { }

    @Override
    public void onLoadMoreDataArrived(List list) { }

    @Override
    public void onRefreshError() { }

    @Override
    public void onLoadMoreError() { }

    @Override
    public boolean isAddedToParentView() { return true; }

    @Override
    public Context getCurrentContext() { return getContext(); }

    @Override
    public void onAttach(Context context){
        super.onAttach(context);

        DaggerScondFragmentComponent.builder()
                .secondFragmentModule(new SecondFragmentModule(this))
                .build()
                .inject(this);
    }
}

定义子类继承BaseFragment

public class SecondFragment extends BaseFragment{
    private View view;

    @BindView(R.id.poetryListView)
    ListView poetryListView;

    @BindViews({R.id.refreshDataButton, R.id.loadDataButton})
    List<Button> buttonList;

    private PoetryListAdapter poetryListAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, 
    						@Nullable ViewGroup container,
                             Bundle savedInstanceState){
        super.onCreateView(inflater, container, savedInstanceState);

        if(null == view){
            view = inflater.inflate(R.layout.second_fragment, null);

            //这里必须进行绑定
            ButterKnife.bind(this, view);

            //secondPresenter = new SecondPresenter(this);

            poetryListAdapter = new PoetryListAdapter(getContext(), null);
            poetryListView.setAdapter(poetryListAdapter);

            secondPresenter.refershData(1);
        }
        return  view;
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onFirstLoadDataArrived(List list) {
        poetryListAdapter.getDataSource().addAll(0, list);
        poetryListAdapter.notifyDataSetChanged();
    }

    @Override
    public void onLoadMoreDataArrived(List list) {
        poetryListAdapter.getDataSource().addAll(list);
        poetryListAdapter.notifyDataSetChanged();
    }

    @Override
    public void onRefreshError() {
        Toast.makeText(getContext(), "未发现新数据" , Toast.LENGTH_LONG).show();
        buttonList.get(0).setEnabled(false);
    }

    @Override
    public void onLoadMoreError() {
        Toast.makeText(getContext(), "已加载完所有数据" , Toast.LENGTH_LONG).show();
        buttonList.get(1).setEnabled(false);
    }

    @OnClick({R.id.refreshDataButton, R.id.loadDataButton})
    public void onViewClicked(View v){
        switch (v.getId()) {
            case R.id.refreshDataButton:
                if(null == secondPresenter){
                    Log.i("Debug",  "firstPresenter is null");
                    return;
                }
                secondPresenter.refershData(1);
                break;
            case R.id.loadDataButton:
                if(null == secondPresenter){
                    Log.i("Debug",  "firstPresenter is null");
                    return;
                }

                secondPresenter.loadMoreData(1);
                break;
        }
    }
}

注意要点

Component中只是通过inject注入了Fragment、Activity、Dialog的自定义基类,由基类来进行dagger注入和Presenter的注入操作, 这些自定义的基类的子类只需要继承基类就可以实现dagger的注入和自动加载功能, 实现了代码的复用。

总结

用法

这里使用Inject 注入需要如下四步:

  • 1 定义一个Scope作用域类,限定注解的使用范围。

  • 2 定义一个FragmentModule,构造函数需要使用Inject注入,其中包含Contract.View接口,实现Module注解和View接口提供。

  • 3 定义一个FragmentComponment接口, 对FragmentModule进行装载,并指定inject参数类型。
  • 4 在Presenter的构造函数上使用Inject进行注入。

  • 5 在Fragment的onAttach方法内实现对Fragment的注解注入,注入完成后,就可以使用@Inject实现来实现自动装载的功能了

Dagger2的优势

  • 1.可以对Presenter进行共用了,比如 有三个列表的页面,分别是 新闻、活动、消息,都是以列表的形式来加载数据的。
    我们统一通过Presenter的refershData(int dataType)和loadMoreData(int dataType)来进行数据的加载和调用,然后通过IView的接口进行回调。

  • 2.解除了传统Presenter对与Fragment和Activity的依赖,使用dagger进行组件注解后,Presenter中只关注传入进来的IView接口对象,而不关注这个实现这个IView接口的对象是谁,是Fragment、Dialog、Activity都不影响使用, 实现了真正意义上的解耦合。

Component中只是通过inject注入了Fragment、Activity、Dialog的自定义基类,由基类来进行dagger注入和Presenter的注入操作,这些自定义的基类的子类只需要继承基类就可以实现dagger的注入和自动加载功能, 实现了代码的复用。

猜你喜欢

转载自blog.csdn.net/panyongjie2577/article/details/88621179