Android開發者之數據存儲,你真的會存儲數據嗎?

【版權申明】非商業目的可自由轉載
博文地址:https://blog.csdn.net/ShuSheng0007/article/details/102684695
出自:shusheng007

前言

現在大家都在說:數據就是未來世界的黃金,其實個人覺得應該說被處理過的數據纔是,而這個處理技術就被我們親切的稱呼爲大數據,就是在海量數據中淘金。
在Android的日常開發過程中,我們經常會遇到將數據存儲到本地的需求,但做了很多這樣的工作,卻不瞭解各種存儲方式的適用場景,導致各種安全及性能問題,我自己最開始也有這方面的問題。

選擇存儲方式時要考慮:數據的尺寸、類型、特徵、安全性等問題,下面記錄一下目前android系統提供的各種存儲方式。

文件存儲系統

Android的文件系統繼承至Linux系統。早期的劃分更側向於物理劃分,即可卸載的外部存儲,例如外插sd卡,U盤等,不可卸載的爲內部存儲。發展到目前,這個概念已經淡化了,現在主流設備都沒有sd卡插槽了,但是設備的存儲仍然被劃分爲內部與外部。現在內外存儲的最大區別集中在讀寫權限上。

內部存儲系統

Android會爲每個App分配一塊私有的存儲空間,存儲在這裏的數據只有此App自己可以訪問到,用戶及其他App均不可訪問,被root的設備除外

特點:

  1. 數據隨着App的卸載而自動刪除
  2. 不需要申請存儲讀寫權限
  3. 數據私有,除自己外不可見

內部存儲又分爲一個持久目錄與一個緩存目錄。當設備存儲不足時,系統會刪除緩存目錄的數據以釋放空間,而且不會通知App,因此在使用緩存中的數據時,原則上應該先檢查其狀態。爲了節約空間,我們應該限制緩存目錄的空間,例如2M, 然後定期主動清除。

獲取內部存儲目錄

getFilesDir()

在我的Mix2上的路徑爲

/data/user/0/app的包名/files

獲取內部存儲緩存目錄

getCacheDir() 

在我的Mix2上的路徑爲

/data/user/0/app的包名/cache

外部存儲系統

外部存儲就比較複雜了,最初的Android設備外部存儲就是指外部接入的存儲,例如sd卡,U盤,硬盤。現在除了被操作系統劃分爲內部存儲的部分,其餘的都叫外部存儲。

外部存儲甚至可以使用內部存儲模擬,所以說外部還是內部就是系統根據一些標準人爲劃分的。

adb shell sm set-virtual-disk true

外部存儲根據功能又可以進一步分爲公開文件目錄與私有文件目錄

公開文件

指主觀上希望App產生的數據可以供其他app訪問,例如相機拍攝的照片希望讓別的App使用,瀏覽器下載的文件也希望讓其他App訪問,即使是在用戶卸載了這些App之後。

此類文件Google推薦使用如下兩種方式處理

  1. MediaStore
  2. Storage Access Framework

上面兩種方式均是利用content provider 來進行交互,有時間再詳細寫一下關於這兩個方面的內容。

其實還有一種Android10以後不推薦文件目錄,就是設備存儲的根目錄,但是要注意,訪問這些目錄是需要存儲讀寫權限的。

獲取根目錄(Android 10 以後廢棄)

Environment.getExternalStorageDirectory()

在Mix2的目錄爲

/storage/emulated/0

另一種獲取獲取根目錄下一級目錄的方法爲

Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)

在Mix2的目錄爲

/storage/emulated/0/Pictures

注意那個入參,Environment.DIRECTORY_PICTURES 就是“Pictures”,我們也可以傳入其他的參數,例如

public static String DIRECTORY_MOVIES = "Movies";
public static String DIRECTORY_DOWNLOADS = "Download";
public static String DIRECTORY_SCREENSHOTS = "Screenshots";

第二種方式可以看做的第一種方式的特例,如果你想要在根目錄下面建立一個你自己的文件夾,那就需要使用第一種方式了。

特點:

  1. 需要存儲讀寫權限
  2. 不跟隨App的卸載而刪除
  3. 容量巨大
  4. 不可靠,使用時需要確保存儲可用

私有文件

指主觀上不希望App產生的數據被別的App訪問。

是一個包含App包名的路徑,與內部存儲一樣,存在一個持久目錄和一個緩存目錄

獲取外部存儲目錄

getExternalFilesDir(String type)

當參數傳入null時,在我的Mix2上的路徑爲

/storage/emulated/0/Android/data/app的包名/files

入參爲子目錄,如果傳入“Pictures”,那麼就會在上面的目錄下面增加一級“/Pictures”
獲取外部存儲緩存目錄

getExternalCacheDir()

在我的Mix2上的路徑爲

/storage/emulated/0/Android/data/app的包名/cache

一般情況下,我應該將APP的非敏感數據存放在此處。

特點:

  1. Android 4.4 (API level 19)之後不需要存儲讀寫權限
  2. 跟隨App的卸載而刪除
  3. 技術上仍然是全局可訪問的,只要某個應用知道具體文件的目錄,且具有讀寫存儲的權限

將圖片插入相冊中

有時我們會有在相冊中顯示APP保存的圖片這樣的需求,那如何做呢?

思路:使用 'MediaScannerConnection’將要展示的圖片掃描一下,讓系統生成一個代表這張圖片的Uri並插入一個系統維護的列表中,相冊會去讀取這個Uri然後展示。

下面的代碼的功能爲:保存一張圖片並掃描到媒體庫中。打開系統的相冊後,會看到我們保存的照片

    private fun saveImageOfRaw(resId: Int) {
        val inputStream = resources.openRawResource(resId)
        //路徑必須是存儲在外部存儲的非私有路徑下才能在相冊裏面展示
//        val dir = [email protected](Environment.DIRECTORY_PICTURES) 不可以
//        val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) 可以
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
        val dirPath = Environment.getExternalStorageDirectory().path
        val dir = File("$dirPath/AndroidDevMemo")
        if (dir.exists().not()) {
            dir.mkdir()
        }

        val file = File(dir, "mySon.jpg")
        val data = ByteArray(inputStream.available())
        inputStream.read(data)
        FileOutputStream(file).use {
            it.write(data)
        }
        MediaScannerConnection.scanFile(this@StorageActivity, arrayOf(file.toString()), null,
            object : MediaScannerConnection.OnScanCompletedListener {
                override fun onScanCompleted(path: String?, uri: Uri?) {
                    printResult("Scanned $path:->uri=$uri\n\n")
                }
            })
    }

圖片的路徑爲:

/storage/emulated/0/AndroidDevMemo/mySon.jpg

掃描後生成的Uri爲:

content://meida/external/images/meida/44152

值得一提的是,當圖片的存儲目錄是私有文件目錄時,無法顯示在相冊中,但是可以生成Uri ,需要手動保存到媒體庫中吧。
請參考MediaStore

SharedPreferences

原理

底層爲一個xml文件,存儲路徑隨着不同的設備可能會不一致,此處給出獲取其路徑的方法

 val dataPath= getDatabasePath("app包名_preferences.xml").absolutePath

上面是獲取默認文件的方式,如果自己定義了shared preferences 文件的名稱,則傳入自定義名稱。

SharedPreferences 主要用於存儲鍵值對

使用方法

  1. 獲取一個SharedPreferences 實例
    一般會生成一個APP共享的實例
    val sharePreferences = getSharedPreferences("$PACKAGE_ID _preference", Context.MODE_PRIVATE)
    
  2. 保存數據
    保存數據有同步和異步兩種方式,不過每一種方式都是先獲取一個Eidtor,然後壓入要保存的數據,然後提交.
    同步方式使用commit(),異步方式使用apply()
    with(sharePreferences.edit()) {
         putString("name", "ShuSheng007")
         commit()
         //commit() 同步寫入
         //apply() 異步寫入
     }
    
  3. 提取數據
val result = sharePreferences.getString("name", "")

數據庫 (sqlite)

Android 支持 sqlite 數據庫,而且提供了一套使用sqlite數據的Api,但是目前 google 推薦使用 Room這個庫來代替直接操sqlite數據庫Api.

概述

好知識需要及時總結,積極傳播,自己就是從一個什麼都不懂的人自學過來的,深知隨便一個小問題對於入門的人來說就是一座大山,希望本文可以給翻山的同學一點幫助,也是對自己過往的一個記錄,也許幾年後咱就離開這個行業了。

不知不覺10月也過去了,突然感覺自己的人生活成了那種 “20歲就死了,80歲才埋”的樣子了,對未來沒有了興奮感,沒有了期待。有人說,如果感到迷惘就去讀書和健身,我決定試一試,嘗試做一些打破日復一日的常規的事情。。。

本文源碼下載地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章