IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Dagger2实战详解以及Hilt的使用 -> 正文阅读

[移动开发]Dagger2实战详解以及Hilt的使用

Dagger2是一个依赖注入(DI)框架。那么什么是依赖注入?其实我们一直在使用依赖注入,只是没有提到这个概念。例如下面最简单的例子:

class Car{
	Engine engine;
	//1.通过构造器依赖注入
	public User(){
		engine=new Engine();
	}
	//2.通过set方法依赖注入
	public setEngine(engine){
		this.engine=engine;
	}
}

Engine engine对象的来源要么是自己在构造函数里面new出来,要么是通过setEngine()从外部传入。这两种方式都是依赖注入。都是需要我们手动new对象的。
而我们要讨论的是另外一种依赖注入方式,就是要把new对象的操作统一放在一个地方一起操作,而不是在业务代码里面到处new对象。这种方式有个专门的名称叫做控制反转(IOC),控制反转的意思就是new对象不是由用户亲自操作,而是由专门的机制替用户来完成。提供这种机制的框架我们称为DI框架或者IOC框架,IOC和DI的概念本质上就是对提供这种机制的框架的不同的描述方式罢了,有时两者概念可以通用,最典型的就是Java后端用的Spring IOC框架(官方是这样叫的,如果你愿意叫Spring DI框架也是没什么问题的)。而在Android中,就是我们的Dagger框架。

Dagger历史

Dagger1最初是由square公司开发的,google要推出依赖注入框架,发现square公司已经开发出来了,于是接手了Dagger1,并改了名字叫Dagger2。依赖注入框架有两者实现方式,一种是通过反射在运行时注入,或者叫动态注入,Spring IOC就是使用这种方式实现。另一种就是在编译时通过APT实现,或者叫静态注入。Dagger1是静态动态混合开发的,Dagger2是通过静态方式开发的。Dagger的本意是DAG(有向无环图)的意思,有向无环图就是指对象之间的依赖关系。加上后缀就变成了dagger,正好是一个英文单词。而dagger这个单词就是匕首的意思。但这把匕首并不是特别好用,主要原因是dagger资料太少,官方文档举了几个Coffe的例子,并没有什么实战意义,而国内能搜索到的dagger博客很多也是一知半解,而且对于android开发来说依赖注入的概念还是比较陌生的,还有一个主要的原因是用起来也比较麻烦。
难的原因:
1.中文资料少
2.依赖注入概念比较陌生
3.框架本身用起来麻烦
4.使用不当(不易察觉)造成非常奇怪的问题

单独一个原因还好,多个加在一起就非常的头疼。但Dagger框架本身是没什么问题的,也没什么bug。为了降低使用难度,google推出了Hilt,Hilt是基于Dagger的,并不是一个全新的框架。Hilt也英文意思是刀柄,意思是Dagger这个匕首的刀柄并不好用,给你换个。
我缺的是刀柄吗?你官方多写点和Android相关的实战例子会死?
既然是基于Dagger那么本质上你还是要学Dagger的,而且理解Dagger本身的概念才是核心,重点还是在Dagger,Hilt只是语法糖。

1.基本例子

下面我们使用Dagger来替我们new对象。
第一步:我们在User的构造方法上面添加一个@Inject注解,这是告诉Dagger,User这个类你帮我创建。

import javax.inject.Inject;

public class User {
    @Inject
    public User() {
    }
}

第二步:我们在MainActivity声明我们的User user;并且添加@Inject注解。

public class MainActivity extends AppCompatActivity {
    @Inject
    User user;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        
    }

这里可能就有个比较然后迷惑的地方,为什么这个@Inject可以在构造函数上又可以在常用变量上?因为放在不同位置,这个注解的作用是不一样的。我们看一下他的实现:

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}

这个注解可以放在方法,构造器和成员变量上,如果放在构造器,表示这个类需要由
Dagger替我们创建,如果放在成员变量上,表示这个成员变量的对象由Dagger为我们提供,类似于生产者消费者的关系,而Dagger只是个生产对象的工厂。

第三步:注入依赖。经过前面的两步,我们已经有了变量声明User user;和Dagger为我们new出来的User()对象,但是这两者还没有关联起来,现在就是要将他们关联起来。我们需要用到Dagger提供的组件将他们关联起来。创建接口并且添加@Component注解,接口名字是可以随便取的。这里的inject方法名字是可以随便取的,里面的MainActivity表示在哪里完成注入。这里可能就非常令人困惑了,这到底是要干什么?

@Component
public interface AppComponent {
    /**
     * 这个方法的名字是随便取的
     * @param mainActivity
     */
    void inject(MainActivity mainActivity);

    //void inject(SecondActivity secondActivity);
}

下面我们要Build或者Rebuild一下工程,就会真相大白了。
我们可以看到在build目录下面多出了三个文件,DaggerAppComponent,User_Factory和MainActivity_MembersInjector。这三个类都是Dagger通过APT帮我们生成的,User_Factory非常明显就是用来创建User对象的,DaggerAppComponent就是我们上面写的AppComponent的实现类,而MainActivity_MembersInjector只是个中间的工具类。
来看下面的DaggerAppComponent一个关键代码你就明白了,这个方法将new出来的User和MainActivity关联上了。
大致原理就是这样,三个类的源码也比较简单,打开看一下就明白了。总之,通过这些生成类就可以实现关联了。
在这里插入图片描述

  @CanIgnoreReturnValue
  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectUser(instance, new User());
    return instance;
  }

第四步:调用执行关联代码。代码生成了,但是必须调用才能执行啊(这步其实框架本来可以替我们完成,但dagger没有,后面Hilt帮我们完成了)。

public class MainActivity extends AppCompatActivity {
    @Inject
    User user;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //关联
        DaggerAppComponent.create().inject(this);
        Log.d("MainActivity", "user:" + user);
    }

这时候运行代码,我们发现User对象已经自动为我们注入了。

 D/MainActivity: user:com.xzh.androidbase.mvp.model.entry.User@3571984

这里可能有同学就问了,我就是简单的new一个User对象,直接new不就完了吗?有必要这么麻烦吗?这就要聊聊Dagger到底解决了什么痛点,或者说,他到底有什么优势。
1.解耦。你写的一个构造方法,但是有一天这个构造方法改了,那么所有用到的地方都要修改。什么?通过重构工具也可以?是不可以的,因为多或者少参数都是不能重构的。
2.单例。通过Dagger提供的单例功能,可以自动生成单例,而不要自己写单例。什么?我可以自己写单例,而且有工具可以自动生成单例?那你可以写局部单例吗,只在单个Activity生效?这些Dagger都可以帮你做到,不用自己实现单例。
3.测试。是的,Dagger结合测试是非常方便的,因为测试需要不停的mock对象,而Dagger正好就是提供对象的。什么?我从来不写测试,而且没时间写?那你等着出bug慢慢找bug吧!
4.封装和简洁性。通过将new对象这件事情封装起来,不会看到到处凌乱的new对象代码,项目将变得非常简洁。
5.方便管理。对象的创建方式可能发生修改,可能要到处修改创建的地方,找起来比较麻烦,用Dagger后就可以在一个地方统一管理。

我觉得单单测试这一个优点就有足够的理由使用Dagger。就当作是用Dagger来给测试做mock对象的准备吧。

2.项目实际例子

上面的User例子只是个玩具例子,在实际项目中是没什么意义。而且有时候对象并不是我们自己写的,而是第三方框架里面的,那样的话我们就不能在构造函数上面使用@Inject注解了。这时候我们需要使用另一种方式来注入依赖

2.1使用Module注入需要依赖的模块

在项目中我们经常需要用到网络,Retrofit,Okhttp等,这些都是第三方提供的,我们不能在他们的类的构造器上用@Inject注解,但非要这样的话,就只能继承那个类,这样就违背了我们的初衷,将代码变得更难以维护了。我们使用的新方式是用Dagger给我们提供的Module机制,我们看下面的代码。
我们定义一个类并给他添加@Module注解,写一个方法返回Retrofit对象,并在这个方法上面添加@Provides注解,表示这个是需要注入这个返回的对象,相对于@Inject注解,只是他是用在@Module里面了。

@Module
public class NetModule {

    @Provides
    public Retrofit provideRetrofit(){
        return new Retrofit
                .Builder()
                .baseUrl("www.google.com")
                .build();
    }
}

在Component里面指定我们刚刚写的NetModule,可以指定多个Module。这样我们就已经可以提供依赖注入的方式获取Retrofit对象了

@Component(modules = {NetModule.class})
public interface AppComponent {
    /**
     * 这个方法的名字是随便取的
     * @param mainActivity
     */
    void inject(MainActivity mainActivity);

    //void inject(SecondActivity secondActivity);
}

老样子,我们现在直接用@Inject把Retrofit变量注入就可以用了。

public class MainActivity extends AppCompatActivity {
 
    @Inject
    Retrofit retrofit;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerAppComponent.create().inject(this);
        Log.d("MainActivity", "retrofit:" + retrofit);
     }

输出结果。

D/MainActivity: retrofit:retrofit2.Retrofit@c8c02a7

现在对原来的代码做一些改动,provideRetrofit方法多了一个OkHttpClient clint的参数,这个参数的内容可以通过上面的provideOkHttpClient方法获取。这是Dagger提供的一种机制,参数真实对象可以提供别的被@Provides注解的方法注入。

@Module
public class NetModule {

    @Provides
    public OkHttpClient providekHttpClient(){
        return new OkHttpClient.Builder()
                .callTimeout(10, TimeUnit.SECONDS)
                .build();
    }

    @Provides
    public Retrofit provideRetrofit(OkHttpClient client ){
        return new Retrofit
                .Builder()
                .baseUrl("https://www.google.com")
                .client(client)
                .build();
    }
}

更进一步我们可以获取到网络请求的Service类。到这里,已经能够应付大部分的开发需求了。

 @Provides
    public RepoService provideRepoService(Retrofit retrofit){
        return retrofit.create(RepoService.class);
    }

到目前为止,用Module机制注入依赖就讲完了。下面我们进一步思考一些细节问题。
Retrofit这个对象每次获取到的对象都是不一样的,而且Retrofit这个对象是非常复杂的,里面的变量非常的多,也就是说,创建Retrofit对象是非常的销毁资源的。所以什么样什么办法将Retrofit对象设为单例?既然我们已经用的Dagger,那么当然是用Dagger的方式来实现单例。

2.2利用作用域实现单例

先看一下用法,后面解释为什么要这样使用。
在每个Provides上面加上@Singleton注解

@Module
public class NetModule {

    @Singleton
    @Provides
    public OkHttpClient providekHttpClient(){
        return new OkHttpClient.Builder()
                .callTimeout(10, TimeUnit.SECONDS)
                .build();
    }

    @Singleton
    @Provides
    public Retrofit provideRetrofit(OkHttpClient client ){
        return new Retrofit
                .Builder()
                .baseUrl("https://www.google.com")
                .client(client)
                .build();
    }

    @Singleton
    @Provides
    public RepoService provideRepoService(Retrofit retrofit){
        return retrofit.create(RepoService.class);
    }
}

在AppComponent上也加上@Singleton。这样就已经实现单例的,使用起来还是比较方便的。

@Singleton
@Component(modules = {NetModule.class})
public interface AppComponent {
    /**
     * 这个方法的名字是随便取的
     * @param mainActivity
     */
    void inject(MainActivity mainActivity);

    //void inject(SecondActivity secondActivity);
}

下面测试一下。我们测试RepoService这个对象。

public class MainActivity extends AppCompatActivity {

    @Inject
    RepoService repoService;

    @Inject
    RepoService repoService2;

    @Inject
    RepoService repoService3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerAppComponent.create().inject(this);
        Log.d("MainActivity", "repoService1:" + repoService);
        Log.d("MainActivity", "repoService2:" + repoService2);
        Log.d("MainActivity", "repoService3:" + repoService3);
 		}

 
}

可以发现三个对象都是一样的。

D/MainActivity: repoService1:retrofit2.Retrofit$1@5a3cfde
D/MainActivity: repoService2:retrofit2.Retrofit$1@5a3cfde
D/MainActivity: repoService3:retrofit2.Retrofit$1@5a3cfde

但如果你在另外一个Activity或者Fragment注入这个RepoService的对象的话,你会发现是另外一个对象。这就说明这个单例并不是全局单例,而仅仅是在某个Activity内才有效的局部单例,这显然是不满足要求的。

D/SecondActivity: repoService1:retrofit2.Retrofit$1@7f6cd63
D/SecondActivity: repoService2:retrofit2.Retrofit$1@7f6cd63
D/SecondActivity: repoService3:retrofit2.Retrofit$1@7f6cd63

那么到底是怎么回事,是Dagger提供的这个@Singleton有问题吗?Dagger的设计者应该也没这么傻。这里就要说说Scope的概念。

2.3 Dagger的注入流程分析

这点是重中之重,没弄明白这个后面将非常难懂。我们来回顾一下下面这个inject方法。

@Singleton
@Component(modules = {NetModule.class })
public interface AppComponent {
    void inject(MainActivity mainActivity);
    //void setValue(MainActivity mainActivity)
}

这个方法是我们自己定义的,名字其实可以随便取,这个方法的关键点是他是一个void方法,这是有特别的含义的,他的本质其实是一个set方法。什么?set方法?是的,没有开玩笑,虽然非常奇怪,但他就是。他的本质就是类似下面的代码,将Module里面的provider的对象赋值给MainActivity的成员变量,就是在这里依赖注入的。所以一般这个方法取名字叫inject,你也可以取名字叫setValue。

public void inject(MainActivity mainActivity){
      mainActivity.成员1=module.对应的provider;
      mainActivity.成员2=module.对应的provider;
}

你这么知道是这样的?因为源码就是这样实现的,其实这个inject方法才是Dagger的精髓和切入口。我们来看下源码就知道了。下面我们把inject这个方法名改为setValue,因为在源码里面可能引起误解。

```java
@Singleton
@Component(modules = {NetModule.class })
public interface AppComponent {
    //void inject(MainActivity mainActivity);
     void setValue(MainActivity mainActivity)
}

public final class DaggerAppComponent implements AppComponent {
  @Override
  public void setValue(MainActivity mainActivity) {
    injectMainActivity(mainActivity);}

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectUser(instance, provideUserProvider.get());
    MainActivity_MembersInjector.injectRetrofit(instance, provideRetrofitProvider.get());
    MainActivity_MembersInjector.injectClient(instance, providekHttpClientProvider.get());
    MainActivity_MembersInjector.injectRepoService(instance, provideRepoServiceProvider.get());
    MainActivity_MembersInjector.injectRepoService2(instance, provideRepoServiceProvider.get());
    MainActivity_MembersInjector.injectRepoService3(instance, provideRepoServiceProvider.get());
    return instance;
  }
 }

我们看到setValue方法,也就是原来的inject方法调用injectMainActivity(MainActivity instance)方法,而这个方法调用 MainActivity_MembersInjector.injectXXX方法。这里的injectXXX方法名是Dagger取的,前面方法名换成setValue就是为了说明这个injectXXX名字不是我们定义的。(小问题,源码看一下就明白了,取名setValue也更能说明这个方法的含义)
以Retrofit为例。
MainActivity_MembersInjector.injectRetrofit(instance, provideRetrofitProvider.get());
我们看看injectRetrofit的实现,如下面的代码,看参数就明白了,就是将Module的Retrofit对象赋值给MainActivity的Retrofit对象,这样就完成了依赖注入(也就是赋值)了。

  @InjectedFieldSignature("com.xzh.androidbase.mvp.ui.activity.MainActivity.retrofit")
  public static void injectRetrofit(MainActivity instance, Retrofit retrofit) {
    instance.retrofit = retrofit;
  }

到这里,注入流程就讲完了,其实也不难,只是这个void inject(MainActivity mainActivity)方法太奇怪了,不解释谁知道是这个含义?在接口里面定义一个这种方法让新手摸不着头脑。其实这个设计是非常糟糕的,有点暗示的味道,暗示你妹啊,谁知道你什么意思?按理说,应该在这个方法上面设计一个类似@setter注解来标记这个方法,表明这个方法是用来赋值的,但是这样又容易和@Inject注解混淆,因为@Inject也有赋值的含义,而且又不能用@Inject来标记方法,这样做的话,@Inject注解用法就很混乱。取别的名字也不太好,干脆不取了,就默认叫void inject来暗示这是赋值方法。其实这个名字强制写为setValue比较好,并且强制不能取别的名字,要是能随便改名字就没有暗示的味道了。叫inject容易和@Inject搞混,新手会有这样的疑惑,怎么别的地方@Inject这里又inject?也不知道是谁想取的inject说这么多,就是为了能弄明白这个方法的真正含义,有点罗嗦了。

2.4 Scope

作用域(Scope)的定义:将某个对象的生命周期限定为其组件的生命周期。
前面的@Singleton只能实现局部单例的效果,那么是为什么呢?关键点就在于DaggerAppComponent create的时间,在前面我们都是在Activity里面create的,要想变成全局的,只需要在Application里面创建这个DaggerAppComponent就可以了。

public class App extends Application {
    static AppComponent appComponent=   DaggerAppComponent.create();

    public static AppComponent getAppComponent() {
        return appComponent;
    }
 }

然后在Actvity里面调用:这样就可以实现全局单例了。为什么这样就是全局单例?这里就需要理解前面小结的内容了。inject(this)就是给MainActivity里面被@Inject标记的成员变量赋值,那么对象从哪里来?从App.getAppComponent()这里来的,也就是DaggerAppComponent对象,DaggerAppComponent再get对应的provider对象,而DaggerAppComponent对象现在是App里面成员变量,也就是个全局变量,并且是static的,所以可以实现全局单例的效果。这只能说明全局。要想说明单例是要看Dagger这么实现Scope。

       // DaggerAppComponent.create().inject(this);
       App.getAppComponent().inject(this);

2.5组件依赖

@Component有两个成员变量,modules前面已经用过了,现在我们需要使用dependencies这个变量。

@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
@Target(TYPE)
@Documented
public @interface Component {
 
  Class<?>[] modules() default {};
 
  Class<?>[] dependencies() default {};
}

用法用法也非常的简单,直接指定需要依赖的Component就行。
我们定义一个UserComponent来依赖原来的AppComponent。

@UserScope
@Component(modules = {UserModule.class},dependencies = AppComponent.class)
public interface UserComponent {
}

这样我们定义的这个UserComponent就拥有了AppComponent的所有功能。和gradle模块直接的依赖是类似的。Dagger规定,不同的Component要有不同的Scope,依赖的时候必须有scope,不然报unscoped的错误,

UserComponent (unscoped) cannot depend on scoped components:

所以我们自定义了一个UserScope专门给UserComponent,UserScope的实现和Singleton一样,只是换了一个名字。

@Scope
@Documented
@Retention(RUNTIME)
public @interface UserScope {}

现在贴出代码来说明一些问题,代码必须要写成下面的样子。后面解释。

@UserScope
@Component(modules = {UserModule.class},dependencies = AppComponent.class)
public interface UserComponent {

    void inject(MainActivity mainActivity);
    void inject(SecondActivity secondActivity);
}

使用dependencies指定依赖类,定义了两个inject方法,被依赖的Component不需要写这两个方法了,移交给UserComponent。你写了也没事,并不会报错。因为在实际使用中不太可能直接用AppComponent这个组件。既然用不到,写了也没用。非要用就写一个吧。也没事。还有一个需要注意的是@UserScope这个注解,UserComponent使用了这个注解,那么他的modules的方法也要写上这个注解。不然报错。

@Singleton
@Component(modules = {NetModule.class })
public interface AppComponent {
    /**
     * 这个方法的名字是随便取的
     *
     */
   // void setValue(MainActivity mainActivity);

    //void inject(SecondActivity secondActivity);

     Retrofit retrofit();

    RepoService repoService();

     OkHttpClient okHttpClient();
}

这里需要注意的是在组件依赖的时候,被依赖的组件的模块的provide人方法全部要在组件里面声明,不然会报错,其实这个设计比较糟糕,完全是不需要的,你Dagger都是可以知道有哪些方法是被@Provider注解的,为什么不帮我们内部实现就好了?

@Module
public class UserModule {
    @UserScope
    @Provides
    public User provideUser(){
        return new User();
    }

    @UserScope
    @Provides
    public Repo provideRepo(){
        return new Repo("xzh");
    }

}

UserModule,比较简单,用户这个业务功能可能使用到的对象都在这里提供了。
下面进行最关键的一步——依赖注入。我们分别在MainActivity和SecondActivity里面使用UserComponent实现注入。

public class MainActivity extends AppCompatActivity {
    @Inject
    User user;

    @Inject
    User user2;
    @Inject
    Retrofit retrofit;

    @Inject
    OkHttpClient client;

    @Inject
    RepoService repoService;

    @Inject
    RepoService repoService2;

    @Inject
    RepoService repoService3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		DaggerUserComponent.builder()
		.appComponent(App.getAppComponent())
		.build()
		.inject(this);

        Log.d("MainActivity", "user:" + user);
        Log.d("MainActivity", "user2:" + user2);
        Log.d("MainActivity", "retrofit:" + retrofit);
        Log.d("MainActivity", "repoService1:" + repoService);
        Log.d("MainActivity", "repoService2:" + repoService2);
        Log.d("MainActivity", "repoService3:" + repoService3);
        startActivity(new Intent( MainActivity.this,SecondActivity.class));

    }

代码和前面的一样,只是这次我们使用的是DaggerUserComponent这个实现类,而且我们这次没有使用create方法,而是使用了builder方法,为什么不用create方法了呢?因为在使用组件依赖后,Dagger就不提供create方法了,只提供builder实现。create方法只适合在简单的情况下使用,一组件依赖,就复杂了,需要用户自己build,Dagger只能帮你到这了。最主要原因就是appComponent(App.getAppComponent())这个方法,这是设置需要依赖组件的方法,我们可能依赖多个组件,Dagger不知道我们现在想用哪个,所以不能给我们简单的全部用一个create就搞定了。下面我们看看SecondActivity的代码。

public class SecondActivity extends AppCompatActivity {
    @Inject
    User user;

    @Inject
    User user2;
    @Inject
    RepoService repoService;

    @Inject
    RepoService repoService2;

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

        DaggerUserComponent.builder().appComponent(App.getAppComponent()).build().inject(this);

        Log.d("SecondActivity", "user:" + user);
        Log.d("SecondActivity", "user2:" + user2);
        Log.d("SecondActivity", "repoService:" + repoService);
        Log.d("SecondActivity", "repoService2:" + repoService2);
    }
}

和MainActivity是差不多的,我们注入了几个变量,用于测试。
到目前为止,组件依赖已经全部完成了,我们来运行一下代码。

D/MainActivity: user:com.xzh.androidbase.mvp.model.entry.User@b4dbe39
D/MainActivity: user2:com.xzh.androidbase.mvp.model.entry.User@b4dbe39
D/MainActivity: retrofit:retrofit2.Retrofit@9258c7e
D/MainActivity: repoService1:retrofit2.Retrofit$1@d3f65df
D/MainActivity: repoService2:retrofit2.Retrofit$1@d3f65df
D/MainActivity: repoService3:retrofit2.Retrofit$1@d3f65df
D/SecondActivity: user:com.xzh.androidbase.mvp.model.entry.User@e8b7e6b
D/SecondActivity: user2:com.xzh.androidbase.mvp.model.entry.User@e8b7e6b
D/SecondActivity: repoService:retrofit2.Retrofit$1@d3f65df
D/SecondActivity: repoService2:retrofit2.Retrofit$1@d3f65df

这里我们重点关注repoService和user变量,repoService不管在MainActivity还是在SecondActivity都是单例,而user在MainActivity里面是一个单例,在SecondActivity里面是另一个单例。也就是所谓的局部单例。而我们使用的依赖组件是UserComponent,UserComponent是一个局部单例组件,所以user对象是一个局部单例,但他依赖了AppComponent,而AppComponent是一个全局单例,所以repoService变量是一个全局单例,这样就实现一个组件既有全局单例,又有局部单例的功能。是不是非常的神奇?还是有点难度的,都写几遍就明白了。

如何让UserComponent也变成全局变量呢?非常的简单,和AppComponent一样,把UserComponent写到Application里面就可以了。如下:

public class App extends Application {
    static AppComponent appComponent=   DaggerAppComponent.create();
   static UserComponent userComponent;

    public static UserComponent getUserComponent() {
        return userComponent;
    }

    public static AppComponent getAppComponent() {
        return appComponent;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        userComponent = DaggerUserComponent.builder().appComponent(appComponent).build();

    }
 
}

这样就可以实现UserComponent为全局变量。然后在MainActivity和SecondActivity里面都调用 App.getUserComponent().inject(this);这行代码就可以了。
到这里组件依赖这个最难的点就完成了。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-10-22 11:03:19  更:2021-10-22 11:03:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 0:53:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码