Android ViewModel没有零参数构造函数

Pra*_*ina 46 android mvvm android-architecture-components

我正在关注文档以了解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;
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我运行代码时,我得到异常:

final UserViewModelviewModel = ViewModelProviders.of(this).get(UserViewModel.class);
Run Code Online (Sandbox Code Playgroud)

引起:java.lang.RuntimeException:无法创建类UserViewModel的实例引起:java.lang.InstantiationException:java.lang.Class没有零参数构造函数

Sha*_*med 44

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

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

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

我还没有尝试过这个,但是这里有链接到google的优秀样本:github.com/googlesamples/android-architecture-components.具体来说,结帐此类GithubViewModelFactory.java用于Java代码,此类GithubViewModelFactory.kt用于相应的Kotlin代码


Dim*_*sus 35

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

@AndroidEntryPoint
class BestFragment : Fragment() { 
....
Run Code Online (Sandbox Code Playgroud)

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

class BestFragmentViewModel @ViewModelInject constructor(var userManager: UserManager) : ViewModel() {
....
}
Run Code Online (Sandbox Code Playgroud)

  • 我有一个活动,我的实现与你的相同,但它仍然抛出上述错误 (4认同)
  • 每次都让我 (4认同)

For*_*ran 32

对于刀柄:

简单添加@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
Run Code Online (Sandbox Code Playgroud)

  • 根据最新的库版本,这应该是可接受的解决方案 (2认同)

yoA*_*ex5 28

ViewModelFactory 这将为我们提供一个正确的ViewModel 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

ViewModelKey 是一个注释,用于在地图中使用作为键,看起来像

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
@interface ViewModelKey {
    Class<? extends ViewModel> value();
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以创建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;
    }
} 
Run Code Online (Sandbox Code Playgroud)

实例化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);

    }
}
Run Code Online (Sandbox Code Playgroud)

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

@Singleton
@Component(modules = {ApplicationModule.class, ViewModelModule.class})
public interface ApplicationComponent {
    //
}
Run Code Online (Sandbox Code Playgroud)


kga*_*oid 6

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

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

如果您使用以下代码:

val viewModel = ViewModelProviders.of(this).get(CalculatorViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)

您会收到一条警告,指出ViewModelProviders 已被弃用。

你可以这样做:

val viewModel = ViewModelProvider(this).get(CalculatorViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)

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

  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"
  }
}
Run Code Online (Sandbox Code Playgroud)
  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.**
Run Code Online (Sandbox Code Playgroud)


Mat*_*tti 6

我遇到了一些问题,@ViewModelInject因为它已被 HILT 弃用。要解决此问题,请更改此代码:

class MainViewModel @ViewModelInject constructor(
    val mainRepository: MainRepository
): ViewModel()
Run Code Online (Sandbox Code Playgroud)

和:

@HiltViewModel
class MainViewModel @Inject constructor(
    val mainRepository: MainRepository
): ViewModel()
Run Code Online (Sandbox Code Playgroud)

当然,请记住@AndroidEntryPoint在您的片段或活动(无论您在哪里实例化ViewModel)上方添加注释,如下所示:

@AndroidEntryPoint
class UsageFragment : Fragment(R.layout.fragment_usage) {
   .
   .
   .
}
Run Code Online (Sandbox Code Playgroud)

终极提示:

您可以立即查看 HILT 是否正常工作,查看ViewModel.

在这里它不起作用

在此处输入图片说明

在这里它确实有效

在此处输入图片说明

如果您在更新代码后没有看到它们,请单击 Build -> Rebuild Project


jav*_*kam 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)
Run Code Online (Sandbox Code Playgroud)

通过application由APIViewModelProvider.AndroidViewModelFactory.getInstance



小智 5

我有同样的错误。我正在使用Hilt,在我的例子中,它缺少第二个 hilt 编译器依赖项

现在我两者都有:

kapt com.google.dagger:hilt-android-compiler:#version
Run Code Online (Sandbox Code Playgroud)

kapt androidx.hilt:hilt-compiler:#version
Run Code Online (Sandbox Code Playgroud)

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

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