81

我正在关注文档以了解 LiveData 和 ViewModel。在文档中, 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没有零参数构造函数

4

21 回答 21

65

在我使用 HILT 的情况下,它在具有 ViewModel 的片段上方缺少一个注释:@AndroidEntryPoint

@AndroidEntryPoint
class BestFragment : Fragment() { 
....

当然,在您的 ViewModel 类中,您还需要使用 HILT 所需的内容进行注释:@ViewModelInject

class BestFragmentViewModel @ViewModelInject constructor(var userManager: UserManager) : ViewModel() {
....
}
于 2020-07-25T00:39:34.340 回答
61

在初始化ViewModelusing的子类时ViewModelProviders,默认情况下它希望您的UserModel类具有零参数构造函数。在您的情况下,您的构造函数具有参数MutableLiveData<User> user

解决此问题的一种方法是为您的UserModel.

否则,如果您希望 ViewModel 类有一个非零参数构造函数,您可能必须创建一个自定义ViewModelFactory类来初始化您的 ViewModel 实例,该实例实现了该ViewModelProvider.Factory接口。

我还没有尝试过,但这里有一个优秀的谷歌示例的链接:github.com/googlesamples/android-architecture-components。具体来说,查看这个类GithubViewModelFactory.java以获得 Java 代码和这个类GithubViewModelFactory.kt以获得相应的 Kotlin 代码。

于 2017-05-26T06:19:22.000 回答
33

ViewModelFactory这将为我们提供正确的 ViewModelViewModelModule

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是在 Map 中用作键的注释,看起来像

@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);

    }
}

并且不要伪造添加ViewModelModulemodules列表中

@Singleton
@Component(modules = {ApplicationModule.class, ViewModelModule.class})
public interface ApplicationComponent {
    //
}
于 2018-03-03T17:22:01.640 回答
13

对于刀柄:

@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
于 2021-07-28T17:01:29.737 回答
9

我有一些问题,@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

于 2021-04-11T14:50:03.280 回答
6

2020 年初, Google在 androidx 生命周期库的 2.2.0 版本中弃用了 ViewModelProviders 类。

不再需要使用 ViewModelProviders 来创建 ViewModel 的实例,您可以将 Fragment 或 Activity 实例传递给 ViewModelProvider 构造函数。

如果您使用如下代码:

val viewModel = ViewModelProviders.of(this).get(CalculatorViewModel::class.java)

您将收到一条警告说ViewModelProviders 已被弃用。

您可以改为:

val viewModel = ViewModelProvider(this).get(CalculatorViewModel::class.java)

或者,要使用委托,请进行以下更改。

  1. 在 build.gradle (Module: 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,因此您无需在依赖项中同时指定两者。

  2. 您需要在 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"
  }
}
  1. 在您的片段或活动中,将导入更改为:

    导入 androidx.activity.viewModels

  2. 然后创建 ViewModel 的代码变为:

    val viewModel: CalculatorViewModel by viewModels()

    代替

    val viewModel = ViewModelProviders.of(this).get(CalculatorViewModel::class.java)

    使用 viewModel 对象:

    val viewModel: CalculatorViewModel by viewModels()

    viewModel.newNumber.observe(this, Observer { stringResult -> newNumber.setText(stringResult) })

其中newNumer 是 LiveData 对象

In a Fragment that you want to share the Activity's ViewModel, you'd use

`val viewModel: CalculatorViewModel by activityViewModels()`

**That's the equivalent of passing the Activity instance in the (deprecated) 
ViewModelProviders.of() function.**
于 2020-03-16T07:02:52.487 回答
5

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)

通过applicationapiViewModelProvider.AndroidViewModelFactory.getInstance


于 2020-07-29T02:15:50.407 回答
4

我有同样的错误。我正在使用Hilt,在我的情况下,它是缺少第二个 hilt 编译器依赖项

现在我有两个:

kapt com.google.dagger:hilt-android-compiler:#version

kapt androidx.hilt:hilt-compiler:#version

在我的应用程序级别 build.gradle 文件中,它可以工作。

com.google.dagger:hilt-android-compiler在您使用 Hilt Android Gradle 插件(请参阅文档)时需要,而androidx.hilt:hilt-compiler:#version 在您想要 Hilt 和 Jetpack 集成时显然需要,例如注入 Android Jetpack ViewModel(参见文档

于 2020-09-27T17:38:56.377 回答
4

如果您使用hilt,您可能会忘记使用@AndroidEntryPoint注释您的活动或片段

于 2021-01-04T13:31:13.607 回答
3

此失败的最常见原因是在 Fragment/Activity 的开头缺少 @AndroidEntryPoint,如下所示:

@AndroidEntryPoint
class MyFragment : Fragment {
val viewModel by viewModels<MyViewModel>()
}

同样,您的 ViewModel 应该由 HiltViewModel 注释,如下所示:

@HiltViewModel
class MyViewModel@Inject constructor(
private val var1: Type1
) : ViewModel()
于 2021-05-31T12:15:01.147 回答
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
于 2020-04-29T16:44:45.400 回答
1

我编写了一个库,它应该使实现这一目标更直接、更简洁,不需要多重绑定或工厂样板,同时还能够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
        })
    }
}
于 2018-11-21T18:36:34.327 回答
1

我有同样的问题,通过将导航 ui 库添加到我的项目来修复它:

implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
于 2020-07-24T10:37:19.627 回答
0

您可以通过 viewmodel 工厂在 viewmodel 中传递数据。您还可以查看此示例以供参考。

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)
于 2020-07-11T11:40:30.693 回答
0

关于接受的答案,如果您使用 Hilt 并且刚刚添加了 ViewModel,请不要忘记重建您的项目。简单地运行项目并不会创建所需的工厂类(应该是自动生成的),这是很难发现的。

重建之前不存在以下类:

在此处输入图像描述

于 2020-08-31T07:25:23.363 回答
0

对于科因:

我有这个问题,原来我只是从 AndroidX 导入 viewModels() 而不是从 Koin 导入 viewModel()

于 2021-04-24T18:41:22.917 回答
0

如果您使用的是 dagger hilt 和 2.31 或更高版本,则不要在视图模型类中使用“ViewModelInject”。Dagger 提供了使用视图模型的新方法,因此请按照以下说明进行操作。

1:在类2之上添加@HiltViewModel:使用Inject而不是ViewModelInject

@HiltViewModel
class AuthViewModel @Inject constructor( 
private val authRepository: AuthRepository,
    ...
) : ViewModel() 
{...}
于 2021-07-26T07:27:47.967 回答
0

如果您在 NavHost 块内使用导航组合并调用屏幕,则刀柄无法注入视图模型。为此,您可以使用这种方式;

    NavHost(navHostController, startDestination = "HomeScreen") {
    composable("HomeScreen") {
        HomeScreen(homeScreenViewModel = hiltViewModel())
    }
}

不要忘记为hiltViewModel()->添加此依赖项implementation("androidx.hilt:hilt-navigation-compose:1.0.0-alpha02")

于 2021-06-11T16:44:08.737 回答
0

创建一个没有任何参数的构造函数,即

Default/ No-arg constructor

在视图模型类中。

在我的例子中,我忘记生成这个构造函数并且在我学习的时候浪费了 30 分钟——之后它对我有用。

于 2020-04-20T11:39:48.607 回答
-2

该问题可以通过扩展应用UserModel程序AndroidViewModel上下文感知 ViewModel 并需要Application仅参数构造函数来解决。(文档)

前-(在 kotlin 中)

class MyVm(application: Application) : AndroidViewModel(application)

这适用于版本2.0.0-alpha1

于 2018-07-01T15:47:35.473 回答
-3

如果您在构造函数中有参数,则:

DAGGER 2 @inject 依赖的公共构造函数

@Inject
public UserViewModel(UserFacade userFacade)
{ 
    this.userFacade = userFacade;
}

否则 dagger 2 将向您发送错误“无法实例化视图模型对象”

于 2018-05-08T10:12:31.053 回答