Android存儲(chǔ)使用參考

可能遇到的問題

android系統(tǒng)自身自帶有存儲(chǔ)馁菜,另外也可以通過(guò)sd卡來(lái)擴(kuò)充存儲(chǔ)空間贡珊。前者好比pc中的硬盤最爬,后者好移動(dòng)硬盤。 前者空間較小门岔,后者空間大爱致,但后者不一定可用。 開發(fā)應(yīng)用寒随,處理本地?cái)?shù)據(jù)存取時(shí)糠悯,可能會(huì)遇到這些問題:

  1. 需要判斷sd卡是否可用: 占用過(guò)多機(jī)身內(nèi)部存儲(chǔ),容易招致用戶反感妻往,優(yōu)先將數(shù)據(jù)存放于sd卡;
  • 應(yīng)用數(shù)據(jù)存放路徑互艾,同其他應(yīng)用應(yīng)該保持一致,應(yīng)用卸載時(shí)讯泣,清除數(shù)據(jù):

    • 標(biāo)新立異在sd卡根目錄建一個(gè)目錄纫普,招致用戶反感

    • 用戶卸載應(yīng)用后,殘留目錄或者數(shù)據(jù)在用戶機(jī)器上好渠,招致用戶反感

  • 需要判斷兩者的可用空間: sd卡存在時(shí)昨稼,可用空間反而小于機(jī)身內(nèi)部存儲(chǔ),這時(shí)應(yīng)該選用機(jī)身存儲(chǔ);

  • 數(shù)據(jù)安全性拳锚,本應(yīng)用數(shù)據(jù)不愿意被其他應(yīng)用讀寫;

  • 圖片緩存等假栓,不應(yīng)該被掃描加入到用戶相冊(cè)等媒體庫(kù)中去。

基本操作

  1. 使用外部存儲(chǔ)霍掺,需要的權(quán)限匾荆,在AndoridManifest.xml中:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
> 從API 19 / Andorid 4.4 / KITKAT開始,不再需要顯式聲明這兩個(gè)權(quán)限杆烁,除非要讀寫其他應(yīng)用的應(yīng)用數(shù)據(jù)($appDataDir)
  • 判斷sd卡可用:
/**
 * Check if the primary "external" storage device is available.
 * 
 * @return
 */
public static boolean hasSDCardMounted() {
    String state = Environment.getExternalStorageState();
    if (state != null && state.equals(Environment.MEDIA_MOUNTED)) {
        return true;
    } else {
        return false;
    }
}

存儲(chǔ)的用量情況

  • 根據(jù)系統(tǒng)用戶不同牙丽,所能占用的存儲(chǔ)空間大小也有不同

    在API level 9及其以上時(shí),File對(duì)象的getFreeSpace()方法獲取系統(tǒng)root用戶可用空間连躏;
    getUsableSpace()取非root用戶可用空間

  • 當(dāng)有多個(gè)存儲(chǔ)可用時(shí)獲取磁盤用量剩岳,根據(jù)當(dāng)前系統(tǒng)情況選用合適的存儲(chǔ)贞滨。

  • 根據(jù)系統(tǒng)存儲(chǔ)用量入热,合理設(shè)定app所用的空間大小晓铆;運(yùn)行時(shí)勺良,也可做動(dòng)態(tài)調(diào)整。

  • 在API level 9及其以上的系統(tǒng)骄噪,可直接調(diào)用File對(duì)象的相關(guān)方法尚困,以下需自行計(jì)算:

@TargetApi(VERSION_CODES.GINGERBREAD)
public static long getUsableSpace(File path) {
    if (path == null) {
        return -1;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
        return path.getUsableSpace();
    } else {
        if (!path.exists()) {
            return 0;
        } else {
            final StatFs stats = new StatFs(path.getPath());
            return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();
        }
    }
}

路徑的規(guī)律

一般地,通過(guò)Context 和Environment`相關(guān)的方法獲取文件存取的路徑链蕊。

通過(guò)這兩個(gè)類可獲取各種路徑事甜,如圖:

    ($rootDir)
+- /data                -> Environment.getDataDirectory()
|   |
|   |   ($appDataDir)
|   +- data/com.srain.cube.sample
|       |
|       |   ($filesDir)
|       +- files            -> Context.getFilesDir() / Context.getFileStreamPath("")
|       |       |
|       |       +- file1    -> Context.getFileStreamPath("file1")
|       |   ($cacheDir)
|       +- cache            -> Context.getCacheDir()
|       |
|       +- app_$name        ->(Context.getDir(String name, int mode)
|
|   ($rootDir)
+- /storage/sdcard0     -> Environment.getExternalStorageDirectory()
    |                       / Environment.getExternalStoragePublicDirectory("")
    |
    +- dir1             -> Environment.getExternalStoragePublicDirectory("dir1")
    |
    |   ($appDataDir)
    +- Andorid/data/com.srain.cube.sample
        |
        |   ($filesDir)
        +- files        -> Context.getExternalFilesDir("")
        |   |
        |   +- file1    -> Context.getExternalFilesDir("file1")
        |   +- Music    -> Context.getExternalFilesDir(Environment.Music);
        |   +- Picture  -> ... Environment.Picture
        |   +- ...
        |
        |   ($cacheDir)
        +- cache        -> Context.getExternalCacheDir()
        |
        +- ???

各個(gè)路徑的特性

下面介紹這些路徑的特性以及使用中需要注意的細(xì)節(jié):

  1. 根目錄($rootDir):

    內(nèi)部存儲(chǔ)路徑: /data, 通過(guò)Environment.getDataDirectory() 獲取
    外部存儲(chǔ)路徑: /storage/sdcard0 (也有類似 /mnt/ 這樣的),通過(guò)Environment.getExternalStorageDirectory()獲取

    示例:

    Environment.getDataDirectory(): 
            /data
    
    Environment.getExternalStorageDirectory(): 
            /storage/sdcard0
    
  • 應(yīng)用數(shù)據(jù)目錄($appDataDir)

    • 內(nèi)部?jī)?chǔ)存: $appDataDir = $rootDir/data/$packageName,
    • 外部存儲(chǔ): $appDataDir = $rootDir/Andorid/data/$packageName
      在這些目錄下的數(shù)據(jù)谬泌,在app卸載之后,會(huì)被系統(tǒng)刪除逻谦,我們應(yīng)將應(yīng)用的數(shù)據(jù)放于這兩個(gè)目錄中掌实。
  • 外部存儲(chǔ)中,公開的數(shù)據(jù)目錄邦马。 這些目錄將不會(huì)隨著應(yīng)用的刪除而被系統(tǒng)刪除贱鼻,請(qǐng)斟酌使用:

    Environment.getExternalStorageDirectory(): 
        /storage/sdcard0
    
    // 同 $rootDir
    Environment.getExternalStoragePublicDirectory(""): 
        /storage/sdcard0
    
    Environment.getExternalStoragePublicDirectory("folder1"): 
        /storage/sdcard0/folder1
    
  • 應(yīng)用數(shù)據(jù)目錄下的目錄

    一般的在$appDataDir下,會(huì)有兩個(gè)目錄:

    1. 數(shù)據(jù)緩存:$cacheDir = $appDataDir/cache:

      • 內(nèi)部存儲(chǔ):Context.getCacheDir(), 機(jī)身內(nèi)存不足時(shí)滋将,文件會(huì)被刪除
      • 外部存儲(chǔ):Context.getExternalCacheDir()

      外部存儲(chǔ)沒有實(shí)時(shí)監(jiān)控邻悬,當(dāng)空間不足時(shí),文件不會(huì)實(shí)時(shí)被刪除随闽,可能返回空對(duì)象

      示例:

      Context.getCacheDir(): 
              /data/data/com.srain.cube.sample/cache
      
      Context.getExternalCacheDir(): 
              /storage/sdcard0/Android/data/com.srain.cube.sample/cache
      
    • 文件目錄 $filesDir = $appDataDir/files:

      • 內(nèi)部存儲(chǔ):通過(guò)Context.getFilesDir() 獲取

      Context.getFileStreamPath(String name)返回以name為文件名的文件對(duì)象父丰,name為空,則返回 $filesDir 本身

      示例:

      Context.getFilesDir(): 
              /data/data/com.srain.cube.sample/files
      
      Context.getFileStreamPath(""):
              /data/data/com.srain.cube.sample/files
      
      Context.getFileStreamPath("file1"):
              /data/data/com.srain.cube.sample/files/file1
      
      • 外部存儲(chǔ):通過(guò)Context.getExternalFilesDir(String type), type為空字符串時(shí)獲取.

      type系統(tǒng)指定了幾種類型:

      Environment.DIRECTORY_MUSIC
      Environment.DIRECTORY_PICTURES
      ...
      

      示例:

      Context.getExternalFilesDir(""): 
              /storage/sdcard0/Android/data/com.srain.cube.sample/files
      
      Context.getExternalFilesDir(Environment.DIRECTORY_MUSIC)
              /storage/sdcard0/Android/data/com.srain.cube.sample/files/Music
      
  • $cacheDir / $filesDir 安全性

在內(nèi)部存儲(chǔ)中掘宪,$cacheDir, $filesDir是app安全的础米,其他應(yīng)用無(wú)法讀取本應(yīng)用的數(shù)據(jù),而外部存儲(chǔ)則不是添诉。

在外部存儲(chǔ)中屁桑,這兩個(gè)文件夾其他應(yīng)用程序也可訪問。

在外部存儲(chǔ)中栏赴,$filesDir中的媒體文件蘑斧,不會(huì)被當(dāng)做媒體掃描出來(lái),加到媒體庫(kù)中须眷。

  • $cacheDir / $filesDir 同級(jí)目錄

在內(nèi)部存儲(chǔ)中:通過(guò) Context.getDir(String name, int mode)可獲取和 $filesDir / $cacheDir 同級(jí)的目錄

目錄的命名規(guī)則為 app_ + name, 通過(guò)mode可控制此目錄為app私有還是其他app可讀寫竖瘾。

示例:

Context.getDir("dir1", MODE_PRIVATE):
        Context.getDir: /data/data/com.srain.cube.sample/app_dir1
  • 特別注意, 對(duì)于外部存儲(chǔ),獲取$cacheDir 或者 $filesDir及其下的路徑

在API level 8 以下花颗,或者空間不足捕传,相關(guān)的方法獲路徑為空時(shí),需要自己構(gòu)造扩劝。

@TargetApi(VERSION_CODES.FROYO)
public static File getExternalCacheDir(Context context) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO)) {
        File path = context.getExternalCacheDir();

        // In some case, even the sd card is mounted,
        // getExternalCacheDir will return null
        // may be it is nearly full.
        if (path != null) {
            return path;
        }
    }

    // Before Froyo or the path is null,
    // we need to construct the external cache folder ourselves
    final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
    return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庸论,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子棒呛,更是在濱河造成了極大的恐慌聂示,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件簇秒,死亡現(xiàn)場(chǎng)離奇詭異鱼喉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門扛禽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锋边,“玉大人,你說(shuō)我怎么就攤上這事编曼〕枘” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵灵巧,是天一觀的道長(zhǎng)搀矫。 經(jīng)常有香客問我,道長(zhǎng)刻肄,這世上最難降的妖魔是什么瓤球? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮敏弃,結(jié)果婚禮上卦羡,老公的妹妹穿的比我還像新娘。我一直安慰自己麦到,他們只是感情好绿饵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓶颠,像睡著了一般拟赊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粹淋,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天吸祟,我揣著相機(jī)與錄音,去河邊找鬼桃移。 笑死屋匕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的借杰。 我是一名探鬼主播过吻,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蔗衡!你這毒婦竟也來(lái)了纤虽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粘都,失蹤者是張志新(化名)和其女友劉穎廓推,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翩隧,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了堆生。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片专缠。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖淑仆,靈堂內(nèi)的尸體忽然破棺而出涝婉,到底是詐尸還是另有隱情,我是刑警寧澤蔗怠,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布墩弯,位于F島的核電站,受9級(jí)特大地震影響寞射,放射性物質(zhì)發(fā)生泄漏渔工。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一桥温、第九天 我趴在偏房一處隱蔽的房頂上張望引矩。 院中可真熱鬧,春花似錦侵浸、人聲如沸旺韭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)区端。三九已至,卻和暖如春澳腹,著一層夾襖步出監(jiān)牢的瞬間珊燎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工遵湖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悔政,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓延旧,卻偏偏與公主長(zhǎng)得像谋国,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迁沫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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