JetPack——Hilt

手动依赖注入

LoginRetrofitService

复制代码
public class LoginRetrofitService {
}

UserRemoteDataSource

复制代码
public class UserRemoteDataSource {

    private final LoginRetrofitService loginService;

    public UserRemoteDataSource(LoginRetrofitService loginService) {
        this.loginService = loginService;
    }
}

UserLocalDataSource

复制代码
public class UserLocalDataSource {
}

UserRepository

复制代码
public class UserRepository {

    private final UserLocalDataSource localDataSource;
    private final UserRemoteDataSource remoteDataSource;

    public UserRepository(UserLocalDataSource localDataSource,
                          UserRemoteDataSource remoteDataSource) {
        this.localDataSource = localDataSource;
        this.remoteDataSource = remoteDataSource;
    }
}

LoginViewModelFactory

复制代码
public class LoginViewModelFactory implements ViewModelProvider.Factory {

    private final UserRepository userRepository;

    public LoginViewModelFactory(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @NonNull
    @Override
    @SuppressWarnings("unchecked")
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        return (T) new LoginViewModel(userRepository);
    }
}

LoginViewModel

复制代码
public class LoginViewModel extends ViewModel {

    private final UserRepository userRepository;

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

ManualLoginActivity

想要获取LoginViewModel,需要根据依赖关系依次创建LoginRetrofitService、UserLocalDataSource、UserRemoteDataSource、UserRepository、LoginViewModelFactory

复制代码
public class ManualLoginActivity extends AppCompatActivity {

    private static final String TAG = ManualLoginActivity.class.getSimpleName();

    private LoginViewModel loginViewModel;

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

        LoginRetrofitService loginService = new LoginRetrofitService();
        UserLocalDataSource localDataSource = new UserLocalDataSource();
        UserRemoteDataSource remoteDataSource = new UserRemoteDataSource(loginService);
        UserRepository userRepository = new UserRepository(localDataSource, remoteDataSource);
        LoginViewModelFactory factory = new LoginViewModelFactory(userRepository);
        loginViewModel = new ViewModelProvider(this, factory).get(LoginViewModel.class);

        String msg = "【手动DI】依赖注入成功,ViewModel = " + loginViewModel;
        Log.d(TAG, msg);
        showText(msg);
    }

    private void showText(String text) {
        TextView tv = new TextView(this);
        tv.setText(text);
        tv.setGravity(Gravity.CENTER);
        tv.setTextColor(Color.BLACK);
        tv.setPadding(32, 200, 32, 32);
        setContentView(tv);
    }
}

Hilt

实现依赖项注入可为您带来以下优势:

  • 重用代码
  • 易于重构
  • 易于测试

Hilt 是 Android 的依赖项注入库,可减少在项目中执行手动依赖项注入的样板代码

依赖

libs.version.toml

复制代码
[versions]
......
ksp = "2.0.20-1.0.24"
hilt = "2.57.1"

[libraries]
......
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" }

[plugins]
......
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }

build.gradle(根目录)

复制代码
plugins {
    ......
    alias(libs.plugins.ksp) apply false
    alias(libs.plugins.hilt) apply false
}

build.gradle(项目目录)

复制代码
android {
	......
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = '17'
    }
}
plugins {
	......
    alias(libs.plugins.ksp)
    alias(libs.plugins.hilt)
}
dependencies {
	......
    implementation libs.hilt.android
    ksp libs.hilt.compiler
}

Hilt 依赖注入

MyApplication

复制代码
@HiltAndroidApp
public class MyApplication extends Application {

    private static MyApplication instance;

    @Override
    public void onCreate() {
        super.onCreate();
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

LoginRetrofitService

复制代码
public class LoginRetrofitService {

    @Inject
    public LoginRetrofitService() {
    }
}

UserRemoteDataSource

复制代码
public class UserRemoteDataSource {

    private final LoginRetrofitService loginService;

    @Inject
    public UserRemoteDataSource(LoginRetrofitService loginService) {
        this.loginService = loginService;
    }
}

UserLocalDataSource

复制代码
public class UserLocalDataSource {

    @Inject
    public UserLocalDataSource() {
    }
}

UserRepository

@Singleton 全 App 共享一个

复制代码
@Singleton
public class UserRepository {

    private final UserLocalDataSource localDataSource;
    private final UserRemoteDataSource remoteDataSource;

    @Inject
    public UserRepository(UserLocalDataSource localDataSource,
                          UserRemoteDataSource remoteDataSource) {
        this.localDataSource = localDataSource;
        this.remoteDataSource = remoteDataSource;
    }
}

LoginViewModel

@HiltViewModel + 构造函数 @Inject → 依赖自动装配,不再需要手写 Factory。

复制代码
@HiltViewModel
public class LoginViewModel extends ViewModel {

    private final UserRepository userRepository;

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

HiltLoginActivity

为带有 @AndroidEntryPoint 注解的其他 Android 类提供依赖项,省去new的过程

复制代码
@AndroidEntryPoint
public class HiltLoginActivity extends AppCompatActivity {

    private static final String TAG = HiltLoginActivity.class.getSimpleName();

    private LoginViewModel loginViewModel;

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

        // 一行搞定:Hilt 已经把 UserRepository 及其下层依赖全部装配好
        loginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);

        String msg = "【Hilt】依赖注入成功,ViewModel = " + loginViewModel;
        Log.d(TAG, msg);
        showText(msg);
    }

    private void showText(String text) {
        TextView tv = new TextView(this);
        tv.setText(text);
        tv.setGravity(Gravity.CENTER);
        tv.setTextColor(Color.BLACK);
        tv.setPadding(32, 200, 32, 32);
        setContentView(tv);
    }
}

@Module和@InstallIn

  • @Module 注解的类向 Hilt 提供有关如何创建无法通过构造函数注入提供的类型(例如接口或第三方类)的实例
  • @InstallIn 告知 Hilt 每个模块将用在或安装在哪个 Android 类中

@Bind 注入接口

复制代码
public interface DemoInterface {
    String demo();
}

public class DemoImpl implements DemoInterface {

    @Inject
    public DemoImpl() {
    }

    @Override
    public String demo() {
        return "DemoImpl";
    }
}

存在接口和具体实现,创建一个带有 @Binds 注解的抽象函数实现接口的依赖注入

  • 返回值为接口

  • 参数为实现

    @Module
    @InstallIn(SingletonComponent.class)
    public abstract class DemoModule {
    @Binds
    public abstract DemoInterface bind(DemoImpl impl);
    }

    @AndroidEntryPoint
    public class HiltModuleActivity extends AppCompatActivity {

    复制代码
      private static final String TAG = HiltModuleActivity.class.getSimpleName();
      @Inject
      DemoInterface mDemo;   // 业务用接口
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
    
          LinearLayout root = new LinearLayout(this);
          root.setOrientation(LinearLayout.VERTICAL);
          root.setPadding(48, 200, 48, 48);
    
          TextView tv = new TextView(this);
          tv.setGravity(Gravity.CENTER);
          tv.setTextColor(Color.BLACK);
          root.addView(tv);
    
          setContentView(root);
    
          String msg = "当前实现:" + mDemo.demo();
          Log.d(TAG, msg);
          tv.setText(msg);
      }

    }

@Provides 注入外部库

复制代码
@Module
@InstallIn(SingletonComponent.class)
public class DemoModule {

    @Provides
    public Gson provideGson() {
        return new GsonBuilder().create();
    }
}

@AndroidEntryPoint
public class HiltModuleActivity extends AppCompatActivity {

    @Inject
    Gson gson;   // @Provides 提供的实例

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

        TextView tv = new TextView(this);
        tv.setText("Gson实例:" + gson.toJson("hello @Provides"));
        setContentView(tv);
    }
}

@Qualifier和@Retention

  • @Qualifier 为同一类型定义多个绑定
  • @Retention 设置注解声明周期

如下生成@PrettyGson注解,用于Gson实例对象的依赖注入

复制代码
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PrettyGson {
}

@Module
@InstallIn(SingletonComponent.class)
public class DemoModule {

    // 默认绑定:压缩输出。
    @Provides
    public Gson provideGson() {
        return new GsonBuilder().create();
    }

    // 同类型的第二个绑定,靠 @PrettyGson 限定符区分,避免和上面的冲突。
    @Provides
    @PrettyGson
    public Gson providePrettyGson() {
        return new GsonBuilder().setPrettyPrinting().create();
    }
}

@AndroidEntryPoint
public class HiltModuleActivity extends AppCompatActivity {

    @Inject
    Gson gson;            // 默认绑定

    @Inject
    @PrettyGson
    Gson prettyGson;      // 限定符绑定

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

        // 用带字段的对象序列化,才能看出 setPrettyPrinting() 的换行缩进效果;
        // 序列化单个字符串没有"结构",两种输出会一样。
        Map<String, String> data = new HashMap<>();
        data.put("name", "Kiro");
        data.put("action", "@Provides demo");

        String compact = gson.toJson(data);
        String pretty = prettyGson.toJson(data);

        TextView tv = new TextView(this);
        tv.setText("默认Gson: " + compact
                + "\n\nPrettyGson:\n" + pretty);
        setContentView(tv);
    }
}

自带的预定义限定符

如下注入ApplicationContext和ActivityContext

复制代码
@Inject
@ApplicationContext
Context appContext;

@Inject
@ActivityContext
Context activityContext;
相关推荐
君顾11 小时前
代驾跑腿业务系统实战开发指南
java·开发语言·代驾跑腿
雨声不在1 小时前
docker-android 重启后镜像起不来
android·docker·容器
阿pin2 小时前
Android随笔-Framework
android·framework
夏幻灵2 小时前
从语法坑点到 JS 内存底层:Vue 3 watch 侦听器深度全解
开发语言·javascript·vue.js
达达尼昂2 小时前
Flutter AI Harness 如何让 Agent 参与软件开发全流程
android·人工智能·后端
NGINX开源社区2 小时前
NGINX Ingress Controller 5.5:安全性与性能提升,迁移更轻松
java·服务器·nginx
sRect2 小时前
用ESP32-S3开发板开发一个接收消息的BB机
android·serverless·嵌入式
k4m7v2pz2 小时前
Rust 长跑守护进程日志治理:切分、时区与结构化
开发语言·后端·rust·日志系统·日志轮转·ndjson
FfHUCisI2 小时前
Golang RESTful API 设计原则
开发语言·golang·restful
breeze jiang2 小时前
ESLint flat config 配置实战:五大字段、规则严重级别与 --fix 能力边界详解
开发语言·前端·javascript