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;
相关推荐
小羊没烦恼!2 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里2 天前
java中的继承和多态的区别
java
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕2 天前
JSONObject与JSONArray封装数据格式区别
java·json
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
胡写代码2 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖2 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
此时不提桶,更待何时2 天前
01-06-A-JVM排查实战详解
java·jvm