Dagger2从使用到原理剖析

有没有像我这种从系统转应用的开发,做系统几乎都不需要跟框架打交道,但是呢,做应用,那这些开源框架就不得不天天见面了。我相信很多都会跟我一样,网上很多Dagger2的文章,但是都云里雾里,似懂非懂,所以我决定再忙都要把我的学习过程分享给大家,血泪总结啊!
对于Dagger2,我觉得最好先弄懂依赖注解是什么个意思,然后再来看看Dagger2,它跟ButterKnife还是有一定的区别的。注解并不是我要讲的重点,不过我在学习的过程中发现一篇文章写的特别好,不会的朋友可以去看看!从头到尾带你玩转注解
现在我们开启我们的Dagger2之旅!

一、使用

我们先来讲讲Dagger2怎么使用,如果一个开源框架,你连怎么用都不会,那你懂这个框架再多的原理都是废话,所以学开源框架,首先要学会怎么使用它,让它为我们项目服务。我们先从最简单的例子开始讲。

(1)引入Dagger2

你要用一个开源库,肯定要先引入它,只有引入了才有来用。由于Dagger2是利用apt插件在编译中产生辅助类,所以同时要配置apt插件,配置如下所示:

  • 配置apt插件(在build.gradle(Project:xxx)中添加如下代码)
dependencies {
        classpath 'com.android.tools.build:gradle:2.2.3'
        classpath 'me.tatarka:gradle-retrolambda:3.2.4'
        //添加apt插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
  • 添加依赖(在build.gradle(Module:app)中添加如下代码)

apply plugin: 'com.android.application'
//添加如下代码,应用apt插件
apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile 'com.google.dagger:dagger:2.4'
    compile 'com.google.code.gson:gson:2.5'
    apt 'com.google.dagger:dagger-compiler:2.4'
    //java注解
    compile 'org.glassfish:javax.annotation:10.0-b28'
}

(2)demo说明

我们先从最简单的demo说起,先来看一个很普通的demo,只是在TextView上显示一个text,看看Dagger2是怎么进行注解的。我这里结合了MVP模式讲解;
在mvp中,最常见的一种依赖关系,就是Activity持有presenter的引用,并在Activity中实例化这个presenter,即Activity依赖presenter,presenter又需要依赖View接口,从而更新UI。我这里有两个接口IView,IPresenter。

  • 接口定义
/**
 * Created by wuchunmei on 16-12-30.
 */

public interface IPresenter {
    /**
     * 加载数据
     */
    void loadData();
}
/**
 * Created by wuchunmei on 16-12-30.
 */

public interface IView {
    /**
     * 更新UI
     * @param data
     */
    void updateUi(String data);
}
  • 实现接口,处理业务逻辑(我这里是MyPreserter 实现IPreserter接口)
/**
 * Created by wuchunmei on 16-12-30.
 */

public class MyPresenter implements IPresenter {
    private IView mIView;

    public MyPresenter(IView view){
        mIView = view;
    }
    @Override
    public void loadData() {
       mIView.updateUi("Mvp Update UI "+System.currentTimeMillis());
    }
}
  • Activity负责更新UI

/**
 * Created by wuchunmei on 16-12-30.
 */

public class MvpDemoActivity extends AppCompatActivity implements IView, View.OnClickListener {
    @InjectView(R.id.btn_mvp)
    Button mButton;
    @InjectView(R.id.mvp_text)
    TextView mTextView;

    @Inject
    MyPresenter myPresenter; //获取依赖的对象

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mvp_demo_layout);
        ButterKnife.inject(this);
        DaggerViewComponent.builder().viewModule(new ViewModule(this)).build().inject(this);
        initView();
    }

    private void initView() {
        mButton.setOnClickListener(this);
    }


    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn_mvp:
                myPresenter.loadData();
                break;
        }

    }

    @Override
    public void updateUi(String data) {
        mTextView.setText(data);
    }
}

相信你一定会看到在Activity中有这么一句:

 @Inject
    MyPresenter myPresenter; //获取依赖的对象

我们先看MvpDemoActivity里的代码,之前是直接声明MyPresenter,现在在声明的基础上加了一个注解@Inject,表明MyPresenter是需要注入到MvpDemoActivity中,即MvpDemoActivity依赖于MyPresenter,这里要注意的是,使用@Inject时,不能用private修饰符修饰类的成员属性。当看到某个类被@Inject标记时,就会到他的构造方法中,如果这个构造方法也被@Inject标记的话,就会自动初始化这个类,从而完成依赖注入。如果构造方法没有被@Inject标识,则会有Module中找@Provide标识的方法,由它提供。
难道仅仅用一个@Inject 注解就可以让我们的业务层跟Activity层产生联系了?那当然不是,我们还需要一个提供依赖的Module,因为Activity是需要依赖的一方,但是到目前还没有人愿意给他提供依赖,所以我们还需要个提供依赖方,那你需要我就给你呗,多大点事啊,看我的!

  • Module提供依赖
/**
 * Created by wuchunmei on 16-12-30.
 */

@Module
public class ViewModule {
    private IView mIView;

    public ViewModule(IView mIView){
        this.mIView = mIView;
    }
    @Provides
    public MyPresenter providePresenter(){
        return new MyPresenter(mIView);
    }
}

ViewModule是一个注解类,用@Module注解标注,主要用来提供依赖。等等,刚才通过@Inject就可以完成依赖,为什么这里还要用到Module类来提供依赖?之所以有Module类主要是为了提供那些没有构造函数的类的依赖,这些类无法用@Inject标注,比如第三方类库,系统类,以及上面示例的View接口。
看到了么,Module类用一个@Module进行注解,让Dagger2知道,你看到我这个标识就知道我就是给@Inject提供依赖的那位大方人士,你肯定也看到了providePresenter()方法上面有 @Provides这个注解标识,这就表示这个方法是要把这个方法提供出去的。
好了,有了Activity的@Inject 需要注入依赖的需求方,也有了Module类的@Module无私提供依赖的提供方,这样子就可以了?No No No ,别忘了,人要在这个社会上健康成长,是需要有一定的规则存在的。Dagger2中的依赖需求方跟依赖提供方也需要一个桥梁,不然怎么能找到彼此呢?所以在Dagger2中@Inject依赖需求方跟 @Module依赖提供方之间的友谊是靠@Component这个仁兄来签牵线的,也就是常说的桥梁。

  • Component桥梁
/**
 * Created by wuchunmei on 16-12-30.
 */
 //这里表示Component会从ViewModule类中拿那些用@Provides注解的方法来生成需要注入的实例
@Component(modules = ViewModule.class)
public interface ViewComponent {
     /**
     * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
     * (被标记为@Inject的属性)
     * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
     * 用inject即可。
     */
    void inject(MvpDemoActivity activity);
}

所以Module要发挥作用,还是要依靠于Component类,一个Component类可以包含多个Module类,用来提供依赖。我们接着看下面这段代码:

 DaggerViewComponent.builder().viewModule(new ViewModule(this)).build().inject(this);

这里通过new viewModule(this)将view传递到viewModule里,然后viewModule里的providePresenter()方法返回这个MyPresenter,当去实例化MyPresenter时,发现构造函数有个参数,此时会在Module里查找提供这个依赖的方法,将该View传递进去,这样就完成了presenter里View的注入。
Component是一个接口或者抽象类,用@Component注解标注(这里先不管括号里的modules),我们在这个接口中定义了一个inject()方法,参数是MvpDemoActivity。然后rebuild一下项目,会生成一个以Dagger为前缀的Component类,这里是DaggerViewComponent,然后在MvpDemoActivity里完成下面代码.

我们来重新理一遍上面的注入过程,首先弄清楚以下几个概念:

@Inject 带有此注解的属性或构造方法将参与到依赖注入中,Dagger2会实例化有此注解的类
@Module 带有此注解的类,用来提供依赖,里面定义一些用@Provides注解的以provide开头的方法,这些方法就是所提供的依赖,Dagger2会在该类中寻找实例化某个类所需要的依赖。
@Component 用来将@Inject和@Module联系起来的桥梁,从@Module中获取依赖并将依赖注入给@Inject

好了,上面大概的使用过程都已经介绍完成了

重新回顾一下上面的注入过程:首先MvpDemoActivity需要依赖MyPresenter,因此,我们在里面用@Inject对MyPresenter进行标注,表明这是要注入的类。然后,我们对MyPresenter的构造函数也添加注解@Inject,此时构造函数里有一个参数IView,因为MyPresenter需要依赖IView,所以我们定义了一个类,叫做ViewModule,提供一个方法providePresenter,用来提供这个依赖,这个IView是通过ViewModule的构造函数注入进来的,接着我们需要定义Component接口类,并将Module包含进来,即

@Component(modules = ViewModule.class)
public interface MainComponent {
void inject(MvpDemoActivity activity);
}
同时里面声明了一个inject方法,方法参数为MvpDemoActivity,用来获取MvpDemoActivity实例,以初始化在里面声明的MyPresenter

DaggerMyComponent.builder()
.mainModule(new ViewModule(this))
.build()
.inject(this);
此时,注入过程就完成了,或许到此时,还是会有一些疑惑,因为我们看不到实例化的过程在哪里,为什么要这样去写代码,所以下面,就基于这个实例,分析Dagger2内部究竟做了什么。

二、原理剖析

我们先看MyPresenter这个类,在这个类中我们对构造方法用了@Inject标注,然后Rebuild Project,Dagger2会在/app/build/generated/apt/debug/目录下生成一个对应的工厂ViewModule_ProvidePresenterFactory


@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class ViewModule_ProvidePresenterFactory implements Factory<MyPresenter> {
  private final ViewModule module;

  public ViewModule_ProvidePresenterFactory(ViewModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public MyPresenter get() {
    return Preconditions.checkNotNull(
        module.providePresenter(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<MyPresenter> create(ViewModule module) {
    return new ViewModule_ProvidePresenterFactory(module);
  }
}

原来MyPresenter的实例化就在这里,构造函数里的参数就是我们依赖的MyPresenter,它是由ViewModule_ProvidePresenterFactory通过get()提供。接着是一个create()方法,并且有一个参数ViewModule_ProvidePresenterFactory,用来创建这个ViewModule_ProvidePresenterFactory类。

这篇文章分析得很好:
Dagger顿悟
对原理有兴趣的朋友可以去研究。

Dagger2在recyclerView中的demo展示

(1)bean 数据

/**
 * Created by wuchunmei on 16-12-29.
 */
public class Person {
    private String mName;
    private int mAge;
    private String mWork;

    public Person(@Nullable String name, int age, String work) {
        mName = name;
        mAge = age;
        mWork = work;
    }

    public String getName() {
        return mName;
    }

    public void setName(String name) {
        mName = name;
    }

    public int getAge() {
        return mAge;
    }

    public void setAge(int age) {
        mAge = age;
    }

    public String getWork() {
        return mWork;
    }

    public void setWork(String work) {
        mWork = work;
    }
}

(2)数据Module


/**
 * Created by wuchunmei on 16-12-29.
 */
@ActivityScope
@Module
public class PersonModule {

    private Context mContext;
    @Inject
    public PersonModule(Context context){
         mContext = context;
    }
    @ActivityScope
    @Provides
    public Person providePerson(){
        return new Person("公共",14,"公司");
    }

    @ActivityScope
    @Provides
    public  Context provideContext(){
        return mContext;
    }
    @ActivityScope
    @Provides
    public  List<Person> providePersons(){
        String name = "菜鸟";
        int age = 24;
        String group ="SDK";
        List<Person> userInfos = new ArrayList<>();
        for (int i = 0;i<100;i++){
            Person user = new Person(name + i , age , group +i);
            userInfos.add(user);
        }
        return userInfos;
    }

    @ActivityScope
    @Provides
    public PersonAdapter providePersonAdapter(Context context, List<Person> persons){
        return new PersonAdapter(context , persons);
    }
}

(3)adapter

public class PersonAdapter extends RecyclerView.Adapter<PersonAdapter.ViewHolder> {

    private Context mContext;
    private RecyclerView.LayoutManager mLayoutManager;
    private List<Person> mData;


    public  PersonAdapter(Context context, List<Person> data){
        mContext = context;
        mData = data;
    }

    public PersonAdapter(Context context, List<Person> data, RecyclerView.LayoutManager layoutmanager){
        mContext = context;
        mData = data;
        mLayoutManager = layoutmanager;
    }

    public List<Person> getData() {
        return mData;
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = View.inflate(parent.getContext(), R.layout.listview_item_layout_with_checkbox, null);
        // 创建ViewHolder
//        ViewHolder holder = new ViewHolder(view);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        //设置TextView内容
        Person person = mData.get(position);
        Log.i("wu","person>>"+person.getName());
        holder.mTextView.setText(person.getName());
        //设置ImageView资源
        holder.mImageView.setImageResource(R.drawable.back_home_off);
    }

    @Override
    public int getItemCount() {
        int count = 0;
        if (null != mData) {
            count = mData.size();
        }
        return count;
    }

    //定义ViewHolder,包括两个控件
    public static class ViewHolder extends RecyclerView.ViewHolder {
        @InjectView(R.id.id_index_item_image)
        ImageView mImageView;
        @InjectView(R.id.id_index_item_text)
        TextView mTextView;
        @InjectView(android.R.id.checkbox)
        AnimCheckBox mCheckbox;

        public ViewHolder(View itemView) {
            super(itemView);
            ButterKnife.inject(this,itemView);
        }
    }

(4)Component

//Component可以傳多個module
@ActivityScope
@Component(modules = PersonModule.class )
public abstract class MainComponent {
    /**
     * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
     * (被标记为@Inject的属性)
     * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
     * 用inject即可。
     */
    public abstract void inject(RecyclerViewActivity activity);
}

(5)Activity

public class RecyclerViewActivity extends AppCompatActivity  implements View.OnClickListener{
    @InjectView(R.id.recyclerview_vertical)
    MzRecyclerView mRecyclerView;
    @Inject
    PersonAdapter mPersonAdapter;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview_linear_layout);
        DaggerMainComponent.builder().personModule(new PersonModule(this)).build().inject(this);
        ButterKnife.inject(this);
        initView();
    }

    private void initView() {
        mRecyclerView.setAdapter(mPersonAdapter);

        // 创建一个线性布局管理器
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        // 设置布局管理器
        mRecyclerView.setLayoutManager(layoutManager);

    }
 }






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值