Android Kotlin中无法创建VideoViewModel实例问题求助
问题:无法创建ViewModel实例
我是MVVM架构新手,使用Android Studio Iguana开发,尝试多种方案后仍遇到异常:无法创建ViewModel实例。以下是相关代码、依赖配置及报错信息:
VideoActivity代码
class VideoActivity : AppCompatActivity() { private lateinit var viewModel: VideoViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProvider(this).get(VideoViewModel::class.java) val videos= listOf( Video(id = 1, url = "url1.mp4"), Video(id = 1, url = "url2.mp4"), Video(id = 1, url = "url3.mp4"), Video(id = 1, url = "url4.mp4"), Video(id = 1, url = "url5.mp4"), ) viewModel.downloadVideos(videos) viewModel.videos.observe(this) { downloadedVideos -> if (downloadedVideos.isNotEmpty()) { viewModel.playNextVideo() } } } }
VideoRepository及VideoViewModel代码
class VideoRepository(private val context: Context) { suspend fun downloadVideos(videos: List<Video>): List<Video> { val downloadedVideos = mutableListOf<Video>() for (video in videos) { val filePath = downloadVideo(video.url) if (filePath.isNotBlank()) { video.downloadStatus = DownloadStatus.DOWNLOADED video.filePath = filePath downloadedVideos.add(video) } } return downloadedVideos } private suspend fun downloadVideo(videoUrl: String): String = withContext(Dispatchers.IO) { try { val url = URL(videoUrl) val connection = url.openConnection() as HttpURLConnection connection.connect() val inputStream: InputStream = connection.inputStream val file = File(context.getExternalFilesDir(Environment.DIRECTORY_MOVIES), "video_${System.currentTimeMillis()}.mp4") val outputStream = FileOutputStream(file) val buffer = ByteArray(1024) var bytesRead: Int while (inputStream.read(buffer).also { bytesRead = it } != -1) { outputStream.write(buffer, 0, bytesRead) } outputStream.flush() outputStream.close() inputStream.close() file.absolutePath } catch (e: Exception) { e.printStackTrace() "" } } fun playVideo(video: Video) { val videoView = (context as VideoActivity).findViewById<VideoView>(R.id.video_view) videoView.setVideoURI(Uri.parse(video.filePath)) videoView.start() } } class VideoViewModel(var repository: VideoRepository) : ViewModel(){ private val _videos = MutableLiveData<List<Video>>() val videos:LiveData<List<Video>> = _videos private lateinit var videoView: VideoView private var currentVideoIndex = 0 fun downloadVideos(videos: List<Video>){ viewModelScope.launch { val downloadVideos = repository.downloadVideos(videos) _videos.postValue(downloadVideos) } } fun playNextVideo(){ currentVideoIndex = (currentVideoIndex + 1) % (videos.value?.size ?: 1) val video = videos.value?.get(currentVideoIndex) video?.let{ repository.playVideo(it) } } }
App Gradle依赖
implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.ui) implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.3.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0") implementation(libs.androidx.appcompat)
报错信息
Process: com.android, PID: 4717 java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android/com.android.main.VideoActivity}: java.lang.RuntimeException: Cannot create an instance of class com.android.main.VideoViewModel at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3825) ... Caused by: java.lang.RuntimeException: Cannot create an instance of class com.android.main.VideoViewModel at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:201) ... Caused by: java.lang.NoSuchMethodException: com.android.adbeets.main.VideoViewModel.<init> [] at java.lang.Class.getConstructor0(Class.java:3325) ...
解决方案
核心问题
报错里的NoSuchMethodException明确说明:ViewModelProvider找不到VideoViewModel的无参构造函数。默认的ViewModelProvider只能实例化无参或者继承AndroidViewModel(带Context参数)的ViewModel,而你的VideoViewModel需要传入VideoRepository参数,必须自定义ViewModelFactory来实现实例化。
另外,VideoRepository里强制将Context转为VideoActivity是不良设计,会导致耦合过高,需要优化。
步骤1:自定义ViewModelFactory
创建一个工厂类,负责创建带参数的VideoViewModel:
class VideoViewModelFactory(private val repository: VideoRepository) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(VideoViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return VideoViewModel(repository) as T } throw IllegalArgumentException("未知的ViewModel类") } }
步骤2:在Activity中使用Factory创建ViewModel
修改VideoActivity的ViewModel初始化代码:
class VideoActivity : AppCompatActivity() { private lateinit var viewModel: VideoViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 先创建Repository实例,再通过Factory创建ViewModel val repository = VideoRepository(this) viewModel = ViewModelProvider(this, VideoViewModelFactory(repository)).get(VideoViewModel::class.java) // ...剩余代码不变 } }
步骤3:优化Repository的耦合问题
去掉VideoRepository中强制转换Context的代码,改用接口回调通知Activity播放视频:
- 在
VideoRepository中定义回调接口:
interface VideoPlayCallback { fun onPlayVideo(video: Video) } class VideoRepository( private val context: Context, private val playCallback: VideoPlayCallback ) { // ...原有downloadVideos和downloadVideo代码不变 // 修改playVideo方法,通过回调通知Activity fun playVideo(video: Video) { playCallback.onPlayVideo(video) } }
- 在
VideoActivity中实现这个接口,并传递给Repository:
class VideoActivity : AppCompatActivity(), VideoRepository.VideoPlayCallback { private lateinit var viewModel: VideoViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 传递this作为回调参数 val repository = VideoRepository(this, this) viewModel = ViewModelProvider(this, VideoViewModelFactory(repository)).get(VideoViewModel::class.java) // ...剩余代码不变 } // 实现回调方法,处理视频播放逻辑 override fun onPlayVideo(video: Video) { val videoView = findViewById<VideoView>(R.id.video_view) videoView.setVideoURI(Uri.parse(video.filePath)) videoView.start() } }
步骤4:更新依赖版本(可选但推荐)
你的lifecycle和协程版本较旧,建议更新到稳定新版本,避免潜在问题:
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
内容的提问来源于stack exchange,提问作者Naveen
相关产品推荐
相关产品推荐

