对于dagger2使用的好处,这里就不过多介绍,可以参考其官方文档说明点击打开链接,这里主要是讲解一下,本人在学习、使用dagger2时的一些总结,对dagger2的快速入门做一些笔记。接下来会分几篇文章来介绍一下自己对dagger2的快速入门,这一篇主要讲一下,dagger2环境的快速搭建,以及dagger2的三个重要组成部分。
一、dagger2环境的快速搭建
1.在项目的build.gradle 的添加下面的代码:
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2' //其他
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //添加android apt命令工具
}
2.在Module的build.gradle顶部添加下面代码:
// 应用插件
apply plugin: 'com.neenbedankt.android-apt'
同时在其dependencies{ }中加入以下代码同步即可:
compile 'com.google.dagger:dagger:2.0.2' //dagger2 api
apt 'com.google.dagger:dagger-compiler:2.0.2' //dagger2 注解处理器
provided 'org.glassfish:javax.annotation:10.0-b28' //添加android缺失的部分javax注解
注意在与butterknife一起使用时,在module的build.gradle 的dependencies{ } 里面要添加如下代码:
//butterknife注入
compile 'com.jakewharton:butterknife:8.5.1'
apt 'com.jakewharton:butterknife-compiler:8.5.1' //注意是apt 而不是annotationProcessor
原因的话可以点此查看详细说明 点击打开链接 。
二、dagger2的三个重要组成部分
dagger2三个重要组成部分为:module,component, container,三者的关系如下图所示:
由于dagger2主要是编译期对注解内容进行获取,所以这三个组成部分也有相应的注解符号,下面主要说明一下所涉及的注释符号:
Container:
@Inject : 通过在需要依赖注入的地方(一般为activity或fragment)使用,它能告诉dagger2这个类或字段需要依赖注入,dagger2就会构造这个类和对象的实例来满足依赖注入。
Module :
@Module : 用该注解修饰的类,里面主要提供该依赖对象的实例化方法,方法名一般为provide + 对象名,如provideRegister() ,同时用@Provides注解修饰。用该注解修饰这个类,当dagger2在构造类的实例时,就知道从哪里去找到相应的依赖。
@Provides : 用这个来修饰@Module注解类中的方法,表明要提供的实例化对象
Component:
@Componet : 它修饰的是一个接口,其实就是一个依赖注入器,里面提供了依赖注入的方法,方法名一般为inject(目标类对象) 。可以说它是连接@Inject 和 @Module 的中间桥梁,连接两个部分。它后面可以跟多个Module,来实例化多个对象。
当Module 、Component 准备好,在需要依赖注入的目标类中除了要@Inject 相应的对象,还需要进行一项初始化操作将依赖注入到相应的目标类中,在这操作之前一般需要rebuild一下。这样整个依赖注入过程才算完成了。。。
三、简单使用代码示例
本文将以典型的用户登录案例,将MVP架构与Dagger2结合起来!熟悉其使用场景,在场景中了解dagger2的入门使用:
先看一下这个登录案例工程的目录结构:
如上图,component,module是项目中所用到的dagger2的依赖注入器和module , bean 是mvp中的model,presenter是项目所用到的presenter层,以及p层与v层交互的接口等。
下面说一下,如何将mvp与dagger2融合到用户登录场景中,主要分为三个步骤:
1.搭建mvp架构;2.利用dagger2解耦,将m层注入p层,p层注入v层;3.rebuid项目,初始化依赖注入
(一)搭建mvp架构
M: 创建用户登录信息实体类User,这个没什么好说的,包括用户名和密码。
业务逻辑类:定义接口IUser,里面包含一个登录方法,创建一个实现类IUserImp来实现这个接口,同时定义一个接口回调OnLoginListener,监听登录成功或失败。主要看一下实现类IUserImp的代码:
public class IUserImp implements IUser{
private User mUser;
//构造传入User对象
public IUserImp(User user) {
mUser = user;
}
@Override
public void login(final String name, final String password, final OnLoginListener listener) {
Log.e("zxh", "login:mUser== " + mUser);
//模拟登录,子线程休眠一下
new Thread(new Runnable() {
@Override
public void run() {
//延时一下
SystemClock.sleep(2000);
if ("zxh".equals(name) && "123".equals(password)) {
mUser.setName(name);
mUser.setPassWord(password);
listener.loginSuccess(mUser); //成功回调
}else {
listener.loginFailed(); //失败回调
}
}
}).start();
}
}
确定P层与V层交互的接口类:IUserLoginView,里面定义与页面交互的各种逻辑:代码如下
public interface IUserLoginView {
//获取用户名和密码
String getUserName();
String getUserPassWord();
//显示和隐藏进度条
void showProgress();
void hideProgress();
//成功跳转到新页面,失败弹土司
void toMainActivity(User user);
void showFailedToast();
}
接下来看一下LoginPresenter,通过持有IUserLoginView和IUserImp的引用来实现页面逻辑与业务逻辑的操作,它只有一个登录的方法,代码如下:
public class LoginPresenter {
private IUserLoginView mIUserLoginView; //页面逻辑操作接口
private Handler mHandler;
private IUserImp mIUserImp; //model业务操作实现类
//通过构造传入相应的引用
public LoginPresenter(IUserLoginView IUserLoginView,Handler handler,IUserImp iUserImp) {
mIUserLoginView = IUserLoginView;
mHandler = handler;
mIUserImp = iUserImp;
}
//定义一个登录的方法
public void login(){
//显示progress
mIUserLoginView.showProgress();
Log.e("zxh","mIUserImp=="+ mIUserImp);
//开始登录
mIUserImp.login(mIUserLoginView.getUserName(), mIUserLoginView.getUserPassWord(), new OnLoginListener() {
@Override
public void loginSuccess(final User user) {
//主线程中去更新UI
mHandler.post(new Runnable() {
@Override
public void run() {
mIUserLoginView.toMainActivity(user); //进入新页面
mIUserLoginView.hideProgress(); //隐藏progress
}
});
}
@Override
public void loginFailed() {
//主线程中去更新UI
mHandler.post(new Runnable() {
@Override
public void run() {
mIUserLoginView.showFailedToast(); //弹土司
mIUserLoginView.hideProgress(); //隐藏progress
}
});
}
});
}
}
public class LoginActivity extends AppCompatActivity implements IUserLoginView{
@Inject
LoginPresenter mLoginPresenter; //注入引用对象
@BindView(et_name)
EditText mEtName;
@BindView(R.id.et_password)
EditText mEtPassword;
@BindView(R.id.btn_login)
Button mBtnLogin;
@BindView(R.id.pb)
ProgressBar mPb;
private Unbinder mBind;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//ButterKnife绑定
mBind = ButterKnife.bind(this);
/**dagger2依赖注入初始化*/
DaggerLoginComponent.builder()
.userModule(new UserModule())
.iUserImpModule(new IUserImpModule())
.loginModule(new LoginModule(this))
.build()
.injectLoginActivity(this);
}
@OnClick(R.id.btn_login)
public void onViewClicked() {
//点击登录按钮,执行登录逻辑
mLoginPresenter.login();
}
@Override
protected void onDestroy() {
super.onDestroy();
//解除butterknife绑定
mBind.unbind();
}
@Override
public String getUserName() { //获取用户名
return mEtName.getText()
.toString();
}
@Override
public String getUserPassWord() { //获取密码
return mEtPassword.getText()
.toString();
}
@Override
public void showProgress() { //显示progress
mPb.setVisibility(View.VISIBLE);
}
@Override
public void hideProgress() { //隐藏progress
mPb.setVisibility(View.GONE);
}
@Override
public void toMainActivity(User user) { //跳转到登录成功页面
Intent intent = new Intent(this,MainActivity.class);
intent.putExtra("name",user.getName());
intent.putExtra("passWord",user.getPassWord());
startActivity(intent);
// finish();
}
@Override
public void showFailedToast() { //弹土司
Toast.makeText(this,"登录失败,用户名或密码错误...",Toast.LENGTH_SHORT).show();
}
}
项目MVP框架搭好了,现在就来仔细分析一下如何利用dagger2进行依赖注入,实现项目不用new对象。
1.先来看一下LoginActivity,它里面通过@Inject 注解注入了LoginPresenter对象。假如不用dagger2,则如下所示:
//不通过dagger2则要new 好几次对象
LoginPresenter presenter = new LoginPresenter(this,mHandler,new IUserImp(new User()));
要创建User对象,IUserImp对象和LoginPresenter对象。
2.接下来,看下dagger2怎么处理而不用new这些对象呢?
前文对dagger2的分析可知,通过@Module可提供这些实例化对象,那么对于这三个实例化对象,可通过三个Module来提供,代码分别如下:
@Module
public class UserModule {
//这里通过@Provides,提供了User实例化对象的方法,如果这边不提供provideUser()方法
//则用@Inject 对User类的构造方法注解,否则会报错,
//具体报错及原因可查看以下链接:
//http://blog.csdn.net/zxhandroid/article/details/70677260
@Provides
User provideUser() {
return new User();
}
}
@Module
public class IUserImpModule {
//这里通过@Provides,提供了User实例化对象的方法,如果这边不提供provideIUserImp(User user)方法
//则用@Inject 对IUserImp类的构造方法注解,否则会报错,
//具体报错及原因可查看以下链接:
//http://blog.csdn.net/zxhandroid/article/details/70677260
@Provides
IUserImp provideIUserImp(User user){
return new IUserImp(user);
}
}
@Module
public class LoginModule {
private IUserLoginView mIUserLoginView;
public LoginModule(IUserLoginView IUserLoginView) {
mIUserLoginView = IUserLoginView;
}
@Provides
IUserLoginView provideIUserLoginView() {
return mIUserLoginView;
}
@Provides
Handler provideHandler() {
return new Handler();
}
//1.带参数的方法,里面的每一个参数必须有@Provides注解的provide方法提供实例化对象或相应类中构造方法被@Inject注解,否则会报错。
//2.对于抽象类或接口,则可以通过构造方法传入,并通过provide()方法提供,如IUserLoginView。
//3.由于LoginPresenter的构造中有接口参数,所以只能通过provide方法来提供实例化对象,不能直接在其构造方法中加@Inject注解,
//原因很简单,因为抽象类或接口无法进行实例化,所以即便加上了@Inject注解在rebuild时也会报错。
@Provides
LoginPresenter provideLoginPresenter(IUserLoginView IUserLoginView, Handler handler, IUserImp iUserImp){
return new LoginPresenter(IUserLoginView,handler,iUserImp);
}
}
3.创建LoginComponent 注入器提供方法注入到相应的LoginActivity中,具体见代码:
/**
* Created by zengxianghui900 on 17/5/9.
* 由于LoginPresenter对象实例化需要同时实例化User和IUserImp对象,因此modules要依赖这三个
*/
@Component(modules = {UserModule.class,IUserImpModule.class,LoginModule.class})
public interface LoginComponent {
//提供方法注入到LoginActivity目标类中
void injectLoginActivity(LoginActivity loginActivity);
}
(三)rebuid项目,初始化依赖注入
接下来对整个项目进行rebuild一下,就会在项目Module 的build文件夹下,自动生成相应的代码,生成代码目录如下:
不报错,且自动生成了代码的话,就可以在LoginActivity中就可以进行初始化操作了,初始化代码如下:
/**dagger2依赖注入初始化*/
DaggerLoginComponent.builder()
.userModule(new UserModule())
.iUserImpModule(new IUserImpModule())
.loginModule(new LoginModule(this))
.build()
.injectLoginActivity(this);
到此 ,整个案例就完成了。。
代码已提交到github上,后续会不断完善,欢迎查看并star! 点击打开链接