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

Android应用点击保存按钮崩溃:ViewModel构造相关InvocationTargetException问题求助

Android应用点击保存按钮崩溃:ViewModel构造相关InvocationTargetException问题求助

大家好,我现在在用Kotlin开发一款带底部导航的Android应用,其中ContactFragment模块需要读取系统联系人,用户通过AutoCompleteTextView选择联系人后,点击按钮就能把该联系人存入Room数据库作为紧急联系人。目前系统联系人加载和显示都正常,但选好联系人点击保存按钮时,应用直接崩溃,报错信息如下:

AndroidRuntime: java.lang.reflect.InvocationTargetException
    at java.lang.reflect.Constructor.newInstance0(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
    at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:314)
    at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:306)

我没有使用任何依赖注入框架,下面是我的相关代码,麻烦大家帮忙看看问题出在哪:


相关代码

ContactsViewModel类

import android.app.Application
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import com.example.sentryone.ContactDatabase
import com.example.sentryone.EmergencyContact
import kotlinx.coroutines.launch

class ContactsViewModel (application: Application) : AndroidViewModel(application) {
    private val dao = ContactDatabase.getDatabase(application).contactDao()

    fun insert(contact: EmergencyContact) = viewModelScope.launch {
        Log.d("TAGviewmodel", "insert: in contact viewmodel the issue must be here ")
        dao.insert(contact)
        Log.d("TAGvieemodel", "insert: the num is inserted in db ")
    }

    val allContacts: LiveData<List<EmergencyContact>> = dao.getAll()
}

EmergencyContactDao接口

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query

@Dao
interface EmergencyContactDao {
    @Insert suspend fun insert(contact: EmergencyContact)
    @Query("SELECT * FROM emergency_contacts")
    fun getAll(): LiveData<List<EmergencyContact>>
}

ContactDatabase类

import android.content.Context
import android.util.Log
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [EmergencyContact::class], version = 1)
abstract class ContactDatabase : RoomDatabase(){
    abstract fun contactDao(): EmergencyContactDao

    companion object {
        @Volatile private var INSTANCE : ContactDatabase? = null

        fun getDatabase(context: Context): ContactDatabase {
            Log.d("TAG", "getDatabase: databse is about to create")
            return INSTANCE ?: synchronized(this) {
                Log.d("TAG", "getDatabase: in the process of making db ")
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    ContactDatabase::class.java,
                    "emergency_db"
                )
                    .build()
                INSTANCE = instance
                instance
            }
            Log.d("TAG", "getDatabase: db created ")
        }
    }
}

ContactFragment类

import android.content.pm.PackageManager
import android.os.Bundle
import android.provider.ContactsContract
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.example.sentryone.EmergencyContact
import com.example.sentryone.databinding.FragmentContactBinding
import com.example.sentryone.viewModels.ContactsViewModel
import android.Manifest
import android.util.Log
import androidx.fragment.app.viewModels

class ContactFragment : Fragment() {
    private var _binding : FragmentContactBinding? = null
    private val binding get() = _binding!!
    private val viewModel: ContactsViewModel by viewModels()
    private lateinit var contactSuggestions: List<Pair<String, String>> // name, phone

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding= FragmentContactBinding.inflate(inflater,container,false)
        val view = binding.root
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        checkContactPermission()

        binding.btnAddContact.setOnClickListener {
            // Check if contactSuggestions has been initialized before using it
            if (!::contactSuggestions.isInitialized) {
                Toast.makeText(requireContext(), "Contacts not loaded yet. Please try again.", Toast.LENGTH_SHORT).show()
                return@setOnClickListener
            }

            val entered = binding.contactAutoComplete.text.toString()
            Log.d("TAG", "onViewCreated: in contact frag num is entered")
            val match = contactSuggestions.find { it.second.contains(entered) }
            
            if (match != null) {
                val emergencyContact = EmergencyContact(name = match.first, phone = match.second)
                viewModel.insert(emergencyContact)
                Toast.makeText(requireContext(), "联系人已添加", Toast.LENGTH_SHORT).show()
                binding.contactAutoComplete.text.clear()
            } else {
                Toast.makeText(requireContext(), "未找到匹配的联系人", Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun checkContactPermission() {
        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(arrayOf(Manifest.permission.READ_CONTACTS), 100)
        } else {
            loadContacts()
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 100 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            loadContacts()
        } else {
            Toast.makeText(requireContext(), "需要联系人权限才能使用此功能", Toast.LENGTH_SHORT).show()
        }
    }

    private fun loadContacts() {
        contactSuggestions = mutableListOf()
        val cursor = requireContext().contentResolver.query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            arrayOf(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER),
            null,
            null,
            null
        )
        cursor?.use {
            while (it.moveToNext()) {
                val name = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                val phone = it.getString(it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER))
                (contactSuggestions as MutableList).add(Pair(name, phone))
            }
        }
        val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_dropdown_item_1line, contactSuggestions.map { "${it.first} - ${it.second}" })
        binding.contactAutoComplete.setAdapter(adapter)
    }
}

问题分析与解决建议

从报错信息来看,InvocationTargetException是反射调用时包装的内部异常,核心问题应该出在ViewModel初始化过程中,结合代码我梳理了几个可能的原因:

  1. EmergencyContact实体类配置错误
    Room要求实体类必须标注@Entity注解并指定表名,同时要有主键字段。如果你的实体类缺少这些配置,Room在创建数据库时会抛出异常,导致ViewModel初始化失败。建议检查实体类,正确配置示例如下:

    import androidx.room.Entity
    import androidx.room.PrimaryKey
    
    @Entity(tableName = "emergency_contacts")
    data class EmergencyContact(
        @PrimaryKey(autoGenerate = true) val id: Int = 0,
        val name: String,
        val phone: String
    )
    
  2. ContactDatabase方法逻辑错误
    getDatabase方法中,Log.d("TAG", "getDatabase: db created ")这行代码在return语句之后,永远不会执行,虽然不直接导致崩溃,但建议调整到synchronized块内部,保证逻辑合理性:

    fun getDatabase(context: Context): ContactDatabase {
        Log.d("TAG", "getDatabase: databse is about to create")
        return INSTANCE ?: synchronized(this) {
            Log.d("TAG", "getDatabase: in the process of making db ")
            val instance = Room.databaseBuilder(
                context.applicationContext,
                ContactDatabase::class.java,
                "emergency_db"
            )
                .build()
            INSTANCE = instance
            Log.d("TAG", "getDatabase: db created ")
            instance
        }
    }
    
  3. 添加异常捕获定位具体错误
    可以在Fragment的保存按钮点击事件中添加异常捕获,直接查看崩溃的具体原因:

    if (match != null) {
        val emergencyContact = EmergencyContact(name = match.first, phone = match.second)
        try {
            viewModel.insert(emergencyContact)
            Toast.makeText(requireContext(), "联系人已添加", Toast.LENGTH_SHORT).show()
            binding.contactAutoComplete.text.clear()
        } catch (e: Exception) {
            Log.e("TAG", "添加联系人失败: ${e.message}", e)
            Toast.makeText(requireContext(), "添加失败: ${e.message}", Toast.LENGTH_SHORT).show()
        }
    }
    
  4. 确认Room依赖配置正确
    检查Module级别的build.gradle是否添加了完整的Room依赖:

    dependencies {
        def room_version = "2.5.0"
        implementation "androidx.room:room-runtime:$room_version"
        kapt "androidx.room:room-compiler:$room_version"
        implementation "androidx.room:room-ktx:$room_version"
    }
    

按照以上步骤排查后,应该能解决崩溃问题。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 08:08:11