You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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播放视频:

  1. 在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)
    }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:44:52