android dagger2 讲解,dagger 2 详解

android dagger2 讲解,dagger 2 详解前言依赖注入概念网络有很多解释 这里就不详细介绍 本文通过一个简单的示例一步步深入了解依赖注入的优势以及为什么使用依赖注入 概念依赖注入 DependencyIn 简称 DI 又叫控制反转 InversionofC 简称 IOC 当一个类的实例需要另另一个类的实例进行协助时 在传统的设计中 通常由调用者来创建被调用者的实例 然而依赖注入的方式 创建被调用者不再由调用

前言

依赖注入概念网络有很多解释,这里就不详细介绍,本文通过一个简单的示例一步步深入了解依赖注入的优势以及为什么使用依赖注入。

概念

依赖注入(Dependency Injection),简称DI,又叫控制反转(Inversion of Control),简称IOC。

当一个类的实例需要另另一个类的实例进行协助时,在传统的设计中,通常由调用者来创建被调用者的实例,然而依赖注入的方式,创建被调用者不再由调用者创建实例,创建被调用者的实例的工作由IOC容器来完成,然后注入到调用者。因此也被称为依赖注入。

作用

将各层的对象以松耦合的方式组织在一起,解耦,各层对象的调用完全面向接口。当系统重构或者修改的时候,代码的改写量将大大减少。

Android Studio 引入Dagger2

1.在工程根目录的build.gradle引入apt插件

classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’

整体文件:

// 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.0-alpha1’

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

}

2.在app目录下的build.gradle加上一行

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

dependencies下面加上:

apt ‘com.google.dagger:dagger-compiler:2.2’

provided ‘org.glassfish:javax.annotation:10.0-b28’

compile ‘com.google.dagger:dagger:2.2’

别忘了加入lint warning

lintOptions {

warning ‘InvalidPackage’

}

整体文件:

apply plugin: ‘com.android.application’

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

android {

compileSdkVersion 23

buildToolsVersion “23.0.3”

defaultConfig {

applicationId “com.iiseeuu.dagger2demo”

minSdkVersion 15

targetSdkVersion 23

versionCode 1

versionName “1.0”

testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’

}

}

lintOptions {

warning ‘InvalidPackage’

}

}

dependencies {

compile fileTree(include: [‘*.jar’], dir: ‘libs’)

compile ‘com.android.support:appcompat-v7:23.4.0’

compile ‘com.android.support.constraint:constraint-layout:1.0.0-alpha1’

testCompile ‘junit:junit:4.12’

androidTestCompile ‘com.android.support.test.espresso:espresso-core:2.2.2’

androidTestCompile ‘com.android.support.test:runner:0.5’

androidTestCompile ‘com.android.support:support-annotations:23.4.0’

apt ‘com.google.dagger:dagger-compiler:2.2’

provided ‘org.glassfish:javax.annotation:10.0-b28’

compile ‘com.google.dagger:dagger:2.2’

compile ‘com.google.code.gson:gson:2.7’

}

@Inject介绍

注解(Annotation)来标注目标类中所依赖的其他类,同样用注解来标注所依赖的其他类的构造函数,那注解的名字就叫Inject

使用dagger2之前

public class A {

private String field;

public A(){

}

public void doSomething(){

Log.e(“A”, “do something”);

}

}

//Activity调用

public class MainActivity extends AppCompatActivity {

A a;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

a = new A();

a.doSomething();

}

}

使用dagger2之后

public class A {

private String field;

@Inject

public A(){

}

public void doSomething(){

Log.e(“A”, “do something”);

}

}

//Activity调用

public class MainActivity extends AppCompatActivity {

@Inject

A a;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

a.doSomething();

}

}

这样写直接运行的话 是会报错的,变量a没有被实例化,会报空指针错误。因为他们没有任何关联。这个时候我们需要把MainActivity和A类关联起来,这个时候就需要@Commponent了,下面请看@Commponent:

@Commponent

A类的构造函数与调用类Activity都使用Inject注解,Component一般是个接口,就是将MainActivity与A类桥接起来。

我们定义一个AComponent接口,并使用@Component注解:

@Component

public interface AComponent {

A a();

}

//Activity调用

public class MainActivity extends AppCompatActivity {

@Inject

A a;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

a = DaggerAComponent.builder().build().a();

a.doSomething();

}

}

AComponent会查找MainActivity中用Inject注解标注的属性,查找到相应的属性后会接着查找该属性对应的用Inject标注的构造函数(这时候就发生联系了),剩下的工作就是初始化该属性的实例并把实例进行赋值。因此我们也可以给Component叫另外一个名字注入器(Injector)

Component现在是一个注入器,就像注射器一样,Component会把A的实例注入到MainActivity中,来初始化MainActivity类中的依赖。

a = DaggerAComponent.builder().build().a();这种写法不太友好,一般情况下,我们只需要将MainActivity的实例交给AComponent引用即可。

public class MainActivity extends AppCompatActivity {

@Inject

A a;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

DaggerAComponent.builder().build().inject(this);

a.doSomething();

}

}

@Module

Module的职责是用来生成实例,可以把他比作一个工厂,专业生成各种类的实例。

项目中使用到了第三方的类库,第三方类库又不能修改,所以根本不可能把Inject注解加入这些类中,这时我们的Inject就失效了。

比如我项目中依赖GSON解析库。这个时候我需要新建一个类一个提供一个Gson的实例。

@Module

public class AModule {

@Provides

public Gson provideGson(){

return new Gson();

}

}

//component类 引用module为AModule.class

@Component(modules = AModule.class)

public interface AComponent {

void inject(MainActivity mainActivity);

}

//activity调用

public class MainActivity extends AppCompatActivity {

@Inject

A a;

@Inject

Gson gson;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

DaggerAComponent.builder().build().inject(this);

a.field = “test”;

String aStr = gson.toJson(a);

Log.e(“mainactivity”,”astr = “+aStr);

a.doSomething();

}

}

@Provides

module类对外提供实例方法的注解

注意:

component首先先去提供的module里面寻找提供的实例,没有找到时再去找构造函数@inject注解

一个component可以依赖多个module,一个component可以依赖另一个component

例如:AModule 提供A类的实例,GsonModule提供Gson的实例

//AModule.java

@Module

public class AModule {

@Provides

public A provideA(){

return new A();

}

}

//GsonModule.java

@Module

public class GsonModule {

@Provides

public Gson provideGson(){

return new Gson();

}

}

//component

@Component(modules = {AModule.class,GsonModule.class})

public interface AComponent {

void inject(MainActivity mainActivity);

}

//调用activity不变

@Scope and @Singleton

这个注解是用来划分作用域的,标记当前对象使用范围。

比如限制对象只能在所有Activity中使用,或者只能在Application中使用,或者只能在Fragment中使用

@Singleton 单例模式全局共用一个对象 就是@Scope的一个实现。

这个Scope比较难以理解,我们举个例子自定义一个Scope:

假如有个项目包含用户体系,用户登录成功后,A界面、B界面和C界面要依赖用户来获取一些数据,LoginActivity界面不依赖于用户体系。

我们想要把User对象实例可以在A、B、C界面共用。

那么整体项目的Scope划分结果图为:

6c447de89e6534dda44d78f01af70de1.png

untitled.jpg

1.自定义UserScope注解

@Scope

@Retention(RetentionPolicy.RUNTIME)

public @interface UserScope {

}

2.新建一个User实体类

public class User {

private String name;

private String avatar;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAvatar() {

return avatar;

}

public void setAvatar(String avatar) {

this.avatar = avatar;

}

}

3.新建一个UserModule来提供User的实例,提供实例方法使用自定义的UserScope注解,表示提供实例仅限于UserScope范围内使用。

@Module

public class UserModule {

private User user;

public UserModule(User user) {

this.user = user;

}

@Provides

@UserScope

User provideUser(){

return user;

}

}

4.新建Component桥梁。他是一个子Component,依赖于一个全局的父Component。

Subcomponent注解与Component依赖另一个Component有点像,他们区别在于:

Subcomponent可以获取到父Component的所有可以产生出的对象。

Component依赖则只能获取到被依赖的Component所暴露出来的可以生产的对象

@UserScope

@Subcomponent(modules = UserModule.class)

public interface UserComponent {

AComponent plus(AModule aModule);

BComponent plus(BModule bModule);

CComponent plus(CModule cModule);

}

5.新建一个全局的父Component,引用子Component。

@Singleton

@Component(modules = AppModule.class)

public interface AppComponent {

UserComponent plus(UserModule userModule);

}

6.新建一个AppModule,提供一个全局的application实例

@Module

public class AppModule {

private Application application;

public AppModule(Application application) {

this.application = application;

}

@Provides

@Singleton

public Application provideApplication() {

return application;

}

}

7.创建一个App实例,在程序启动时,调用它。

public class App extends Application{

//application组件

private AppComponent appComponent;

//用户组件

private UserComponent userComponent;

//获取当前application的实例

public static App get(Context context) {

return (App) context.getApplicationContext();

}

@Override

public void onCreate() {

super.onCreate();

//注入全局Application

appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();

}

//对外提供UserComponent

public UserComponent getUserComponent() {

return userComponent;

}

//注入UserComponent,调用此方法后,UserCope生效

public UserComponent createUserComponent(User user) {

userComponent = appComponent.plus(new UserModule(user));

return userComponent;

}

//释放UserComponent组件

public void releaseUserComponent() {

userComponent = null;

}

}

8.LoginActivity 点击登录按钮,创建User实例,并开始UserScope生命周期

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

User user = new User();

user.setName(etUserName.getText().toString());

user.setAvatar(etPassword.getText().toString());

App.get(MainActivity.this).createUserComponent(user);

startActivity(new Intent(MainActivity.this, AActivity.class));

}

});

9.AActivity、BActivity、CActivity 使用User对象为同一个User对象,LoginActivity是没有权限使用User对象的。

下面为详细代码:

//AActivity.java

public class AActivity extends AppCompatActivity{

@Inject

User user;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_a);

//注入

App.get(this).getUserComponent().plus(new AModule()).inject(this);

TextView textView = (TextView) findViewById(R.id.text);

textView.setText(“username:”+user.getName()+”user:”+user);

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

startActivity(new Intent(AActivity.this, BActivity.class));

}

});

}

}

//BActivity.java

public class BActivity extends AppCompatActivity{

@Inject

User user;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_b);

App.get(this).getUserComponent().plus(new BModule()).inject(this);

TextView textView = (TextView) findViewById(R.id.text);

textView.setText(“username:”+user.getName()+”–user:”+user);

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

startActivity(new Intent(BActivity.this, CActivity.class));

}

});

}

}

//CActivity.java

public class CActivity extends AppCompatActivity{

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_c);

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

App.get(CActivity.this).releaseUserComponent();

startActivity(new Intent(CActivity.this, MainActivity.class));

finish();

}

});

}

}

10.最终效果,打印出User对象地址都是同一个地址:user@1524f0fa

eff43e3b21d177c532f36a4f27fbb3f7.png

device-2016-06-23-.png

device-2016-06-23-144137.png

device-2016-06-23-.png

7945f1268ff64d881d25a5cbf19de7da.png

device-2016-06-23-.png

@Qualifier and @Named

@Named 其实是@Qualifier的一种实现,弄明白@Qualifier(限定符)基本上也就明白了@Named

@Qualifier(限定符)主要作用是用来区分不同对象实例。

假如上面示例系统支持多用户,在Activity中引用了两个不同的User实例,我们该怎么区分呢?

1.首先我们自定义一个@Qualifier 名称叫做UserNamed

@Qualifier

@Retention(RetentionPolicy.RUNTIME)

public @interface UserNamed {

String value() default “”;

}

2.修改UserModule对外提供两个User实例userA和UserB,并使用@UserNamed注解标识实例

@Module

public class UserModule {

private User userA;

private User userB;

public UserModule(User userA,User userB) {

this.userB = userB;

this.userA = userA;

}

@UserNamed(“a”)

@Provides

@UserScope

User provideUserA(){

return userA;

}

@UserNamed(“b”)

@Provides

@UserScope

User provideUserB(){

return userB;

}

}

3.修改App.java的createUserComponent 传入两个实例

public UserComponent createUserComponent(User userA,User userB) {

userComponent = appComponent.plus(new UserModule(userA,userB));

return userComponent;

}

4.登录时创建2个实例

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

User userA = new User();

userA.setName(etUserName.getText().toString()+”AAA”);

userA.setAvatar(etPassword.getText().toString());

User userB = new User();

userB.setName(etUserName.getText().toString()+”BBB”);

userB.setAvatar(etPassword.getText().toString());

App.get(MainActivity.this).createUserComponent(userA,userB);

startActivity(new Intent(MainActivity.this, AActivity.class));

}

});

5.Activity中的使用:

public class AActivity extends AppCompatActivity{

@UserNamed(“a”)

@Inject

User userA;

@UserNamed(“b”)

@Inject

User userB;

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_a);

//注入

App.get(this).getUserComponent().plus(new AModule()).inject(this);

TextView textView = (TextView) findViewById(R.id.text);

textView.setText(“username:”+userA.getName()+”–user:”+userA+””+”username:”+userB.getName()+”–user:”+userB);

findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

startActivity(new Intent(AActivity.this, BActivity.class));

}

});

}

}

我们在回头看下我们自定义的@UserNamed与系统定义的@Named源码的区别大家应该就能明白了~两个类的实现是一样的代码

//@UserNamed定义:

@Qualifier

@Retention(RetentionPolicy.RUNTIME)

public @interface UserNamed {

String value() default “”;

}

//@Named的定义:

@Qualifier

@Documented

@Retention(RUNTIME)

public @interface Named {

/ The name. */

String value() default “”;

}

dagger2 主要功能 就此介绍完毕,如果觉得不错,就尽快用起来吧~

参考:

demo地址:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/202232.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 上午7:35
下一篇 2026年3月20日 上午7:35


相关推荐

  • mysql基础知识笔记

    mysql基础知识笔记

    2022年2月21日
    53
  • iframe的使用

    iframe的使用页面使用 iframe 在页面中嵌套另一个页面

    2026年3月17日
    2
  • Postman解决token传参问题

    Postman解决token传参问题postman 解决 token 传参问题

    2026年3月16日
    2
  • tomcat修改端口号后无法访问

    tomcat修改端口号后无法访问在保证配置全部正确 启动正常 访问 web 服务正常的情况下 将端口号 8080 修改后无法访问 修改端口 修改 server xml 文件 无法访问 解决 始终打开 tomcat 即打开 bin 目录下的 startup bat 打开后再次访问即可

    2025年12月5日
    5
  • 奇怪的电梯

    奇怪的电梯奇怪的电梯【问题描述】某栋大楼有一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第i层楼(1≤i<N)上有一个数字K(≤K≤N)电梯只有四个按钮:开、关、上、下。上、下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:33125代表了Ki(K1=3,K2=3,…),从一层开始。在一层按“上”可以到4层,按“下”是不起作用的,因为没有-2层。那么从A层到B层至少要按几次按钮呢?【输入格式】第1行为3个用1个空格隔开的正整数,表示N、A、B(l≤N≤200,1≤

    2022年6月14日
    41
  • 【Visual C++】游戏开发笔记十四 游戏画面绘图(四) 华丽的CImage类

    【Visual C++】游戏开发笔记十四 游戏画面绘图(四) 华丽的CImage类本系列文章由 zhmxy555 编写 转载请注明出处 nbsp nbsp http blog csdn net zhmxy555 article details 作者 毛星云 nbsp nbsp nbsp nbsp 邮箱 nbsp nbsp nbsp nbsp nbsp 欢迎邮件交流编程心得我们知道 Visual nbsp C 中的 CBitmap 类的功能简直太弱小了 这曾经让 VisualC 在图像处理方面的功能比较尴尬 之前笔记里面 我们

    2026年3月19日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号