有没有像我这种从系统转应用的开发,做系统几乎都不需要跟框架打交道,但是呢,做应用,那这些开源框架就不得不天天见面了。我相信很多都会跟我一样,网上很多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);
}
}