手动依赖注入
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;