我正在关注this文档以了解LiveData和ViewModel。 在doc中,ViewModel类具有构造函数,
public class UserModel extends ViewModel {
private MutableLiveData<User> user;
@Inject UserModel(MutableLiveData<User> user) {
this.user = user;
}
public void init() {
if (this.user != null) {
return;
}
this.user = new MutableLiveData<>();
}
public MutableLiveData<User> getUser() {
return user;
}
}
然而,当我运行代码时,我得到了异常:
final UserViewModelviewModel = ViewModelProviders.of(this).get(UserViewModel.class);
引起:java.lang.RuntimeException:无法创建类UserViewModel的实例 引起:java.lang.InstantiationException: java.lang.Class中 没有零参数构造函数
答案 0 :(得分:38)
在使用ViewModel
初始化ViewModelProviders
的子类时,默认情况下它期望您的UserModel
类具有零参数构造函数。
在您的情况下,您的构造函数具有参数MutableLiveData<User> user
解决此问题的一种方法是为UserModel
否则,如果您希望ViewModel类具有非零参数构造函数,则可能必须创建一个自定义ViewModelFactory
类来初始化ViewModel实例,该实例将实现ViewModelProvider.Factory
接口。< / p>
我还没有尝试过这个,但是这里链接到google的优秀样本:github.com/googlesamples/android-architecture-components。 具体来说,请查看此类GithubViewModelFactory.java以获取Java代码,并将此类GithubViewModelFactory.kt签出以获取相应的Kotlin代码
答案 1 :(得分:21)
ViewModelFactory
将为ViewModelModule
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels) {
this.viewModels = viewModels;
}
@Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<ViewModel> viewModelProvider = viewModels.get(modelClass);
if (viewModelProvider == null) {
throw new IllegalArgumentException("model class " + modelClass + " not found");
}
return (T) viewModelProvider.get();
}
}
ViewModelModule
负责将所有ViewModel类绑定到
Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels
@Module
public abstract class ViewModelModule {
@Binds
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory viewModelFactory);
//You are able to declare ViewModelProvider.Factory dependency in another module. For example in ApplicationModule.
@Binds
@IntoMap
@ViewModelKey(UserViewModel.class)
abstract ViewModel userViewModel(UserViewModel userViewModel);
//Others ViewModels
}
ViewModelKey
是一个注释,用于在地图中使用键,看起来像
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
Class<? extends ViewModel> value();
}
现在,您可以创建ViewModel并从图形
中满足所有必需的依赖项public class UserViewModel extends ViewModel {
private UserFacade userFacade;
@Inject
public UserViewModel(UserFacade userFacade) { // UserFacade should be defined in one of dagger modules
this.userFacade = userFacade;
}
}
实例化ViewModel
public class MainActivity extends AppCompatActivity {
@Inject
ViewModelFactory viewModelFactory;
UserViewModel userViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((App) getApplication()).getAppComponent().inject(this);
userViewModel = ViewModelProviders.of(this, viewModelFactory).get(UserViewModel.class);
}
}
不要伪造将ViewModelModule
添加到modules
列表
@Singleton
@Component(modules = {ApplicationModule.class, ViewModelModule.class})
public interface ApplicationComponent {
//
}
答案 2 :(得分:17)
在我使用HILT的情况下,它在具有ViewModel的Fragment上面缺少一个注释: @AndroidEntryPoint
@AndroidEntryPoint
class BestFragment : Fragment() {
....
当然,在ViewModel类中,您还需要使用 HILT需要什么: @ViewModelInject
class BestFragmentViewModel @ViewModelInject constructor(var userManager: UserManager) : ViewModel() {
....
}
答案 3 :(得分:5)
2020年初, Google在androidx生命周期库的版本2.2.0中弃用了ViewModelProviders类。
不再需要使用ViewModelProviders创建ViewModel的实例,您可以将Fragment或Activity实例传递给ViewModelProvider构造函数。
如果您使用如下代码:
val viewModel = ViewModelProviders.of(this).get(CalculatorViewModel::class.java)
您将收到 ViewModelProviders已过时的警告。
为避免使用不赞成使用的库,请进行以下更改。
在build.gradle(模块:app)文件中,使用生命周期的版本2.2.0
组件:
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
也添加
implementation "androidx.activity:activity-ktx:1.1.0"
如果您想改用Fragment中的ViewModel,请使用
implementation "androidx.fragment:fragment-ktx:1.2.2"
fragment-ktx自动包含activity-ktx,因此您不需要 在依赖项中同时指定两者。
您需要在android部分中指定Java 8:
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.kgandroid.calculator"
minSdkVersion 17
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
kotlinOptions {
jvmTarget = "1.8"
}
}
在“片段”或“活动”中,将导入更改为:
导入androidx.activity.viewModels
然后,用于创建ViewModel的代码变为:
val viewModel: CalculatorViewModel by viewModels()
代替
val viewModel =
ViewModelProviders.of(this).get(CalculatorViewModel::class.java)
将viewModel对象用作:
val viewModel:viewModels()的CalculatorViewModel
viewModel.newNumber.observe(this,Observer {stringResult-> newNumber.setText(stringResult)})
其中 newNumer是一个LiveData对象
要共享活动的ViewModel的片段中,您将使用
val viewModel: CalculatorViewModel by activityViewModels()
这等同于在(不推荐使用)中传递Activity实例 ViewModelProviders.of()函数。
答案 4 :(得分:5)
我在使用 @ViewModelInject
时遇到了一些问题,因为它已被 HILT 弃用。
要解决此问题,请更改此代码:
class MainViewModel @ViewModelInject constructor(
val mainRepository: MainRepository
): ViewModel()
与:
@HiltViewModel
class MainViewModel @Inject constructor(
val mainRepository: MainRepository
): ViewModel()
当然,请记住在您的片段或活动(无论您在哪里实例化您的 @AndroidEntryPoint
)上方添加 ViewModel
注释,如下所示:
@AndroidEntryPoint
class UsageFragment : Fragment(R.layout.fragment_usage) {
.
.
.
}
终极提示:
您可以立即查看 HILT 是否正常工作,查看 ViewModel
左侧是否有图标。
这里它不起作用:
这里确实有效:
如果您在更新代码后没有看到它们,请点击 Build -> Rebuild Project
答案 5 :(得分:4)
如果您使用 hilt,您可能忘记使用 @AndroidEntryPoint
注释您的活动或片段答案 6 :(得分:2)
对于遇到此问题的每个人,我都是这样遇到的:
1-在您的ViewModel中,不要创建一个构造函数,只需创建一个带有Context和其他参数的函数
public class UserModel extends ViewModel {
private Context context;
private ..........;
public void init(Context context, ......) {
this.context = context;
this..... = ....;
}
// Your stuff
}
2-在您的活动中:
UserViewModel viewModel = ViewModelProviders.of(this).get(UserViewModel.class);
viewModel.init(this, .....);
// You can use viewModel as you want
3-在您的片段中
UserViewModel viewModel = ViewModelProviders.of(getActivity()).get(UserViewModel.class);
viewModel.init(getContext(), .....);
// You can use viewModel as you want
答案 7 :(得分:2)
2020-07-29 10:13:25
对于lifecycle_version = '2.2.0'
,不推荐使用ViewProviders.of API。这是我的情况:
class MainActivityViewModel(application: Application) : AndroidViewModel(application) {
private var repository: UserRepository
val allUsers: LiveData<List<User>>
......
error:
val userViewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
success:
val factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application)
userViewModel = ViewModelProvider(this,factory).get(MainActivityViewModel::class.java)
通过API application
通过ViewModelProvider.AndroidViewModelFactory.getInstance
答案 8 :(得分:2)
我有同样的错误。我正在使用 Hilt ,就我而言,这是缺少的第二个Hilt编译器依赖项
现在我都拥有:
kapt com.google.dagger:hilt-android-compiler:#version
和
kapt androidx.hilt:hilt-compiler:#version
在我的应用程序级别的build.gradle文件中,它可以正常工作。
当您使用Hilt Android Gradle插件(请参见docs)和 androidx.hilt:hilt-compiler时,需要com.google.dagger:hilt-android-compiler :#version
当您想要Hilt和Jetpack集成时(例如注入Android Jetpack ViewModel(请参阅docs),显然需要答案 9 :(得分:1)
对于刀柄:
简单地为主要活动和片段添加@AndroidEntryPoint
,为视图模型添加@HiltViewModel
示例:
@HiltViewModel
class SplashViewModel @Inject constructor(
@AndroidEntryPoint
class SplashFragment : Fragment() {
private lateinit var b: SplashFragmentBinding
private val vm: SplashViewModel by viewModels()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
答案 10 :(得分:1)
此失败的最常见原因是在 Fragment/Activity 的开头缺少 @AndroidEntryPoint,如下所示:
**@AndroidEntryPoint**
class MyFragment : Fragment {
val viewModel by viewModels<MyViewModel>()
}
同样,您的 ViewModel 应由 HiltViewModel 注释,如下所示:
@HiltViewModel
class MyViewModel@Inject constructor(
private val var1: Type1
) : ViewModel()
答案 11 :(得分:0)
关于accepted answer,如果您使用的是Hilt并且刚刚添加了ViewModel,请不要忘记重建项目。正如很难发现的那样,仅运行项目并不会创建所需的工厂类(应该自动生成)。
重建之前,以下类不存在:
答案 12 :(得分:0)
如果您使用的是 dagger hilt 和 2.31 或更高版本,则不要在视图模型类中使用“ViewModelInject”。 Dagger 提供了使用视图模型的新方法,因此请按照以下说明进行操作。
1:在类的顶部添加@HiltViewModel 2:使用 Inject 而不是 ViewModelInject
@HiltViewModel
class AuthViewModel @Inject constructor(
private val authRepository: AuthRepository,
...
) : ViewModel()
{...}
答案 13 :(得分:0)
如果您使用导航组合并在 NavHost 块内调用屏幕,则刀柄无法注入视图模型。为此,您可以使用这种方式;
NavHost(navHostController, startDestination = "HomeScreen") {
composable("HomeScreen") {
HomeScreen(homeScreenViewModel = hiltViewModel())
}
}
不要忘记为 hiltViewModel()
-> implementation("androidx.hilt:hilt-navigation-compose:1.0.0-alpha02")
答案 14 :(得分:0)
我遇到了同样的问题,通过向我的项目添加导航ui库来解决此问题:
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
答案 15 :(得分:0)
您可以通过viewmodel工厂在viewmodel中传递数据。您也可以签出this example供参考。
class UserViewModelFactory(private val context: Context) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return UserViewModel(context) as T
}
}
class UserViewModel(private val context: Context) : ViewModel() {
private var listData = MutableLiveData<ArrayList<User>>()
init{
val userRepository : UserRepository by lazy {
UserRepository
}
if(context.isInternetAvailable()) {
listData = userRepository.getMutableLiveData(context)
}
}
fun getData() : MutableLiveData<ArrayList<User>>{
return listData
}
}
您可以在活动中调用viewmodel,如下所示。
val userViewModel = ViewModelProviders.of(this,UserViewModelFactory(this)).get(UserViewModel::class.java)
答案 16 :(得分:0)
创建一个不含任何参数的构造函数,即
Default/ No-arg constructor
在viewmodel类中。
就我而言,我忘了生成此构造函数,而在学习时浪费了30分钟-在那之后对我有用。
答案 17 :(得分:0)
我写了一个库,该库应该使实现这一过程更加直接和简洁,不需要多重绑定或工厂样板,同时还可以在运行时进一步参数化ViewModel
:
https://github.com/radutopor/ViewModelFactory
@ViewModelFactory
class UserViewModel(@Provided repository: Repository, userId: Int) : ViewModel() {
val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
}
在视图中:
class UserActivity : AppCompatActivity() {
@Inject
lateinit var userViewModelFactory2: UserViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
.get(UserViewModel::class.java)
viewModel.greeting.observe(this, Observer { greetingText ->
greetingTextView.text = greetingText
})
}
}
答案 18 :(得分:-1)
可以通过从UserModel
扩展AndroidViewModel
来解决该问题,Application
是应用程序上下文感知的ViewModel,并且需要class MyVm(application: Application) : AndroidViewModel(application)
仅参数构造函数。 (documentation)
Ex-(科特林语)
2.0.0-alpha1
这适用于版本com.example.app.App
。
答案 19 :(得分:-1)
如果你在构造函数中有参数:
@inject依赖的DAGGER 2公共构造函数
@Inject
public UserViewModel(UserFacade userFacade)
{
this.userFacade = userFacade;
}
否则dagger 2会向您发送错误“无法实例化viewmodel对象”
答案 20 :(得分:-1)
对于 Koin:
我遇到了这个问题,结果我只是从 AndroidX 导入了 viewModels() 而不是从 Koin 导入了 viewModel()