Android利用sqlite实现页面搜索记录功能

前言

数据库作为软件开发过程中必不可少的一门语言,一直深受广大后端程序员的关注和喜爱,作为移动开发来讲,虽然有着更多的其他存储方式可以使用,但是其也是并不可少的存在,特别是在某些特殊的应用场景下,因此掌握数据库的使用在移动开发也格外重要。本次我就通过一个存储搜索记录的例子,来一起巩固总结一下数据库在Android上的使用。

功能要点

相信大家应该在不少的App上见到过搜索记录这个功能,很简单的一个小功能,本次博客的内容,就是用数据库实现这个功能,那么要实现这个功能,需要满足那些条件呢?那肯定是针对搜索记录的增、删、查了

  1. 每次搜索时将搜索的关键字增加到我们的数据库里
  2. 可以删除某一或者全部搜索记录
  3. 查询某个页面对应的搜索记录

对此,我设计的表含有以下三个字段,来支持搜索记录功能的实现。

属性       类型  含义
id INTEGER(主键)

id

origin TEXT 来源(Activity名称)
word TEXT 关键词

界面设计

因为在一个App里可能有多个页面需要搜索功能,所以我们存储不同页面的搜索记录,对此我设计了两个页面,主页面和搜索页面,主界面含有两个入口,并根据上面三点需求设计了搜索页。

      

代码编写

Android自带的数据库为SQLite,要想在Android上使用它,我们需要通过继承官方的SQLiteOpenHelper来实现数据库工具类。

创建ShDbHelper类继承SQLiteOpenHelper并重写onCreate()和onUpgrade()方法,这里我声明了一些常量方便后面使用。

const val DB_VERSION = 1
const val DB_NAME = "my.db"
const val TABLE_NAME = "searchHistory"
const val ID = BaseColumns._ID
const val WORD = "word"
const val ORIGIN = "origin"

class ShDbHelper : SQLiteOpenHelper {

    constructor(context: Context) : super(context, DB_NAME, null, DB_VERSION)

    override fun onCreate(db: SQLiteDatabase) {
    }

    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
    }
}

建表

我们要存储记录就要得先有个表,在onCreate()方法里执行sql语句创建。Android提供了两类方法供我们执行sql操作。第一类我称为全sql方法,第二类为半sql方法。全sql方法的和半sql方法的区别就在sql语句上,前者需要编写整串sql语句,对sql语句的熟悉程度要求较高,主要方法有execSQL()rawQuery()。后者是Android将sql拆分,封装成了一些参数,只需编写部分sql语句。如果对sql语句非常熟悉,可以使用前者,如果怕拼写错误,确保安全的可以使用后者。

  • execSQL():用于执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句。
  • rawQuery():用于执行select语句。 

本例大部分使用后者进行编写。

 //创建表sql语句
        val create_sql = "CREATE TABLE $TABLE_NAME ($ID INTEGER PRIMARY KEY AUTOINCREMENT,$ORIGIN TEXT NOT NULL,$WORD TEXT NOT NULL)"
        //执行一个不是SELECT的SQL语句或任何其他返回数据的SQL语句。它无法返回任何数据
        db.execSQL(create_sql)

插入

存储搜索记录就要往表里执行插入操作,通过一个可写的SQLiteDatabase对象执行insert插入方法,该方法有三个参数,第一个参数为要插入的表的名称,第二个传null就行,第三个参数是一个ContentValues对象,官方提供了ContentValues对象供我们将需要插入的数据封装起来,就像使用map一样。

/*
    * 插入搜索记录
    * */
    fun insert(origin: String, word: String): Long {
        val value = ContentValues()
        value.put(ORIGIN, origin)
        value.put(WORD, word)
        return writableDatabase.insert(TABLE_NAME, null, value)
    }

查询

SQLiteDatabase提供了query()方法供我们对数据库进行查询操作,具体的参数含义看如下代码注释。

/*
    * 查询某页的所有搜索记录
    * */
    fun query(origin: String): ArrayList<String> {
        //需要查询的参数
        val columns = arrayOf(WORD)
        //查询条件
        val selection = "$ORIGIN =?"
        //查询条件对应的值
        val selectionArgs = arrayOf(origin)
        //按ID倒序排列
        val orderBy = "$ID DESC"
        
        val cursor = readableDatabase.query(TABLE_NAME, columns, selection, selectionArgs, null, null, orderBy)
        if (cursor.count != 0) {
            val list = arrayListOf<String>()
            while (cursor.moveToNext()) {
                list.add(cursor.getString(0))
            }
            cursor.close()
            return list
        }
        return arrayListOf()
    }

如果使用rawQuery(),那么上面这些参数对应的sql语句为

“SELECT word FROM searchHistory WHERE origin =? ORDER BY _id DESC”

这两个方法中sql语句的?代表占位符,具体的值为后面传递的参数。这个值其实也可以直接写进sql,后面那个参数传null。

为了方便使用,我将查询出来的数据封装到了一个列表返回。

删除

SQLiteDatabase提供了delete()方法供我们对数据库进行删除操作。参数和查询类似,含义看如下注释。

/*
    * 删除某页某条搜索记录
    * */
    fun delete(origin: String, word: String): Int {
        //表名称、删除条件、条件参数
        return writableDatabase.delete(TABLE_NAME, "$ORIGIN =? and $WORD =?", arrayOf(origin, word))
    }

当然,有时我们需要删除某个页面所有的记录,对此只需去掉word条件就行。

/*
    * 删除某页所有搜索记录
    * */
    fun deleteAll(origin: String): Int {
        return writableDatabase.delete(TABLE_NAME, "$ORIGIN =?", arrayOf(origin))
    }

到这我们本次例子所需要的基本功能增、删、查就都有了,不过从用户的使用角度来看本次例子的功能并没有完善,还缺少用户体验,比如

  1. 搜索同一关键词多次时关键词也被多次存储记录
  2. 若记录存在相同关键词,当前关键词位置应当显示在记录最前面
  3. 搜索记录条数未设限制
  4. 最旧的记录应该删掉

思考一番能发现,其实上面这四个问题都是应该在进行插入操作时做处理的。第一二个问题,都是一种解决办法,那就是在将关键词插入数据库的时候,先查询数据库有没有存储同样的关键词,有就删除再插入。第三四个也可以看做一个问题,对此需要额外设置一个记录最大条数的值,并且在当插入新的关键词时,如果当前记录数已超过最大值,则删掉最旧的一条记录,在执行插入操作。

针对第一二个问题,删除单个关键词的方法已经有了,还缺一个查询单个关键词的方法,再已有查询方法的sql语句上增加一个条件即可。

/*
    * 查询某页某条搜索记录
    * */
    private fun query(origin: String, word: String): Int {
        val projection = arrayOf(WORD)
        val selection = "$ORIGIN =? and $WORD =?"
        val selectionArgs = arrayOf(origin, word)
        val cursor = readableDatabase.query(TABLE_NAME, projection, selection, selectionArgs, null, null, null)
        return cursor.count
    }

对第三四个问题,出了设置最大值,也是先查后删,只不过查询条件变为查询最旧的一条记录的ID,然后在根据此ID删掉对应数据。

private fun queryOldest(origin: String): String {
        val projection = arrayOf(ID)
        val selection = "$ORIGIN =?"
        val selectionArgs = arrayOf(origin)
        val orderBy = "$ID ASC"

        val cursor = readableDatabase.query(TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy)
        cursor.moveToFirst()
        val id = cursor.getString(0)
        cursor.close()
        return id
    }

private fun deleteOldest(origin: String) {
        val id = queryOldest(origin)
        val i = writableDatabase.delete(TABLE_NAME, "$ID =?", arrayOf(id))
        if (i > 0)
            Log.i("kkk", "已删除最旧的记录")
    }

还有一种简单的写法,将查询删除总结到一句sql中。

private fun deleteOldest(origin: String) {
        val i = writableDatabase.delete(TABLE_NAME, "$ID =(select min($ID) from $TABLE_NAME where $ORIGIN =?)", arrayOf(origin))
        if (i > 0)
            Log.i("kkk", "已删除最旧的记录")
    }

用全sql写的话就是

DELETE FROM $TABLE_NAME WHERE $ID = (SELECT MIN($ID) FROM $TABLE_NAME WHERE $ORIGIN = $origin)

然后,插入的方法变成这样

 /*
    * 插入搜索记录
    * */
    fun insert(origin: String, word: String): Long {
        //如若已有记录,删除并从新插入
        if (query(origin, word) > 0)
            delete(origin, word)
        //新增超过每页记录最大存储数时删除最旧当页最旧数据
        val size = query(origin).size
        if (size >= MAX_COUNT)
            deleteOldest(origin)
        val value = ContentValues()
        value.put(ORIGIN, origin)
        value.put(WORD, word)
        return writableDatabase.insert(TABLE_NAME, null, value)
    }

剩下的跟界面相关的代码我就不贴了,都传到github上去了,有兴趣的小伙伴可以克隆下来看看。戳我

本人不才,例子就写到这里了,都是一些比较基础的东西,数据库的东西还有得研究,写的有什么不正确的地方还希望大家能指出来,与大家共勉~

发布了45 篇原创文章 · 获赞 18 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Ever69/article/details/102615373