安卓查询系统数据库(ContentResolver)带关键字解决方法
前言
一个类似把手机中文件查询出来的需求,按关键字模糊搜索,测试提了一个bug,说输入 “\”,“%”,"_"这三个特殊字符,得到的搜索结果是异常的.
ContentResolver的query方法简略介绍
我们项目是用安卓的ContentResolver的query方法去搜索系统的原生数据库的,使用的是以下方法:
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return query(uri, projection, selection, selectionArgs, sortOrder, null);
}
参数含义为
uri
- 查询的表名
projection
- 查询表所需要返回的列
selection
- 查询条件
selectionArgs
- 查询条件的参数
sortOrder
- 排序(升序或者降序)
光说可能有点抽象,我们详细解释一下,例如下面的SQL查询语句:
SELECT artist,title FROM files WHERE media_type=2 and title LIKE '%off%'
我们将SQL查询语句中的条件换成query方法的参数, 就是如下:
private val mResolver = mContext.contentResolver
fun test(){
//注意:这里是伪代码,方便理解
mResolver.query(files, [artist,title], "media_type= ? and title LIKE ?"",[2 , '%off%'], null)
}
方法了解到这里就差不多了
问题分析
从前言问题可以得出,是特殊格式符号的搜索结果不正确,那么我们直接用SQL语句去数据库里面查特殊符号,方便排查问题,语句类似如下:
SELECT artist FROM files WHERE media_type=2 and title LIKE '%\_%'
发现,查询出来的并不是我们想要的结果,百度过后发现是因为这些特殊符号其实是SQL语句中的关键字,并不是查询带下划线的数据,因此我们需要将这种关键字替换成可以被当成普通字符查询的API,这时候就要使用到 ESCAPE,它可以帮助我们达到这一目的,我们将上述SQL语句改为如下:
SELECT artist FROM files WHERE media_type=2 and title LIKE '%\_%' ESCAPE '\'
这时,就能得出我们想要的正确结果.
问题解决
我们找到了问题原因,但是安卓查询数据库的写法与SQL不同,需要注意写法,例如,我们有问题的代码如下:
private val mResolver = mContext.contentResolver
fun test(queryString : String){
val selection = "title like ? "
mResolver.query(uri, null, selection, arrayOf("%$queryString%"), null)
}
那么我们更改后的代码应该为如下:
private val mResolver = mContext.contentResolver
fun test(query : String){
val queryString = query
.replace("\\", "\\\\")
.replace("%", "\\%")
.replace("_", "\\_")
val selection = "title like ? ESCAPE '\'"
mResolver.query(uri, null, selection, arrayOf("%$queryString%"), null)
}
在正确的代码中,我们先将要查询的语句进行转义,然后在真正查询的时候ESCAPE掉,达到将关键字作为搜索语句的效果.