Kotlin实现ContentProvider onCreate()报Context类型不匹配错误
问题描述
我正在尝试实现ContentProvider的onCreate()方法,始终无法找到解决以下报错的方案:
"Type mismatch: inferred type is Context? but Context was expected(类型不匹配:推断类型为可空Context?,但要求传入非空Context)"
现有代码实现
DatabaseHandler.kt
package com.example.rewardreacher import android.annotation.SuppressLint import android.content.ContentValues import android.content.Context import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper import android.widget.Toast const val DATABASE_NAME = "GoalDB" const val TABLE_NAME = "Goals" const val COL_NAME = "name" const val COL_FREQUENCY = "frequency" const val COL_PERFORMED = "performed" const val COL_ID = "id" class DatabaseHandler(var context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, 1) { override fun onCreate(db: SQLiteDatabase?) { // No DB - Then create val createTable = "CREATE TABLE $TABLE_NAME (" + "$COL_ID INTEGER PRIMARY KEY AUTOINCREMENT," + "$COL_NAME VARCHAR(256), " + "$COL_FREQUENCY INTEGER(1), " + "$COL_PERFORMED INTEGER(256))" db?.execSQL(createTable) } override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { db?.execSQL("DROP TABLE IF EXISTS $DATABASE_NAME"); onCreate(db); } fun insertData(goal : Goal) { val db = this.writableDatabase val cv = ContentValues() cv.put(COL_NAME, goal.name) cv.put(COL_FREQUENCY, goal.frequency) cv.put(COL_PERFORMED, goal.performed) val result = db.insert(TABLE_NAME, null, cv) if (result == (0).toLong()) Toast.makeText(context, "Failed", Toast.LENGTH_LONG).show() else Toast.makeText(context, "Success", Toast.LENGTH_LONG).show() } @SuppressLint("Range") fun readData() : MutableList<Goal> { val list: MutableList<Goal> = ArrayList() val db = this.readableDatabase val query = "Select * from $TABLE_NAME" val result = db.rawQuery(query, null) if (result.moveToFirst()) { do { val goal = Goal() goal.id = result.getString(result.getColumnIndex(COL_ID)).toInt() goal.name = result.getString(result.getColumnIndex(COL_NAME)) goal.frequency = result.getString(result.getColumnIndex(COL_FREQUENCY)).toInt() goal.performed = result.getString(result.getColumnIndex(COL_PERFORMED)).toInt() list.add(goal) } while (result.moveToNext()) } result.close() db.close() return list } }
MyContentProvider.kt
package com.example.rewardreacher.provider import android.content.ContentProvider import android.content.ContentValues import android.content.UriMatcher import android.database.Cursor import android.net.Uri import com.example.rewardreacher.DatabaseHandler const val DATABASE_NAME = "GoalDB" const val GOALS_TABLE = "Goals" const val COL_NAME = "name" const val COL_FREQUENCY = "frequency" const val COL_PERFORMED = "performed" const val COL_ID = "id" class MyContentProvider : ContentProvider() { private var dbHandler: DatabaseHandler? = null override fun onCreate(): Boolean { dbHandler = DatabaseHandler(context) return false } private val GOALS = 1 private val GOALS_ID = 2 private val sURIMatcher = UriMatcher(UriMatcher.NO_MATCH) init { sURIMatcher.addURI(AUTHORITY, GOALS_TABLE, GOALS) sURIMatcher.addURI(AUTHORITY, GOALS_TABLE + "/#", GOALS_ID) } companion object { val AUTHORITY = "com.example.rewardreacher.provider.MyContentProvider" val CONTENT_URI : Uri = Uri.parse("content://" + AUTHORITY + "/" + GOALS_TABLE) } override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int { TODO("Implement this to handle requests to delete one or more rows") } override fun getType(uri: Uri): String? { TODO( "Implement this to handle requests for the MIME type of the data" + "at the given URI" ) } override fun insert(uri: Uri, values: ContentValues?): Uri? { TODO("Implement this to handle requests to insert a new row.") } override fun query( uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String? ): Cursor? { TODO("Implement this to handle query requests from clients.") } override fun update( uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>? ): Int { TODO("Implement this to handle requests to update one or more rows.") } }
我没有Android Studio与Kotlin开发经验,猜测这是个比较基础的问题,但始终无法自行修复。
报错原因
这是Kotlin空安全机制的基础校验问题:
ContentProvider自带的getContext()方法返回值是可空类型Context?,系统定义里这个字段有可能为null- 你写的
DatabaseHandler构造函数明确要求传入非空的Context类型 - 直接把可空类型传给要求非空的参数,就会触发你看到的类型不匹配报错
- 另外你当前
onCreate()方法返回false是错误的,ContentProvider初始化成功需要返回true,返回false会被系统判定为初始化失败。
修复方法
两种常用写法,选一种替换你原来的onCreate()实现即可:
- 安全判空写法(新手推荐,不会触发空指针崩溃)
override fun onCreate(): Boolean { // 仅当context不为空时才执行初始化逻辑 context?.let { dbHandler = DatabaseHandler(it) } // 初始化成功(dbHandler不为空)返回true return dbHandler != null }
- 非空断言写法(代码更简洁,正常场景无风险)
override fun onCreate(): Boolean { // 强制断言context不为空,ContentProvider调用onCreate时context必然已经初始化完成 dbHandler = DatabaseHandler(context!!) return true }
补充说明:!!是Kotlin的非空断言操作符,如果你断言的对象实际为null,会直接抛出空指针异常。但根据Android系统的ContentProvider生命周期,onCreate()被调用时Provider已经附着到Context上,context一定非空,所以这种写法在实际生产环境是安全的。
内容的提问来源于stack exchange,提问作者dev.AMS
相关产品推荐
相关产品推荐

