Android 数据持久化

Android 数据持久化

在 Android 中数据持久化的方式有以下几种。

  1. SQLite
    1. SQLiteOpenHelper
  2. ContentProvider
  3. File
    1. InternalStorage
    2. ExternalStorage
  4. SharedPreferences

SQLite

SQLiteOpenHelper 的使用

官方文档
SQLiteOpenHelper 详细解析
使用 SQLiteOpenHelper 的正确姿势
查看当前数据库的工具

关于 SQLiteOpenHelper 介绍的 blog 已经太多了,在这里我尽量多去编程学习,而不是再复述一遍别人说过的话。

总的来说,SQLiteOpenHelper 就是一个Android 原生的 SQLite 数据库操作类,里面包含了 数据库创建,数据库升级。并且可以通过 getReadableDatabase(只读) 和 getWritableDatabase(读写) 方法获取数据库对象进行CURD。

如今我们用的更多的是 GreenDao, LitePal 等第三方框架来进行数据库操作。这些三方库本质上就是对 SQLiteOpenHelper 的封装。

ContentProvider -- 最熟悉的陌生人

ContentProvider 作为 Android 四大组件之一,但对我来说算是最陌生的一个。原因是 ContentProvider 用于进程间的通讯,而同样的功能在公司的项目中往往是用 socket 和 udp 实现的。接下来就记录一些相关的知识

ContentProvider 的介绍

官方文档

ContentProvider 是一种内容共享型的组件。本质上是一块数据(以 database,xml 等方式存储的数据),通过 ContentProvider 这层抽象的容器,提供 CURD 的方法,达到跨进程通讯的目的。

官方提供了以下的方法:

  • onCreate() which is called to initialize the provider
  • query(Uri, String[], Bundle, CancellationSignal) which returns data to the caller
  • insert(Uri, ContentValues) which inserts new data into the content provider
  • update(Uri, ContentValues, String, String[]) which updates existing data in the content provider
  • delete(Uri, String, String[]) which deletes data from the content provider
  • getType(Uri) which returns the MIME type of data in the content provider

在我们自定义的类继承 ContentProvider 后,实现这些方法。并在 AndroidManifest 中注册该类。即可以被其他应用访问了。

数据操作离不开 ContentResolver

ContentResolver 根据不同 ContentProvider 的 URI,来找到对应的 ContentProvider,并进行CURD的操作。
我们拿一个获取通讯录信息作为例子:

public fun getContactList(): List<String> {
        val contacts = ArrayList<String>()
        val projection = arrayOf(ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME)
        val query = ContextUtils.getContext()?.contentResolver?.query(ContactsContract.Contacts.CONTENT_URI, projection, null, null, null)
                ?: return contacts
        while (query.isLast) {
            contacts.add(query.getString(PHONES_NUMBER_INDEX))
            query.moveToNext()
        }
        query.close()
        return contacts
    }

Android 文件系统

内部存储

在安装一个应用的同时,会在 data/data 目录下创建一个与包名对应的文件夹。默认情况下,保存到内部存储的文件是应用的私有文件,其他应用(和用户)不能访问这些文件。 当用户卸载您的应用时,这些文件也会被移除。

方法 路径
Environment.getDataDirectory() /data
Environment.getDownloadCacheDirectory() /cache
Environment.getRootDirectory() /system
  • getFilesDir() 获取在其中存储内部文件的文件系统目录的绝对路径。
  • getDir() 在您的内部存储空间内创建(或打开现有的)目录。
  • deleteFile() 删除保存在内部存储的文件。
  • fileList() 返回您的应用当前保存的一系列文件。

外部存储

获取外部存储的访问权限

要读取或写入外部存储上的文件,您的应用必须获取 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 系统权限。

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

判断是否拥有访问权限

/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

常用路径.png

获取路径方式Environment.getExternalStorageDirectory()
(即路径/storage/sdcard0)

SharedPreferences

> SharedPreferences的理解与使用

SharedPreferences 也是我们常用的数据持久化方法之一,这里就不多赘述了,直接上代码。


object SpUtils {

    private const val FILE_DEFAULT_NAME = "DefaultPreference"

    @JvmStatic
    public fun getSharedPreferences(): SharedPreferences? {
        return ContextUtils.getContext()?.getSharedPreferences(FILE_DEFAULT_NAME, Context.MODE_PRIVATE)
    }

    @JvmStatic
    public fun getSharedPreferences(fileName: String): SharedPreferences? {
        return ContextUtils.getContext()?.getSharedPreferences(fileName, Context.MODE_PRIVATE)
    }

    @JvmStatic
    public fun put(sharedPreferences: SharedPreferences?, key: String, value: Any) {
        var edit = sharedPreferences?.edit()
        when (value) {
            is Int -> edit?.putInt(key, value)
            is String -> edit?.putString(key, value)
            is Boolean -> edit?.putBoolean(key, value)
            is Float, is Double -> edit?.putFloat(key, value as Float)
            is Long -> edit?.putLong(key, value)
        }
        edit?.apply()
    }

    @JvmStatic
    fun contains(sharedPreferences: SharedPreferences?, key: String): Boolean {
        return sharedPreferences?.contains(key) == true
    }

    @JvmStatic
    @Suppress("UNCHECKED_CAST")
    fun <T> get(sharedPreferences: SharedPreferences?, key: String, defaultValue: T): T {
        if (!contains(sharedPreferences, key)) return defaultValue
        when (defaultValue) {
            is Int -> return sharedPreferences?.getInt(key, defaultValue) as T
            is String -> return sharedPreferences?.getString(key, defaultValue) as T
            is Boolean -> return sharedPreferences?.getBoolean(key, defaultValue) as T
            is Float -> return sharedPreferences?.getFloat(key, defaultValue) as T
            is Long -> return sharedPreferences?.getLong(key, defaultValue) as T
        }
        return defaultValue
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 140,856评论 1 295
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 60,475评论 1 254
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 92,639评论 0 208
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 40,628评论 0 171
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,311评论 1 250
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,533评论 1 167
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,235评论 2 266
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,017评论 0 161
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 28,738评论 6 225
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,358评论 0 211
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,115评论 2 211
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,444评论 1 222
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,138评论 0 31
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 26,965评论 2 209
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,349评论 3 200
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,574评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 25,901评论 0 163
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,314评论 2 227
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,433评论 2 228

推荐阅读更多精彩内容