【问题标题】:Android ViewModel has no zero argument constructorAndroid ViewModel 没有零参数构造函数
【发布时间】:2017-10-26 22:26:01
【问题描述】:

我正在关注this 文档以了解 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.类 没有零参数构造函数

【问题讨论】:

标签: android mvvm android-architecture-components


【解决方案1】:

在使用ViewModelProviders 初始化ViewModel 的子类时,默认情况下它希望您的UserModel 类具有零参数构造函数。 在您的情况下,您的构造函数具有参数MutableLiveData&lt;User&gt; user

解决此问题的一种方法是为您的UserModel 设置一个默认的无参数构造函数。

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

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

【讨论】:

【解决方案2】:

ViewModelFactory 将为我们提供来自 ViewModelModule 的正确 ViewModel

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&lt;Class&lt;? extends ViewModel&gt;, Provider&lt;ViewModel&gt;&gt; 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;
    }
} 

实例化视图模型

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 {
    //
}

【讨论】:

  • 我收到一个错误:[dagger.android.AndroidInjector.inject(T)] java.util.Map&lt;java.lang.Class&lt;? extends android.arch.lifecycle.ViewModel&gt;,javax.inject.Provider&lt;android.arch.lifecycle.ViewModel&gt;&gt; cannot be provided without an @Provides-annotated method.
  • 如果没有看到整个项目,很难说问题出在哪里,我可以假设 1. ViewModel 的子级没有在ViewModelModule 中声明。 2.ViewModelModule 没有添加到Component
  • @LevonPetrosyan 我遇到了同样的问题,创建不带参数的构造函数和@Inject 注释
  • @silentsudo 我的问题是我在 Fragment 中注入了 viewModel,但还是谢谢 :))
  • 我认为这是对上述实现最好的解释:medium.com/@marco_cattaneo/…
【解决方案3】:

如果你在构造函数中有参数,那么:

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

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

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

【讨论】:

  • 为什么人们写 cmets 只是为了评论?他有@Inject,但他的问题已经在上面提到了,这不是你写的。
  • 写个例子就好了,上面的答案对我来说不是很清楚,所以我写了一个我容易理解的,其他人也能理解。
【解决方案4】:

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

Ex-(在 kotlin 中)

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

这适用于版本2.0.0-alpha1

【讨论】:

  • AndroidViewModel 只接受 1 个参数,即 Application。解决这两个 ViewModel 版本的唯一方法是创建一个工厂。
【解决方案5】:

我编写了一个库,该库应该使实现这一目标更加简单明了,不需要多重绑定或工厂样板,同时还能够在运行时进一步参数化 ViewModelhttps://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
        })
    }
}

【讨论】:

    【解决方案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. 在您的 Fragment 或 Activity 中,将导入更改为:

      导入 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.**
    

    【讨论】:

    • 我在此行创建视图模型实例时遇到编译器错误 val viewModel: CalculatorViewModel by viewModels() 我希望 CalculatorViewModel 为 AndroidViewModel 因为我想要上下文
    • 即使您扩展 AndroidViewModel,这也可以。您得到的确切错误是什么?
    • 分类器'CalculatorViewModel'没有伴生对象,必须在这里初始化。
    • 我没有遇到过这样的问题。发布问题并在此处提供链接。我会看看那个。
    【解决方案7】:

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

    Default/ No-arg constructor

    在视图模型类中。

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

    【讨论】:

      【解决方案8】:

      对于每个有这个问题的人,我都是这样遇到的:
      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
      

      【讨论】:

      • 如果我们自己提供对象,那么注入和依赖图的意义何在?
      【解决方案9】:

      您可以通过视图模型工厂在视图模型中传递数据。您也可以查看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
          }
      }
      

      你可以在activity中调用viewmodel,如下所示。

      val userViewModel = ViewModelProviders.of(this,UserViewModelFactory(this)).get(UserViewModel::class.java)
      

      【讨论】:

        【解决方案10】:

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

        implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
        

        【讨论】:

          【解决方案11】:

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

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

          当然,在您的 ViewModel 类中,您还需要使用 Annotate HILT 需要什么:@ViewModelInject

          class BestFragmentViewModel @ViewModelInject constructor(var userManager: UserManager) : ViewModel() {
          ....
          }
          

          【讨论】:

          • 我有一个活动,我的实现和你的一样,仍然抛出上述错误
          • 您是否彻底实施了 Hilt?此链接应该可以帮助您处理这些 Hilt Annotations。 dagger.dev/hilt/quick-start.html
          • 每次都能得到我
          • 完全相同的场景并为我工作。非常感谢。
          • 我在这里创建了一个问题来改进错误消息。请加星! issuetracker.google.com/issues/192762605
          【解决方案12】:

          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


          【讨论】:

            【解决方案13】:

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

            重建前以下类不存在:

            【讨论】:

              【解决方案14】:

              我有同样的错误。我正在使用 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 插件(参见 docs)和 androidx.hilt:hilt-compiler:# 时需要版本 当您想要 Hilt 和 Jetpack 集成时显然需要它,例如注入 Android Jetpack ViewModel(请参阅docs

              【讨论】:

              • 我也错过了kapt androidx.hilt:hilt-compiler:#version。谢谢!
              【解决方案15】:

              如果您使用 hilt,您可能会忘记使用 @AndroidEntryPoint

              注释您的活动或片段

              【讨论】:

                【解决方案16】:

                我对@9​​87654325@ 有一些问题,因为它已被使用 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 -&gt; Rebuild Project

                【讨论】:

                • 在尝试了一个小时的不同替代方案后,只有这对我有用。非常感谢。
                • 这对我有用,但我还想提一下,也许这可能在新版本的 HILT 库中,即“hilt_version = '2.35'”在早期版本中,即“hilt_version = '2.28.3 -alpha'" 我想这不是必需的
                【解决方案17】:

                对于 Koin:

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

                【讨论】:

                  【解决方案18】:

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

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

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

                  @HiltViewModel
                  class MyViewModel@Inject constructor(
                  private val var1: Type1
                  ) : ViewModel()
                  

                  【讨论】:

                    【解决方案19】:

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

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

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

                    【讨论】:

                      【解决方案20】:

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

                      1:在类顶部添加@HiltViewModel 2:使用 Inject 代替 ViewModelInject

                      @HiltViewModel
                      class AuthViewModel @Inject constructor( 
                      private val authRepository: AuthRepository,
                          ...
                      ) : ViewModel() 
                      {...}
                      

                      【讨论】:

                        【解决方案21】:

                        对于刀柄:

                        简单添加@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
                        

                        【讨论】:

                        • 根据最新的库版本,这应该是公认的解决方案
                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-03-11
                        • 2020-09-26
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-03-03
                        • 2019-12-17
                        相关资源
                        最近更新 更多