使用Dagger2构建简易MVP框架

2017/03/11 Android

本文使用dagger2构建MVP框架,目的是加深dagger2的理解,一个小demo,记录分享之。 相关文章: Android Mvp实践 Android中利用泛型简化MVP

总体框架

### 工程目录结构 整个工程的目录结构如下: base文件夹存放一些公用的基类文件,di文件夹存放依赖注入相关的代码,mvp中按功能模块划分。本demo实现的功能为:通过点击界面上的按钮,获取手机系统时间并显示。

具体实现

### 在项目中引入dagger ```java buildscript { repositories { jcenter() } dependencies { classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’ } }

apply plugin: ‘com.android.application’ apply plugin: ‘com.neenbedankt.android-apt’

dependencies { //other dependencies

//Dagger2
compile 'com.google.dagger:dagger:2.0.2'
compile 'javax.annotation:jsr250-api:1.0'
apt 'com.google.dagger:dagger-compiler:2.0.2' } ``` ### BaseActivity BaseActivity中传入Presenter,利用@Inject注解Presenter,Dagger2会自动去找Presenter的生成方法,并创建presenter对象,则activity中持有presenter对象,可调用presenter中的方法。 ```java public abstract class BaseActivity<P extends BasePresenter> extends Activity {
protected MyApplication myApplication;

@Inject
protected P mPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(initView());

    myApplication = (MyApplication) getApplication();
    ComponentInject(myApplication.getAppComponent());//依赖注入
    initData();
}

/**
 * 依赖注入的入口
 */
protected abstract void ComponentInject(AppComponent appComponent);

protected abstract View initView();

protected abstract void initData();

} ### BasePresenter BasePresenter中传入BaseView接口,持有view接口,可调用view中的方法 java public class BasePresenter implements presenter{ protected V mRootView;

public BasePresenter(V rootView) {
    this.mRootView = rootView;
    onStart();
}

@Override
public void onStart() {

}

@Override
public void onDestroy() {

} } ``` ### presenter层的具体实现 TempLatePresenter的构造方法使用了@Inject注解,在上面的BaseActivity中自动构造TempLatePresenter对象时就是从这里产生的TempLatePresenter对象,同时该构造方法中还传入了TempLateContract.View参数,因此dagger2会继续寻找TempLateContract.View的生成方法 ```java public class TempLatePresenter extends BasePresenter<TempLateContract.View> {

@Inject
public TempLatePresenter(TempLateContract.View rootView) {
    super(rootView);
}


public void getTime() {
    mRootView.setTime(System.currentTimeMillis() + "");
}

}

### 依赖注入
@Module中提供TempLateContract.View的构造方法
```java
@Module
public class TempLateModule {
    private TempLateContract.View view;

    public TempLateModule(TempLateContract.View view) {
        this.view = view;
    }

    @ActivityScope
    @Provides
    TempLateContract.View provideTempLateView() {
        return this.view;
    }
}

TempLateComponent管理该module java @ActivityScope @Component(modules = TempLateModule.class, dependencies = AppComponent.class) public interface TempLateComponent { void inject(TempLateActivity activity); } ### view层的具体实现 TempLateActivity中调用ComponentInject,实现依赖注入 ```java public class TempLateActivity extends BaseActivity implements TempLateContract.View{

Button btnGetTime;
TextView tvTime;

@Override
protected void ComponentInject(AppComponent appComponent) {
    DaggerTempLateComponent
            .builder()
            .appComponent(appComponent)
            .tempLateModule(new TempLateModule(this)) //请将TempLateModule()第一个首字母改为小写
            .build()
            .inject(this);
}

@Override
protected View initView() {
    return LayoutInflater.from(this).inflate(R.layout.activity_template, null, false);
}

@Override
protected void initData() {
    btnGetTime = (Button) findViewById(R.id.btn_get_time);
    tvTime = (TextView) findViewById(R.id.tv_time);

    btnGetTime.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            mPresenter.getTime();
        }
    });
}

@Override
public void setTime(String time) {
    tvTime.setText(time);
}

} ```

整体流程如下: - TempLateActivity继承BaseActivity并传入TempLatePresenter,view中持有TempLatePresenter对象 - dagger2根据@inject注解知道要构造TempLatePresenter对象 - 找到TempLatePresenter的构造方法,该方法中持有view的引用,发现还需要TempLateContract.View参数 - 在TempLateModule中找到TempLateContract.View的构造方法 - 依次完成TempLateContract.View和TempLatePresenter的构造

Dagger2简单理解

### Module中提供Dependency ```java @Module public class AppModule {

public AppModule(Context context) {
    this.mContext = context;
}

@Provides
public Context provideContext() {
    return mContext;
}

@Provides
public OkHttpClient provideOkHttpClient() {
    OkHttpClient okhttpClient = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build();
    return okhttpClient;
}

@Provides
public Retrofit provideRetrofit(OkHttpClient okhttpClient) {
    Retrofit retrofit = new Retrofit.Builder()
            .client(okhttpClient)
            .baseUrl("https://api.github.com")
            .build();
    return retrofit;
} } ``` 这种用来生产Dependency的、用 @Provides修饰过的方法叫Provider方法。如果最后找到的 provideOkHttpClient()方法也需要其他参数,那么管理员还会继续递归的找下去,直到所有的Dependency都被满足了,再一个一个创建Dependency ### 工厂管理员:Component #### 方法一:在Component里面定义一个返回Dependency的方法 ```java @Component(modules = {AppModule.class}) public interface AppComponent {
LoginPresenter loginPresenter(); }

public class LoginActivity extends AppCompatActivity { private LoginPresenter mLoginPresenter;

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

    AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();  //<=
    mLoginPresenter = appComponent.loginPresenter();   //<=
} } ``` #### 方法二:Field Injection DaggerAppComponent实现这个方法的方式是,去LoginActivity里面所有被 @Inject修饰的field,然后调用 AppModule相应的Provider方法,赋值给这个field。这里需要注意的是,@Inject field不能使private,不然dagger2找不到这个field。 ```java @Component(modules = {AppModule.class}) public interface AppComponent {
void inject(LoginActivity loginActivity);  //<= }

public class LoginActivity extends AppCompatActivity { @Inject
LoginPresenter mLoginPresenter;

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

    AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); //<=
    appComponent.inject(this); //<=

    //从此之后,mLoginPresenter就被实例化了
    //mLoginPresenter.isLogin()
} } ```

其实AppModule里面的很多Provider方法是不需要定义的。比如说在这种情况下,LoginPresenter的Provider方法 provideLoginPresenter(UserManager userManager, PasswordValidator validator) 就不需要定义,你只需要在定义LoginPresenter的时候,给它的Constructor加上 @Inject修饰一下: ```java public class LoginPresenter { private final UserManager mUserManager; private final PasswordValidator mPasswordValidator;

@Inject
public LoginPresenter(UserManager userManager, PasswordValidator passwordValidator) {
    this.mUserManager = userManager;
    this.mPasswordValidator = passwordValidator;
}

//other methods } ```

源码已上传github

参考文章

使用dagger2来做依赖注入,以及在单元测试中的应用

Search

    Table of Contents