Android MVP+Dagger2使用教程

来源:互联网 发布:华为光猫开启端口23 编辑:程序博客网 时间:2024/06/01 10:21

Dagger2是一个依赖注入框架,在解耦合方面堪称强大。如果你还不知道什么是依赖注入,以及使用Dagger2的原因,那么还是麻烦你先去google、百度一下,这里我暂时不会涉及dagger2的原理,而是使用一个非常简单的MVP例子来应用dagger2。为什么要使用MVP的例子讲解呢?因为dagger2和MVP是天造地设的一对~、

一、不使用dagger2的MVP Demo

这个MVP Demo非常简单,模拟手机号码归属地查询的实现。如下图,在EditText中输入手机号码,点击查询按钮,就会显示出手机号码所归属的省市。PS:为了简(偷)便(懒),这里并不存在网络请求,我只是模拟了这一过程而已,所以手机号输入可以是随意的字符即可,只要保证EditText的内容不为空就可以了,各位见怪勿怪哈哈哈~
这里写图片描述

MVP分为model、view、和presenter,其中Activity承担View的角色,只负责控件的显示和更新。model负责业务逻辑和各种数据实体,presenter则负责连接Activity。

1、View

首先是IQueryView.java

public interface IQueryView {    //查询成功后,显示手机归属地的查询结果    void showSuccessMsg(String successMsg);    //查询失败,显示失败原因    void showErrorMsg(String errorMsg);}

然后在MainActivity中实现该接口:
MainActivity.class

public class MainActivity extends AppCompatActivity implements IQueryView,View.OnClickListener{    //输入手机号码    EditText et;    //显示查询结果    TextView tv;    Button btn;    //presenter的实现在下面    public QueryPresenter presenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        et= (EditText) findViewById(R.id.et);        tv= (TextView) findViewById(R.id.tv_res);        btn= (Button) findViewById(R.id.btn_query);        presenter=new QueryPresenter(this);        btn.setOnClickListener(this);    }    //在TextView中显示查询结果,Activity只负责View的更新和显示    @Override    public void showSuccessMsg(String successMsg) {        tv.setText(successMsg);    }    //查询失败时,Toast提示    @Override    public void showErrorMsg(String errorMsg) {        Toast.makeText(this,errorMsg,Toast.LENGTH_LONG).show();    }    //点击查询按钮时,将查询逻辑交给Presenter    @Override    public void onClick(View view) {        presenter.query(et.getText().toString().trim());    }}

2、Presenter

Presenter负责连接view和model,view无法直接获取model的数据,model也无法直接操作view,即实现了view和model之间的完全解耦。Presenter和View、Presenter和Model之间的通信都是使用接口。
QueryPresenter.java:

public class QueryPresenter implements OnQueryListener{    private IQueryView view;    public QueryModel model;    public QueryPresenter(IQueryView view){        this.view=view;        model=new Model();    }    //使用model发起网络请求    public void query(String phoneNumber){        model.queryNumber(phoneNumber,this);    }    @Override    public void onSuccess(String successMsg) {        //通知View更新        view.showSuccessMsg(successMsg);    }    @Override    public void onError(String errorMsg) {        view.showErrorMsg(errorMsg);    }}

onQueryListener.java:连接Presenter和Model

public interface OnQueryListener {    //model通知presenter查询成功    void onSuccess(String successMsg);    //model通知presenter查询失败    void onError(String errorMsg);}

3、Model

model代表业务逻辑和数据实体,在这里,model完成手机号归属地查询的网络请求(模拟)

QueryModel.java:

public class QueryModel {    public QueryModel(){}    /**     * 模拟手机号码归属地查询     * @param phoneNum     * @param listener     */    public void queryNumber(String phoneNum, OnQueryListener listener){        //模拟网络请求,请求结果为广东 东莞        String result="广东 东莞";        //通知Presenter        listener.onSuccess(result);    }}

好了,完整的MVP Demo就是这样了。可以看到,我们在MainActivity中需要实例化Presenter:

presenter=new QueryPresenter(this);

在QueryPresenter中我们还需要实例化Model

    public QueryPresenter(IQueryView view){        this.view=view;        model=new Model();    }

这就是所谓的依赖,MainActivity依赖QueryPresenter,QueryPresenter依赖QueryModel。在大型项目中,一个类可能依赖多个其他的类,需要书写类似A a=new A( )的代码就会很多很繁琐,而使用dagger2就可以省去很多代码量,更重要的是,降低耦合度,让程序结构更清晰,易于维护和测试。

二、使用dagger2的MVP Demo

1、引入dagger2的准备工作

在project–build.gradle中添加apt插件。PS:apt插件不止这一种
build.gradle:

// Top-level build file where you can add configuration options common to all sub-projects/modules.buildscript {    repositories {        jcenter()    }    dependencies {        classpath 'com.android.tools.build:gradle:2.2.3'        //添加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    }}allprojects {    repositories {        jcenter()    }}task clean(type: Delete) {    delete rootProject.buildDir}

在app–build.gradle中添加依赖,记得在上方添加:

apply plugin: 'com.neenbedankt.android-apt'

app-build.gradle:

apply plugin: 'com.neenbedankt.android-apt'....dependencies {    compile fileTree(include: ['*.jar'], dir: 'libs')    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {        exclude group: 'com.android.support', module: 'support-annotations'    })    compile 'com.android.support:appcompat-v7:25.3.1'    testCompile 'junit:junit:4.12'    //dagger2和Java注解的依赖    compile 'com.google.dagger:dagger:2.11'    compile 'org.glassfish.main:javax.annotation:4.0-b33'    //注意是apt而不是compile    apt 'com.google.dagger:dagger-compiler:2.11'}

好了,准备工作做好了,可以开始使用dagger2了

2、应用dagger2

首先在需要被实例化的依赖对象的构造函数中添加注解@inject
先举个简单的例子吧,QueryModel的构造函数不需要参数,也就是默认的无参构造函数:

    @Inject    public QueryModel(){}

然后在需要用到QueryModel的类QueryPresenter中也添加@inject注解(在对象声明处添加)

    @Inject    public QueryModel model;

在这两个地方都添加了@inject依赖就意味着告诉dagger,我这个model对象是需要实例化的,当用到model时,你就使用被@inject的构造函数来实例化这个model吧

那现在问题是谁来实例化这个对象?于是就轮到Component出来了:
QueryPresenterComponent.java:

@Componentpublic interface QueryPresenterComponent {    void inject(QueryPresenter presenter);}

注意上方的@Component注解,其中的inject()方法表明该Component是可以为QueryPresenter进行依赖注入的。还要注意一点,该类必须是抽象类或者接口,

最后,我们需要在Presenter中实时地声明对类对象进行依赖注入,原本的model=new QueryModel( )就不需要了。
注意:我们使用了DaggerQueryPresenterComponent,这个类是Dagger2自动为我们生成的,命名规则是在我们刚刚创建的类QueryPresenterComponent前面加上Dagger。在make project之后我们才能使用这个类。

    public QueryPresenter(IQueryView view){        this.view=view;        //model=new QueryModel();        DaggerQueryPresenterComponent.builder().build().inject(this);    }

好了,修改完毕后重新运行程序,运行结果是一样的。

现在有个问题就是QueryPresneter的实例化,也就是在MainActivity中的依赖注入。这时候操作基本和上面差不多,但是要发现的一点是QueryPresneter的构造函数是需要一个参数的:

    public QueryPresenter(IQueryView view){        this.view=view;        DaggerQueryPresenterComponent.builder().build().inject(this);    }

来,我们一步一步来

首先,还是添加@inject

在QueryPresenter构造函数和MainActivity对象声明处添加注解:

    @Inject    public QueryPresenter(IQueryView view){        this.view=view;        DaggerQueryPresenterComponent.builder().build().inject(this);    }
    @Inject    public QueryPresenter presenter;

然后就是创建Component了,但是这次稍有不同,在创建Component之前我们先创建Module

Module你可以理解为对象提供参数。Component能为对象选定构造函数然后创建对象实例,那构造函数中需要的参数呢?就要由Module来提供了
MainActivityModule.class:

@Modulepublic class MainActivityModule {    private IQueryView view;    //参数通过构造函数传入    public MainActivityModule(IQueryView view){        this.view=view;    }    //提供参数    @Provides    public IQueryView provideQueryView(){        return view;    }}

注意上方的@Module注解
我们在MainActivityModule的构造函数中传入QueryPresenter所需要的参数
然后使用@Provides注解注明provideQueryView()适用于提供参数的。

当Component找到以下被@inject的构造方法时,会发现凭借自己无法提供IQueryView 参数,然后就会前往它所关联的Module看其能否提供参数,那么接下来就是把Component和Module进行关联了

    @Inject    public QueryPresenter(IQueryView view){        this.view=view;        DaggerQueryPresenterComponent.builder().build().inject(this);    }

再然后,创建Component并关联Module

Component的命名规则一般是:谁需要依赖注入,就在谁的名字后面添加Component,像MainActivity需要注入QueryPresenter,那么在类名就是MainActivityComponent了。

@Component(modules=MainActivityModule.class)public interface MainActivityComponent {    void inject(MainActivity activity);}

注意上方的@Component(modules=MainActivityModule.class)

最后,就是在MainActivity中,显示声明依赖注入了,记得先make project

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        et= (EditText) findViewById(R.id.et);        tv= (TextView) findViewById(R.id.tv_res);        btn= (Button) findViewById(R.id.btn_query);        btn.setOnClickListener(this);//        presenter=new QueryPresenter(this);        DaggerMainActivityComponent.builder().mainActivityModule(new MainActivityModule(this)).build().inject(this);    }

好了,完整流程就是这样啦,最后附上源码
源码下载