面试题答案
一键面试精细权限控制
- 使用权限声明
- 在
AndroidManifest.xml
中声明权限。例如,定义一个自定义读取权限:
<permission android:name="com.example.permission.READ_MY_DATA" android:protectionLevel="normal" />
- 然后在
ContentProvider
的<provider>
标签中指定该权限,使得其他应用需要声明这个权限才能访问ContentProvider
:
<provider android:name=".MyContentProvider" android:authorities="com.example.myprovider" android:exported="true" android:readPermission="com.example.permission.READ_MY_DATA" />
- 在
- 按 URI 路径控制
ContentProvider
可以根据传入的Uri
路径进行不同的权限判断。在query
、insert
、update
和delete
方法中,可以通过解析Uri
来决定是否允许操作。例如:
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? { when (uri.path) { "/public_data" -> { // 公开数据,无需特殊权限 return queryPublicData(projection, selection, selectionArgs, sortOrder) } "/private_data" -> { // 检查是否有特定权限 if (checkCallingOrSelfPermission("com.example.permission.READ_PRIVATE_DATA") == PackageManager.PERMISSION_GRANTED) { return queryPrivateData(projection, selection, selectionArgs, sortOrder) } else { throw SecurityException("Permission denied to access private data") } } else -> throw IllegalArgumentException("Unknown URI: $uri") } }
- 基于签名的权限控制
- 可以设置基于签名的权限,只有签名相同的应用才能访问
ContentProvider
。在权限声明中设置android:protectionLevel="signature"
:
<permission android:name="com.example.permission.SAME_SIGNATURE_ACCESS" android:protectionLevel="signature" />
- 然后在
ContentProvider
的<provider>
标签中指定这个权限。这样只有和ContentProvider
所在应用签名相同的应用才能访问。
- 可以设置基于签名的权限,只有签名相同的应用才能访问
性能优化
- 批量操作
- 对于插入大量数据,使用
ContentProvider
的bulkInsert
方法而不是多次调用insert
。例如:
override fun bulkInsert(uri: Uri, values: Array<ContentValues>): Int { // 执行批量插入操作,例如使用 SQLite 的事务 val db = writableDatabase db.beginTransaction() try { var count = 0 for (value in values) { val newRowId = db.insert(TABLE_NAME, null, value) if (newRowId != -1L) { count++ } } db.setTransactionSuccessful() return count } finally { db.endTransaction() } }
- 对于插入大量数据,使用
- 缓存数据
- 对于经常读取的数据,可以在
ContentProvider
中实现缓存机制。例如,使用LruCache
来缓存查询结果:
private val cache = LruCache<String, Cursor>(10) // 缓存10个查询结果 override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? { val cacheKey = "$uri$projection$selection$sortOrder" val cachedCursor = cache.get(cacheKey) if (cachedCursor != null) { return cachedCursor } val cursor = super.query(uri, projection, selection, selectionArgs, sortOrder) cache.put(cacheKey, cursor) return cursor }
- 对于经常读取的数据,可以在
- 优化数据库查询
- 确保数据库表有适当的索引。例如,如果经常根据某个列进行查询,为该列创建索引:
val CREATE_TABLE = "CREATE TABLE $TABLE_NAME (" + "$COLUMN_ID INTEGER PRIMARY KEY AUTOINCREMENT, " + "$COLUMN_NAME TEXT, " + "$COLUMN_AGE INTEGER, " + "INDEX idx_name ($COLUMN_NAME))" db.execSQL(CREATE_TABLE)
- 在
ContentProvider
的查询方法中,尽量减少投影列,只返回必要的数据,避免返回大量不必要的列。
- 异步操作
- 对于耗时操作,如加载大量数据,使用异步任务。例如,在
query
方法中如果数据加载耗时,可以启动一个AsyncTask
或使用 Kotlin 的协程来异步加载数据,并返回一个Cursor
包装类,当数据加载完成后更新这个包装类中的Cursor
。
override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? { val asyncCursor = AsyncCursor() GlobalScope.launch(Dispatchers.IO) { val realCursor = loadLargeData(projection, selection, selectionArgs, sortOrder) asyncCursor.setCursor(realCursor) } return asyncCursor }
- 这里
AsyncCursor
是一个自定义的Cursor
包装类,在数据加载完成前提供占位功能,加载完成后更新真实的Cursor
。
- 对于耗时操作,如加载大量数据,使用异步任务。例如,在