Android 數(shù)據(jù)持久化
在 Android 中數(shù)據(jù)持久化的方式有以下幾種。
- SQLite
- SQLiteOpenHelper
- ContentProvider
- File
- InternalStorage
- ExternalStorage
- SharedPreferences
SQLite
SQLiteOpenHelper 的使用
官方文檔
SQLiteOpenHelper 詳細(xì)解析
使用 SQLiteOpenHelper 的正確姿勢(shì)
查看當(dāng)前數(shù)據(jù)庫(kù)的工具
關(guān)于 SQLiteOpenHelper 介紹的 blog 已經(jīng)太多了,在這里我盡量多去編程學(xué)習(xí),而不是再?gòu)?fù)述一遍別人說過的話。
總的來說,SQLiteOpenHelper 就是一個(gè)Android 原生的 SQLite 數(shù)據(jù)庫(kù)操作類,里面包含了 數(shù)據(jù)庫(kù)創(chuàng)建,數(shù)據(jù)庫(kù)升級(jí)。并且可以通過 getReadableDatabase(只讀) 和 getWritableDatabase(讀寫) 方法獲取數(shù)據(jù)庫(kù)對(duì)象進(jìn)行CURD。
如今我們用的更多的是 GreenDao, LitePal 等第三方框架來進(jìn)行數(shù)據(jù)庫(kù)操作。這些三方庫(kù)本質(zhì)上就是對(duì) SQLiteOpenHelper 的封裝。
ContentProvider -- 最熟悉的陌生人
ContentProvider 作為 Android 四大組件之一,但對(duì)我來說算是最陌生的一個(gè)。原因是 ContentProvider 用于進(jìn)程間的通訊,而同樣的功能在公司的項(xiàng)目中往往是用 socket 和 udp 實(shí)現(xiàn)的。接下來就記錄一些相關(guān)的知識(shí)
ContentProvider 的介紹
ContentProvider 是一種內(nèi)容共享型的組件。本質(zhì)上是一塊數(shù)據(jù)(以 database,xml 等方式存儲(chǔ)的數(shù)據(jù)),通過 ContentProvider 這層抽象的容器,提供 CURD 的方法,達(dá)到跨進(jìn)程通訊的目的。
官方提供了以下的方法:
- 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 后,實(shí)現(xiàn)這些方法。并在 AndroidManifest 中注冊(cè)該類。即可以被其他應(yīng)用訪問了。
數(shù)據(jù)操作離不開 ContentResolver
ContentResolver 根據(jù)不同 ContentProvider 的 URI,來找到對(duì)應(yīng)的 ContentProvider,并進(jìn)行CURD的操作。
我們拿一個(gè)獲取通訊錄信息作為例子:
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 文件系統(tǒng)
內(nèi)部存儲(chǔ)
在安裝一個(gè)應(yīng)用的同時(shí),會(huì)在 data/data 目錄下創(chuàng)建一個(gè)與包名對(duì)應(yīng)的文件夾。默認(rèn)情況下,保存到內(nèi)部存儲(chǔ)的文件是應(yīng)用的私有文件,其他應(yīng)用(和用戶)不能訪問這些文件。 當(dāng)用戶卸載您的應(yīng)用時(shí),這些文件也會(huì)被移除。
| 方法 | 路徑 |
|---|---|
| Environment.getDataDirectory() | /data |
| Environment.getDownloadCacheDirectory() | /cache |
| Environment.getRootDirectory() | /system |
-
getFilesDir()獲取在其中存儲(chǔ)內(nèi)部文件的文件系統(tǒng)目錄的絕對(duì)路徑。 -
getDir()在您的內(nèi)部存儲(chǔ)空間內(nèi)創(chuàng)建(或打開現(xiàn)有的)目錄。 -
deleteFile()刪除保存在內(nèi)部存儲(chǔ)的文件。 -
fileList()返回您的應(yīng)用當(dāng)前保存的一系列文件。
外部存儲(chǔ)
獲取外部存儲(chǔ)的訪問權(quán)限
要讀取或?qū)懭胪獠看鎯?chǔ)上的文件,您的應(yīng)用必須獲取 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 系統(tǒng)權(quán)限。
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
判斷是否擁有訪問權(quán)限
/* 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;
}

獲取路徑方式Environment.getExternalStorageDirectory()
(即路徑/storage/sdcard0)
SharedPreferences
SharedPreferences 也是我們常用的數(shù)據(jù)持久化方法之一,這里就不多贅述了,直接上代碼。
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
}
}