深入研究 Google 的 DI 框架 Dagger2 以决定使用 DI Framewor 在中型应用程序中使用它,我还注意到了Hilt
所以根据它的概述描述:
Hilt 的工作原理是通过代码为您生成 Dagger 设置代码。这带走了使用 Dagger 的大部分样板,真正留下了定义如何创建对象以及在哪里注入它们的方面。Hilt 将生成 Dagger 组件和代码以自动为您注入您的 Android 类(如活动和片段)。
Hilt 根据您的可传递类路径生成一组标准的 Android Dagger 组件。这需要使用 Hilt 注释标记您的 Dagger 模块,以告诉 Hilt 它们应该进入哪个组件。在 Android 框架类中获取对象是通过使用另一个 Hilt 注释来完成的,该注释会将 Dagger 注入代码生成到您将扩展的基类中。对于 Gradle 用户,扩展这个类是通过底层的字节码转换完成的。
1.它既不取代Dagger2,也不是它的继任者?
2.刀柄是Dagger2的附加功能,从而简化了Dagger2的使用?
3. 除了还在Alpha中,还有什么缺点吗?
运行单元测试代码时出现以下错误。
Caused by: java.lang.IllegalStateException: Hilt Fragments must be attached to an @AndroidEntryPoint Activity. Found: class androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity
at dagger.hilt.internal.Preconditions.checkState(Preconditions.java:83)
at dagger.hilt.android.internal.managers.FragmentComponentManager.createComponent(FragmentComponentManager.java:75)
at dagger.hilt.android.internal.managers.FragmentComponentManager.generatedComponent(FragmentComponentManager.java:63)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.generatedComponent(Hilt_HomePage.java:70)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.inject(Hilt_HomePage.java:89)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.initializeComponentContext(Hilt_HomePage.java:53)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.onAttach(Hilt_HomePage.java:45)
at androidx.fragment.app.Fragment.onAttach(Fragment.java:1602)
at com.zhixin.wedeep.homepage.ui.Hilt_HomePage.onAttach(Hilt_HomePage.java:35)
at com.zhixin.wedeep.homepage.ui.HomePage.onAttach(HomePage.kt:281)
Run Code Online (Sandbox Code Playgroud)
这是我的测试代码。
@HiltAndroidTest
@UninstallModules(HomePageDataModule::class)
@RunWith(AndroidJUnit4::class)
@LargeTest
class TestHomePageFragment {
private val c = Composition("cyrus", "background", "description", "downloadUrl", "1000", "url", "1", true, "100", 100, "100", "test", "title", "1", "100", "cover", ArrayList(), "ONCE", null)
@Inject
lateinit var cpd: CompositionDao
@get:Rule
var hiltRule = HiltAndroidRule(this)
@Before
fun init() …
Run Code Online (Sandbox Code Playgroud) 我正在开发一个 Android 钢琴“测验”应用程序 - 用户点击钢琴键,然后单击黄色的“检查”按钮提交答案进行评估,并查看在钢琴上绘制的正确答案。主要QuizActivity
有这样的布局:
屏幕的上部包含几个控件(文本、提交按钮等)。屏幕的下部由一个自定义PianoView
组件占据,用于处理钢琴键盘的绘制。
根据MVVM原则,PianoView
应该有自己的PianoViewModel
,将其状态(即当前按下的键,突出显示的键等)存储在KeysStateRepository
. 封闭的QuizActivity
也应该有一个QuizActivityViewModel
, 来处理各种控制(提交答案,跳过问题......)。将QuizActivityViewModel
需要能够从查询中选择键PianoView
(或者更确切地说,从它的KeysStateRepository
),他们提交领域层进行评估,然后将结果发回PianoView
的可视化。
换言之,所述QuizActivity
的ViewModel
应该拥有/是的母体PianoView
的ViewModel
以促进通信和数据共享。
如何建模这种父子关系以在 ViewModel 之间进行通信?
AFAIK aViewModel
不能依赖于另一个ViewModel
(我会通过什么ViewModelStoreOwner
来获得ViewModel
另一个中的 a Viewmodel
?)。我认为至少用Dagger-Hilt是不可能实现的。
想到了解决此问题的三种解决方案,但都无法使用:
Android 开发文档建议使用shared ViewModel
来促进两个 Fragment / Views 之间的数据共享。但是,这不适合我的用例。的PianoView
(或它的视图模型)应该是其状态的具有唯一所有者Repository …
我喜欢将我的 Android 应用程序中使用的第三方库的数量保持在绝对最低限度。
我开始使用Dagger2
,然后切换到Koin
.
Koin
就是这么大的进步Dagger2
。
Koin
具有内置的 ViewModel 支持,不需要任何“额外”的工作人员。
Koin
允许您以最小的努力在任何地方注入任何东西,它非常棒。
在 Android Hilt 公告中,我完成了一个峰值来评估它,因为它会减少我对 3rd 方库的依赖。
完成我的尖峰努力后,我不明白为什么有人会使用 Hilt。
例如:
为了Koin
注入一个Worker
我有工作器实现KoinComponent
,为了Hilt
注入一个工作器,我需要禁用默认WorkerManager
初始化,并使用两个注释@WorkerInject
& @Assisted
。
我错过了什么吗?
我目前正在开发一个大项目,其中有一个 ViewModelA 使用 MediatorLiveData 来观察其他 LiveData 源。我想让这个 ViewModelA 观察来自 ViewModelB 的数据。
解决此问题的一种方法是让 Fragment 使用视图模型,并在 ViewModelB 数据更改时更新 ViewModelA。
@AndroidEntryPoint
class FragmentA: Fragment() {
//ViewModels
private val viewModelA: ViewModelA by viewModels()
private val viewModelB: ViewModelB by viewModels()
onViewCreated... {
viewModelA.someFunction().observe{
viewModelB.someLiveData.value = it
}
}
}
Run Code Online (Sandbox Code Playgroud)
不过,我想出了另一个解决方案,使用 Hilt 将 ViewModelB 注入 ViewModelA 的构造函数中。
class ViewModelA @ViewModelInject constructor(
private val viewModelB: ViewModelB
) : ViewModel() {}
Run Code Online (Sandbox Code Playgroud)
它目前有效,但我认为这不是一个好的做法。我在网上找不到关于此事的太多信息。这会引起任何问题吗?
我是 Hilt/Dagger 的新手,我还没有找到注入伴随对象的好例子。下面是我的单例和数据类。我正在尝试在 create() 函数中使用 TargetNumber 管理器。我正在寻找语法帮助和原因解释。
@InstallIn(SingletonComponent::class)
@Module
data class TargetNumberManager(
val maxNumber: Int = 3,
val prefix: String = "ZZ"
) : ITargetNumberManager {
private var currentNumber = 0
@Singleton
@Provides
override fun getNextTargetNumber(): String {
val targetNumberBuilder: StringBuilder = java.lang.StringBuilder()
targetNumberBuilder.append(prefix)
val lengthOfNumber = currentNumber.toString().length
for (i in lengthOfNumber..maxNumber step 1) {
targetNumberBuilder.append(0)
}
targetNumberBuilder.append(currentNumber++)
return targetNumberBuilder.toString()
}
}
Run Code Online (Sandbox Code Playgroud)
// 数据类
data class Target(
@PrimaryKey
val targetNumber: String,
val targetType: TargetType?,
val numOfElement: Int?,
val location: Coordinate? …
Run Code Online (Sandbox Code Playgroud) 在我的项目中,compose viewModel() 方法在我使用刀柄时给出错误“没有零参数构造函数”。
@Composable
fun HomeScreen(homeViewModel: HomeViewModel = viewModel()) {
...
}
@HiltViewModel
class HomeViewModel @Inject constructor(private val repository: Repository) :
ViewModel() {
...
}
@Module
@InstallIn(ViewModelComponent::class)
abstract class DataModule {
@Binds
abstract fun bindRepository(
fakePuppyRepository: RepositoryImpl
): Repository
}
Run Code Online (Sandbox Code Playgroud)
这些是我的依赖项
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0'
implementation 'androidx.activity:activity-compose:1.3.0-alpha03'
implementation "androidx.navigation:navigation-compose:1.0.0-alpha08"
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0'
implementation 'androidx.compose.runtime:runtime-livedata:1.0.0-beta01'
implementation "com.google.dagger:hilt-android:$hilt_version"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
kapt "com.google.dagger:hilt-compiler:$hilt_version"
kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha03'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
Run Code Online (Sandbox Code Playgroud) 在 Android 项目中,有一个外观作为单例实现。我认为使用 HILT SingletonComponent 将其转换为 DI 是一个更好的主意。
@Module
@InstallIn(SingletonComponent::class)
object MyManagerModule {
@Provides
fun provide(@ApplicationContext context: Context): MyManager {
return MyManager(context)
}
}
class MyManager @Inject constructor(@ApplicationContext private val ctx: Context){
//
}
Run Code Online (Sandbox Code Playgroud)
从几个调用者那里,我使用 HILT 字段注入获取了上述 MyManager 的实例,即
@AndroidEntryPoint
class MyCallerFragment : Fragment() {
@Inject lateinit var myManager: MyManager
// ...
Run Code Online (Sandbox Code Playgroud)
在调试器中,我观察到 DI 实例实际上不是同一个实例(假设这些片段处于相同的活动生命周期中)。我想我一定是误解了 Hilt DI :-( 如果你看到我的盲点,我很想听听任何解释。
我想使用 Hilt 在运行时为 ViewModel 提供一些依赖项。我已遵循 d.android.com this指导的解决方案。
@HiltViewModel
public class ViewViewModel extends ViewModel {
...
@AssistedFactory
public interface ViewViewModelFactory {
ViewViewModel create(@Assisted int version);
}
@AssistedInject
public ViewViewModel(ProfileRepository repository, @Assisted int version) {
mProfileRepository = repository;
}
}
@AndroidEntryPoint
public class CarFragment extends Fragment {
@Inject
ViewViewModel.ViewViewModelFactory mViewViewModelFactory;
...
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
ViewViewModel viewModel = mViewViewModelFactory.create(21);
}
}
Run Code Online (Sandbox Code Playgroud)
但是,仍然出现构建错误ViewModel constructor should be annotated with @Inject instead of @AssistedInject.
我使用的是 hilt …
我在我的项目中使用 HILT 设置了 DI。现在我必须集成 FCM 推送通知,因此我必须向 Firebase 消息传递服务类提供存储库实例,以便在调用新令牌时将新的 fcm 令牌更新到服务器(从服务类执行 api 调用)。我该如何做到这一点以及最佳实践是什么?
应用程序模块
@Module
@InstallIn(ApplicationComponent::class)
class ApplicationModule {
@Provides
@Singleton
fun provideServiceTokenAuthenticator(sharedPreferences: SharedPreferences,
@ApplicationContext appContext: Context,chatManager: ChatManager): ServiceTokenAuthenticator =
ServiceTokenAuthenticator(sharedPreferences, appContext,chatManager)
@Provides
@Singleton
fun provideApiService(okHttpClient: OkHttpClient): ApiService {
return Retrofit.Builder().baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()).client(okHttpClient).build()
.create(ApiService::class.java)
}
@Provides
@Singleton
fun provideOkHttpClient(serviceTokenAuthenticator: ServiceTokenAuthenticator,sharedPreferences: SharedPreferences): OkHttpClient {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.HEADERS
loggingInterceptor.level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
return OkHttpClient.Builder().connectTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES).writeTimeout(2, TimeUnit.MINUTES)
.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): …
Run Code Online (Sandbox Code Playgroud)