Android存儲(chǔ)

安卓存儲(chǔ)

今天晚上Android項(xiàng)目組的小伙伴一起聊了一下Android數(shù)據(jù)持久化的幾種方式外加一些項(xiàng)目中的總結(jié)吧.突然發(fā)現(xiàn)一些不怎么用的東西都已經(jīng)快忘光了,比如說ContentProvider + LoadManger + URIMatcher + CursorAdapter,今天突然提到LoadManager感覺像是一個(gè)沒接觸過的東西,被小伙伴提了一下才記得有這個(gè)類的存在,更別提ContentObserver了.有點(diǎn)跑題了還是聊一下Android的數(shù)據(jù)持久化策略吧!

Android數(shù)據(jù)持久化的方式

總所周知,Android共有五種數(shù)據(jù)持久化的方式,這里是官方文檔.通過閱讀API Guidance可知,這其中方式分別如下:

1. SharePreference

通常用于存儲(chǔ)一些本地化的配置文件,主要分為讀和取,操作如下:

1.寫入操作

    // We need an Editor object to make preference changes.
    // All objects are from android.context.Context
    SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
    SharedPreferences.Editor editor = settings.edit();
    editor.putBoolean("silentMode", mSilentMode);
    // Commit the edits!
    editor.commit();        
     //finally we can see these file in data/data/packagename/shared_preference if your device has been rooted;   

2.讀取操作

    // Restore preferences
    SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
    //false stands for the default value, you can customize yourself
    boolean silent = settings.getBoolean("silentMode", false);

2. Internal Storage

第一點(diǎn)提到的SharePreference存儲(chǔ)的方式最終存放的位置就是在Internal Storage中

    String FILENAME = "hello_file";
    String string = "hello world!";
    //finally the file path is data/data/packagename/files/hello_file
    //and there are kinds of MODE(MODE_PRIVATE,MODE_APPEND,MODE_WORLD_READABLE,MODE_WORLD_WRITABLE)
    FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
    fos.write(string.getBytes());
    fos.close();                    

值得注意的是從API 17開MODE_WORLD_READABLE跟MODE_WORLD_WRITABLE已經(jīng)是deprecated的狀態(tài).從API 23開始,由于谷歌收緊了Android系統(tǒng)的權(quán)限(Android越來越封閉,蘋果越來越開放,目的都是為了體驗(yàn)越來越好!),如果還使用這兩個(gè)屬性會(huì)直接拋出異常SecurityException.因此如果你的應(yīng)用的TargetVersion在API 23,那么如果你想共享應(yīng)用的內(nèi)部數(shù)據(jù)則只能通過主動(dòng)分享的方式發(fā)起共享,詳見參考文檔.

正如官方文檔所提,如果想要存儲(chǔ)靜態(tài)文件,比如說應(yīng)用的鈴聲之類的文件,可以放在項(xiàng)目的res/raw/路徑下,通過openRawResource(R.raw.fileId)獲取對(duì)應(yīng)的資源文件.

保存Cache文件

通過getCacheDir()可以返回app內(nèi)部的cache文件,該目錄即data/data/packagename/cache由系統(tǒng)維護(hù),但是谷歌的官方建議是自行維護(hù)1MB左右,該目錄會(huì)隨著應(yīng)用卸載而被清理掉.其他諸如getFileDir(),getDir()會(huì)在后文有個(gè)總結(jié)性的說明.

3. External Storage

Android的外部存儲(chǔ)可以分為可卸載的存儲(chǔ)例如SD卡,以及不可卸載的內(nèi)部存儲(chǔ)(Internal Storage).通過USB文件傳輸模式連接的時(shí)候,用戶可以對(duì)外部存儲(chǔ)進(jìn)行操作.

當(dāng)用戶掛載外部存儲(chǔ)或者移除外部存儲(chǔ)的時(shí)候,應(yīng)用就無法操作外部存儲(chǔ)了.同時(shí)外部存儲(chǔ)的全局可讀特性也決定了它本身并沒有安全性可言.

獲取訪問外部存儲(chǔ)的權(quán)限

如果你的應(yīng)用需要讀寫外部存儲(chǔ)則需要在manifest文件中配置如下權(quán)限:

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

如果只需要讀取權(quán)限只需申明READ_EXTERNAL_STORAGE權(quán)限即可;如果需要讀寫權(quán)限只需要申明WRITE_EXTERNAL_STORAGE權(quán)限即可,因?yàn)橄到y(tǒng)會(huì)同時(shí)聲明讀取權(quán)限.

注意:從Android4.4開始,如果只需要讀寫app內(nèi)部的文件,則無需申明讀寫權(quán)限.

檢查是否具備讀寫權(quán)限

無論在什么地方進(jìn)行外部存儲(chǔ)操作的時(shí)候你都應(yīng)該檢查外部存儲(chǔ)是否處于可用狀態(tài).

    /*Ckecks if external storage is available for read and write\*/
    public boolean isExternalStorageAvailable(){
        String state = Environment.getExternalStorageState();
        return Environment.MEDIA_MOUNTED.equals();
    }

通過Environment.getExternalStorageState()可以獲得外部存儲(chǔ)的狀態(tài),如連接到電腦,徹底移除,不恰當(dāng)?shù)囊瞥鹊?此時(shí)你可以通過判斷外部存儲(chǔ)的狀態(tài)來決定是否需要訪問外部存儲(chǔ)的媒體文件.如下所示為外置存儲(chǔ)的不同狀態(tài):

    public static final String MEDIA_BAD_REMOVAL = "bad_removal";
    public static final String MEDIA_CHECKING = "checking";
    public static final String MEDIA_EJECTING = "ejecting";
    public static final String MEDIA_MOUNTED = "mounted";
    public static final String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
    public static final String MEDIA_NOFS = "nofs";
    public static final String MEDIA_REMOVED = "removed";
    public static final String MEDIA_SHARED = "shared";
    public static final String MEDIA_UNKNOWN = "unknown";
    public static final String MEDIA_UNMOUNTABLE = "unmountable";
    public static final String MEDIA_UNMOUNTED = "unmounted";

保存文件共享給其他應(yīng)用

主要是公共的多媒體庫(kù),例如MUSIC,PICTURE等等,通過以下方式獲得

    Environment.getExternalPublicDirectory(String type)

其中type可分為如下幾種,對(duì)應(yīng)到外部存儲(chǔ)的不同位置.

    public static String DIRECTORY_ALARMS;
    public static String DIRECTORY_DCIM;
    public static String DIRECTORY_DOCUMENTS;
    public static String DIRECTORY_DOWNLOADS;
    public static String DIRECTORY_MOVIES;
    public static String DIRECTORY_MUSIC;
    public static String DIRECTORY_NOTIFICATIONS;
    public static String DIRECTORY_PICTURES;
    public static String DIRECTORY_PODCASTS;
    public static String DIRECTORY_RINGTONES;

保存應(yīng)用私有文件

如果你的應(yīng)用有一些私有文件,如音效文件,此時(shí)可以在外部存儲(chǔ)中創(chuàng)建一個(gè)私有的文件目錄:

    //if you pass null as the type return the root directory
    //storage/emulated/0/Android/data/packagename/
    //of course you can pass non null type to create a subdirectory
    Context.getExternalFilesDir(String type)    

從Android 4.4開始,讀寫應(yīng)用的私有空間的文件并不需要讀寫權(quán)限,因此如果其他地方無需讀寫權(quán)限的情況下可以通過一下方式申明:

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

這里有個(gè)事情需要說明的是,這個(gè)應(yīng)用的私有目錄會(huì)隨著應(yīng)用的卸載而刪除.同時(shí),在媒體庫(kù)中并不會(huì)顯示應(yīng)用的私有文件,因此,如果在你的app中屬于用戶的文件是不能保存到這個(gè)目錄的,例如用戶購(gòu)買的音樂.

有些手機(jī)會(huì)把內(nèi)置存儲(chǔ)劃出來一部分做為外置存儲(chǔ)使用(我們現(xiàn)在使用的多數(shù)都是這種方式),但是的手機(jī)還提供了SD卡卡槽.對(duì)于Android 4.3及以下的設(shè)備,通過getExternalFilesDir()只能獲得內(nèi)置存儲(chǔ)中劃出來的那一部分,也就是說并不能獲得SD卡部分.從Android4.4開始通過getExternalFilesDir()返回的是一個(gè)數(shù)組.只有在內(nèi)置存儲(chǔ)劃分的外置存儲(chǔ)不可用或者占用滿的情況下才選擇使用SD卡做為應(yīng)用的私有存儲(chǔ)空間.如果想要在Android4.3及以下的設(shè)備中獲取該路徑則通過兼容包ContextCompact.getExternalFilesDirs()獲取.

需要注意的是,盡管對(duì)于媒體中心(MediaStore)而言,應(yīng)用外部存儲(chǔ)的私有空間是不可見的,但是對(duì)于其他具有讀取或者寫入存儲(chǔ)權(quán)限的軟件而言,放在外部存儲(chǔ)的文件是可讀可寫的.因此,如果你不想你的文件被別的軟件使用或者更改的話,就只能放到應(yīng)用的內(nèi)部空間了.

保存緩存文件

通過如下方式獲得緩存文件的保存目錄,緩存目錄會(huì)隨著應(yīng)用的卸載而被刪除:

getExternalCacheDir();

跟上面提到的類似,你也可以通過ContextCompact.getExternalCacheDirs()獲得SD卡之類的外置存儲(chǔ)的緩存路徑.

在代碼開發(fā)的時(shí)候我們應(yīng)該特別注意緩存文件的維護(hù),防止緩存文件占用過多的空間.比如說我們使用一些第三方的框架的時(shí)候會(huì)讓我們配置緩存空間大小.

Using Databases

Android支持SQLite的所有功能,在應(yīng)用里面創(chuàng)建的數(shù)據(jù)庫(kù)能夠被應(yīng)用的任何一個(gè)類訪問,但是不能在應(yīng)用外訪問.通常可以配合URIMatcher + ContentProvider + SQLiteOpenHelper進(jìn)行數(shù)據(jù)庫(kù)數(shù)據(jù)共享,列入聯(lián)系人的獲取.

Using NetWork Connection

即數(shù)據(jù)存儲(chǔ)在服務(wù)端,通過網(wǎng)絡(luò)獲取持久化數(shù)據(jù).

總結(jié)

前面基本上是對(duì)于官方文檔的一種翻譯和理解,后面來說一下自己的總結(jié),首先觀察一下現(xiàn)象

            ///storage/emulated/0/Android/data/com.max.testuninstall/cache
            Log.d(TAG, "onCreate: 外部緩存存儲(chǔ)" + this.getExternalCacheDir().toString());
            //storage/emulated/0/Android/data/com.max.testuninstall/files
            Log.d(TAG, "onCreate: 外部私有存儲(chǔ)" + this.getExternalFilesDir(null).toString());
            ///storage/emulated/0
            Log.d(TAG, "onCreate: 外部共用存儲(chǔ)" + Environment.getExternalStorageDirectory().toString());
            ///data/user/0/com.max.testuninstall/cache
            Log.d(TAG, "onCreate: 內(nèi)部緩存存儲(chǔ)" + this.getCacheDir().toString());
            ///data/user/0/com.max.testuninstall/files
            Log.d(TAG, "onCreate: 內(nèi)部文件存儲(chǔ)" + this.getFilesDir().toString());
            ///data/user/0/com.max.testuninstall/app_null
            Log.d(TAG, "onCreate: 內(nèi)部文件存儲(chǔ)" + this.getDir(null, MODE_PRIVATE).toString());
            ///data/user/0/com.max.testuninstall/app_hello_world
            Log.d(TAG, "onCreate: 內(nèi)部文件存儲(chǔ)" + this.getDir("hello_world", MODE_PRIVATE).toString());
            ///data/user/0/com.max.testuninstall/files(List)
            Log.d(TAG, "onCreate: 內(nèi)部文件存儲(chǔ),返回目錄下所有文件" + this.fileList().toString());
            String FILENAME = "hello_file";
            String string = "hello world!";
            //data/data/packagename/files/hello_file
            FileOutputStream fos = this.openFileOutput(FILENAME, Context.MODE_PRIVATE);
            fos.write(string.getBytes());
            fos.close();

以上對(duì)應(yīng)前文所說的通過不同方式獲取內(nèi)部,外部文件及緩存路徑.

讀寫權(quán)限

Internal Storage本身無需聲明任何權(quán)限即可進(jìn)行讀寫操作.External Storage
從Android4.4開始讀寫應(yīng)用私有空間無需聲明讀寫權(quán)限,Android4.3及以下需要聲明讀寫權(quán)限.Android系統(tǒng)中寫入External Storage權(quán)限包含讀取External Storage權(quán)限.

應(yīng)用卸載

通過上面對(duì)應(yīng)的日志結(jié)合實(shí)際操作發(fā)現(xiàn),Context獲取的路徑在應(yīng)用卸載的時(shí)候會(huì)被同時(shí)刪除,其實(shí)這也是可以理解的,畢竟是屬于應(yīng)用私有的文件.通過Environment獲得的路徑屬于外界環(huán)境的,所以不會(huì)跟隨應(yīng)用的卸載而被刪除掉.

多用戶

在/storage/emulated/目錄下面會(huì)有 0(默認(rèn)用戶),如果新建一個(gè)用戶則是10,再新建則是11依此類推.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末污它,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子账劲,更是在濱河造成了極大的恐慌孟害,老刑警劉巖蟋字,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晌姚,死亡現(xiàn)場(chǎng)離奇詭異沪曙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門按厘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來医吊,“玉大人,你說我怎么就攤上這事逮京∏涮茫” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵懒棉,是天一觀的道長(zhǎng)草描。 經(jīng)常有香客問我,道長(zhǎng)策严,這世上最難降的妖魔是什么穗慕? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮妻导,結(jié)果婚禮上揍诽,老公的妹妹穿的比我還像新娘。我一直安慰自己栗竖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布渠啤。 她就那樣靜靜地躺著狐肢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沥曹。 梳的紋絲不亂的頭發(fā)上份名,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天,我揣著相機(jī)與錄音妓美,去河邊找鬼僵腺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛壶栋,可吹牛的內(nèi)容都是我干的辰如。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贵试,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼琉兜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毙玻,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤豌蟋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后桑滩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梧疲,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幌氮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缭受。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖浩销,靈堂內(nèi)的尸體忽然破棺而出贯涎,到底是詐尸還是另有隱情,我是刑警寧澤慢洋,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布塘雳,位于F島的核電站,受9級(jí)特大地震影響普筹,放射性物質(zhì)發(fā)生泄漏败明。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一太防、第九天 我趴在偏房一處隱蔽的房頂上張望妻顶。 院中可真熱鬧,春花似錦蜒车、人聲如沸讳嘱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沥潭。三九已至,卻和暖如春嬉挡,著一層夾襖步出監(jiān)牢的瞬間钝鸽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工庞钢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拔恰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓基括,卻偏偏與公主長(zhǎng)得像颜懊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子风皿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評(píng)論 25 707
  • 參考谷歌官方文檔 關(guān)于數(shù)據(jù)保存的說明進(jìn)行摘要概述 原文地址參考鏈接地址: 谷歌官方數(shù)據(jù)保存說明主要分為三種方式: ...
    大米愛小蜜閱讀 428評(píng)論 0 0
  • Android存儲(chǔ)挖坑記 最近在搞Android存儲(chǔ)相關(guān)的業(yè)務(wù)饭冬,什么Internal/External/Prima...
    Runxing閱讀 1,276評(píng)論 0 10
  • 用到了要分割字符串,從網(wǎng)上扒了一個(gè)改改揪阶,可以用昌抠。(1)第一種方法啦 從計(jì)數(shù)可以看出有一個(gè)問題,分割到數(shù)組會(huì)自動(dòng)忽略...
    進(jìn)擊的小鹿閱讀 698評(píng)論 0 1
  • 最近看了一些有意思的書裁厅, 不拘于什么類型,能不能看下去侨艾,標(biāo)準(zhǔn)很簡(jiǎn)單执虹, 看完前幾頁(yè),是否還能繼續(xù)唠梨。 好書都是讓人欲罷...
    艾娃微閱讀 263評(píng)論 1 2