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 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android _ 从 Dagger2 到 Hilt 玩转依赖注入(一,面试宝典 -> 正文阅读

[移动开发]Android _ 从 Dagger2 到 Hilt 玩转依赖注入(一,面试宝典

================

提示: 我在学习 Dagger2 时,也阅读了很多文章和官方文档。有些作者会列举出所有注解的用法,有些作者只介绍用法而忽略解释自动生成的代码。我也在寻求一种易于理解 / 接受的讲法,最后我觉得先「基础注解」再「复杂注解」,边介绍用法边解释自动生成代码的方式,或许是更容易理解的方式。期待得到你的反馈~

在讨论的过程中,我们通过一个简单的例子来展开:假设我们有一个用户数据模块,它依赖于两个依赖项:

public class UserRepository {
    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }
} 

首先,你可以选择不使用依赖注入,那么你可能就会在项目多处重复构建,缺点我们在第一节都讨论过了。

new UserRepository(new UserLocalDataSource(), new UserRemoveDataSource()); 

后来,有追求的你已经开始使用依赖注入,你写了一个全局的工具方法:

public static UserRepository get() {
    return new UserRepository(new UserLocalDataSource(), new UserRemoveDataSource());
} 

这确实能满足需求,然而在真实项目中,模块之间的依赖关系往往比这个例子要复杂得多。此时,如果经常手动编写依赖注入的模板代码,不仅耗时耗力,也容易出错。下面,我们开始使用 Dagger2 这个帮手来替我们编写模板代码。

3.1 @Component + @Inject

@Component 和 @Inject 是 Dagger2 最基础的两个注解,仅使用这两个注解就可以实现最简单的依赖注入。

  • @Component:创建一个 Dagger 容器,作为获取依赖项的入口
@Component
public interface ApplicationComponent {
    UserRepository userRepository();
} 
  • @Inject:指示 Dagger 如何实例化一个对象
public class UserRepository {

    private final UserLocalDataSource userLocalDataSource;
    private final UserRemoteDataSource userRemoteDataSource;

    @Inject
    public UserRepository(UserLocalDataSource userLocalDataSource, UserRemoteDataSource userRemoteDataSource) {
        this.userLocalDataSource = userLocalDataSource;
        this.userRemoteDataSource = userRemoteDataSource;
    }
}
--------------------------------------------
public class UserLocalDataSource {
    @Inject
    public UserLocalDataSource() {
    }
}
--------------------------------------------
public class UserRemoveDataSource {
    @Inject
    public UserRemoveDataSource() {
    }
} 

你需要用 @Inject 注解修饰依赖项的构造方法,同时,它的依赖项 UserLocalDataSource 和 UserRemoteDataSource 也需要增加 @Inject 注解。

以上代码在构建后会自动生成代码:

DaggerApplicationComponent.java

1、实现 ApplicationComponent 接口
public final class DaggerApplicationComponent implements ApplicationComponent {
    private DaggerApplicationComponent() {
    }
    
    2、创建依赖项实例
    @Override
    public UserRepository userRepository() {
        return new UserRepository(new UserLocalDataSource(), new UserRemoteDataSource());
    }

    3、构建者模式
    public static Builder builder() {
        return new Builder();
    }

    public static ApplicationComponent create() {
        return new Builder().build();
    }

    public static final class Builder {
        private Builder() {
        }

        public ApplicationComponent build() {
            return new DaggerApplicationComponent();
        }
    }
} 

可以看到,最简单的依赖注入模板代码已经自动生成了。使用时,你只需要通过 ApplicationComponent 这个入口就可以获得 UserReopsitory 实例:

ApplicationComponent component = DaggerApplicationComponent.create();

UserRepository userRepository = component.userRepository(); 

3.2 @Inject 字段注入

有些类不是使用构造器初始化的,例如 Android 框架类 Activity 和 Fragment 由系统实例化,此时就不能再使用 3.1 节 中使用的构造器注入,可以改为字段注入,并手动调用方法请求注入。

构造器注入:(X)
public class MyActivity {
    @Inject
    public MyActivity(LoginViewModel viewModel){
        ...
    }
}
--------------------------------------------
字段注入:
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var viewModel: LoginViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        DaggerApplicationComponent.create().inject001(this)
        super.onCreate(savedInstanceState)
        ...
    }
}
public class LoginViewModel {
    private final UserRepository userRepository;

    @Inject
    public LoginViewModel(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
} 

在 Activity 或 Fragment 中使用时,需要注意组件的生命周期:

  • 在 super.onCreate() 中的恢复阶段,Activity 会附加绑定的 Fragment,这些 Fragment 可能需要访问 Activity。为保证数据一致性,应在调用 super.onCreate() 之前在 Activity 的 onCreate() 方法中注入 Dagger。

  • 在使用 Fragment 时,应在 Fragment 的 onAttach() 方法中注入 Dagger,此操作可以在调用 super.onAttach() 之前或之后完成。

3.3 @Singleton / @Scope

  • @Singleton / @Scope:声明作用域,可以约束依赖项的作用域周期
@Singleton
public class UserRepository {
    ...
}
--------------------------------------------
@Component
@Singleton
public interface ApplicationComponent {
    ...
} 

在 ApplicationComponent 和 UserRepository 上使用相同的作用域注解,表明两者处于同一个作用域周期。这意味着,同一个 Component 多次提供该依赖项都是同一个实例。你可以直接使用内置的 @Singleton,也可以使用自定义注解:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
--------------------------------------------
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomScope {} 

提示: 使用 @Singleton 或 @MyCustomScope,效果是完全一样的。

以上代码在构建后会自动生成代码:

public final class DaggerApplicationComponent implements ApplicationComponent {
    private Provider<UserRepository> userRepositoryProvider;

    private DaggerApplicationComponent() {
        initialize();
    }

    private void initialize() {
        this.userRepositoryProvider = DoubleCheck.provider(UserRepository_Factory.create(UserLocalDataSource_Factory.create(), UserRemoteDataSource_Factory.create()));
    }

    @Override
    public UserRepository userRepository() {
        return userRepositoryProvider.get();
    }
    ...
} 

作用域注解约束

有几个关于作用域注解的约束,你需要注意下:

  • 如果某个组件有作用域注解,那么该组件只能给提供带有该注解的类或者不带任何作用域注解的类;
  • 子组件不能使用和某个父组件的相同的作用域注解。

提示: 关于子组件的概念,你可以看 第 3.5 节

作用域注解规范

只要你满足上面提到的约束规则,Dagger2 框架并不严格限制你定义的作用域语义。你可以按照业务划分作用域,也可以按照生命周期划分作用域。例如:

按照业务划分:
@Singleton
@LoginScope
@RegisterScope
--------------------------------------------
按声明周期划分:
@Singleton
@ActivityScope
@ModuleScope
@FeatureScope 

不过,按照生命周期划分作用域是更加理想的做法,作用域不应该明确指明其实现目的。

3.4 @Module + @Providers

  • @Module + @Providers:指示 Dagger 如何实例化一个对象,但不是以构造器的方式
public class UserRemoteDataSource {
    private final LoginRetrofitService loginRetrofitService;
    @Inject
    public UserRemoteDataSource(LoginRetrofitService loginRetrofitService) {
        this.loginRetrofitService = loginRetrofitService;
    }
}
--------------------------------------------
@Module
public class NetworkModule {
    @Provides
    public LoginRetrofitService provide001(OkHttpClient client) {
        return new Retrofit.Builder()
                .baseUrl("https://example.com")
                .build()
                .create(LoginService.class);
    }
}
--------------------------------------------
@Singleton
@Component(modules = NetworkModule.class)
public interface ApplicationComponent {

    UserRepository userRepository();

    void inject001(MainActivity activity);
} 

@Module 模块提供了一种与 @Inject 不同的提供对象实例的方式。在 @Module 里,@Provides 方法的返回值是依赖项实例,而参数是进一步依赖的对象。另外,你还需要在 @Component 参数中应用该模块。

目前为止,我们构造的依赖关系图如下所示:

3.5 @Subcomponent

  • @Subcomponent:声明子组件,使用子组件的概念可以定义更加细致的作用域

子组件是继承并扩展父组件的对象图的组件,子组件中的对象就可以依赖于父组件中提供的对象,但是父组件不能依赖于子组件依赖的对象(简单的包含关系,对吧?)。

我们继续通过一个简单的例子来展开:假设我们有一个登录模块 LoginActivity,它依赖于 LoginModel。我们的需求是定义一个子组件,它的声明周期只在一次登录流程中存在。在 第 3.2 节 提过,Activity 无法使用构造器注入,所以 LoginActivity 我们采用的是 @Inject 字段注入的语法:

@Subcomponent
public interface LoginComponent {
    void inject(LoginActivity activity);
} 

但是这样定义的 LoginComponent 还不能真正称为某个组件的子组件,需要增加额外声明:

@Module(subcomponents = LoginComponent.class)
public class SubComponentsModule {
}
--------------------------------------------
@Component(modules = {NetworkModule.class,SubComponentsModule.class})
@Singleton
public interface ApplicationComponent {
    UserRepository userRepository();
    LoginComponent.Factory loginComponent();
}
--------------------------------------------
@Subcomponent
public interface LoginComponent {
    @Subcomponent.Factory
    interface Factory{
le.class,SubComponentsModule.class})
@Singleton
public interface ApplicationComponent {
    UserRepository userRepository();
    LoginComponent.Factory loginComponent();
}
--------------------------------------------
@Subcomponent
public interface LoginComponent {
    @Subcomponent.Factory
    interface Factory{
 
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-29 09:28:21  更:2021-08-29 09:29:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 6:14:54-

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