Android文件讀寫探索

最近碰到一個項目要用到Android文件的存取昼牛,之前也一直沒有完全搞清楚统台。最近整理了一下給大家一起分享,也希望大家如果遇到這方面的問題少走一些彎路冰更。

前面兩小節(jié)主要是一些java的基礎(chǔ)如果基礎(chǔ)比較好的同學(xué)可以直接跳過看后面兩節(jié)产徊。
Demo下載地址

關(guān)于Java I/O流

為什么講這個是因為我覺得會有很多和我一樣Java基礎(chǔ)不扎實的小伙伴在看到j(luò)ava.io下面這么多輸入輸出流類實在是沒有頭緒不知道該用哪個,所以我這里通過查閱的資料給大家大概講一講蜀细。
Java中的流是對字節(jié)序列的抽象舟铜,可作為一個輸入源,也可作為一個輸出的目的地奠衔。主要有兩種:字節(jié)流字符流谆刨。

  • 字節(jié)流處理的基本單位為單個字節(jié)塘娶,通常用來處理二進(jìn)制數(shù)據(jù)也就是說我們用它來處理文件。
  • 字符流最基本的單元是Unicode碼元(大小2字節(jié))痊夭,通常用來處理文本數(shù)據(jù)刁岸。

JDK所提供的所有流類位于java.io包中,都分別繼承自以下四種抽象流類:
InputStream:繼承自InputStream的流都是用于向程序中輸入數(shù)據(jù)的她我,且數(shù)據(jù)單位都是字節(jié)(8位)难捌。
OutputSteam:繼承自O(shè)utputStream的流都是程序用于向外輸出數(shù)據(jù)的,且數(shù)據(jù)單位都是字節(jié)(8位)鸦难。
Reader:繼承自Reader的流都是用于向程序中輸入數(shù)據(jù)的根吁,且數(shù)據(jù)單位都是字符(16位)。
Writer:繼承自Writer的流都是程序用于向外輸出數(shù)據(jù)的合蔽,且數(shù)據(jù)單位都是字符(16位)击敌。

這樣大家應(yīng)該再看這些class的時候就大概有方向了。

File類

既然我們要進(jìn)行文件讀寫那我們就要對我們操作的類深入理解拴事。

An abstract representation of file and directory pathnames.User interfaces and operating systems use system-dependent pathname strings to name files and directories. This class presents an abstract, system-independent view of hierarchical pathnames.

這個是官方API的解釋沃斤,大概翻譯一下就是:
這個類是文件和目錄路徑名的抽象表示。它提供了一個抽象的獨立系統(tǒng)分層的路徑名視圖刃宵。用戶界面和操作系統(tǒng)使用與系統(tǒng)相關(guān)的路徑名字符串來命名文件和目錄衡瓶。

為什么說與系統(tǒng)相關(guān)呢。主要有兩個

  1. 一個是依賴系統(tǒng)的前綴字符串牲证,例如磁盤驅(qū)動說明符哮针,UNIX根目錄為“/”等等。
  2. 另一個就是分割符了這個很顯然也跟系統(tǒng)相關(guān)坦袍。

當(dāng)然了我們Android不用考慮那么多十厢,我們只需要注意分隔符,而這個分隔符File類也幫我們申請好了File.separator
我們new 一個File官方給了四個構(gòu)造方法File(String pathname)捂齐,File(String parent,String child),File(File parent,String child),FIle(URI uri)蛮放。這四個方法也非常好理解我這里也不詳細(xì)說明了。
FIle類里面的方法也無非是一些對文件的判斷和一些基本操作奠宜,大家可以看一下API文檔包颁,相信機(jī)智的你一定看的懂的。但是有幾點要注意:

  1. 文件是分區(qū)的压真,不同區(qū)域是有不同的權(quán)限娩嚼。這一點我在下一節(jié)將會詳細(xì)講解。
  2. File類的實例是不可變的榴都,也就是說一旦創(chuàng)建待锈,由File對象表示的抽象路徑名將永遠(yuǎn)不會改變漠其。
  3. 在Android上嘴高,文件名的基礎(chǔ)文件系統(tǒng)編碼始終是UTF-8竿音。

Android的內(nèi)部儲存和外部儲存

既然我們要操作文件那我們還要知道我們要把文件放在哪。那文件放在哪里那就跟Android系統(tǒng)有關(guān)了拴驮。Android把我們手機(jī)分為內(nèi)部儲存和外部儲存春瞬。我們來看一下官方的解釋:

Internal storage:

  • It's always available.
  • Files saved here are accessible by only your app.
  • When the user uninstalls your app, the system removes all your app's files from internal storage.

External storage:

  • It's not always available, because the user can mount the external storage as USB storage and in some cases remove it from the device.
  • It's world-readable, so files saved here may be read outside of your control.
  • When the user uninstalls your app, the system removes your app's files from here only if you save them in the directory from getExternalFilesDir()

大家可以自行谷歌翻譯一下,我給大家提煉一下:
內(nèi)部儲存是指只有自己應(yīng)用才能訪問套啤,并且如果應(yīng)用刪除了宽气,那么這些文件也就跟著一起刪除了。

外部儲存是整個手機(jī)都可以訪問的所以在內(nèi)容上它是不穩(wěn)定的潜沦,因為只要有讀寫的權(quán)限就可以修改這些文件萄涯。(關(guān)于權(quán)限我會在最后一節(jié)給大家詳解)而在硬件上來說它也有可能是不穩(wěn)定的(比如sd卡)。

所以這就和大家以前所理解的概念不同了唆鸡,不能單純的以為不是sd卡就是內(nèi)部儲存涝影。

操作內(nèi)部儲存:

獲得內(nèi)部儲存文件夾的方法都在Cntext這個類中,主要有以下幾個方法:

  • getFilesDir():這個返回的是內(nèi)部儲存的根目錄的絕對路徑争占。
  • getCacheDir()這個方法返回的是內(nèi)部儲存緩存目錄的絕對路徑燃逻。
  • getDir(String name, int mode):檢索,根據(jù)需要創(chuàng)建一個新目錄臂痕,應(yīng)用程序可以在其中放置自己的自定義數(shù)據(jù)文件伯襟。后面那個參數(shù)mode一般寫0或者MODE_PRIVATE

給大家看一下這些目錄具體在哪里

Log.e("File","內(nèi)部緩存自定義目錄 : "+getDir("getDir",MODE_PRIVATE));
Log.e("File","內(nèi)部儲存的根目錄 : "+getFilesDir());
Log.e("File","內(nèi)部儲存緩存目錄 : "+getCacheDir());
Log.e("File" ,"內(nèi)部儲存緩存代碼目錄 : "+getCodeCacheDir());

以下是Log:

E/File: 內(nèi)部緩存自定義目錄 : /data/data/com.kachidoki.learnfiletest/app_getDir
E/File: 內(nèi)部儲存的根目錄 : /data/data/com.kachidoki.learnfiletest/files
E/File: 內(nèi)部儲存緩存目錄 : /data/data/com.kachidoki.learnfiletest/cache
E/File: 內(nèi)部儲存緩存代碼目錄 : /data/data/com.kachidoki.learnfiletest/code_cache

這些返回的目錄都是在data/data/包名下握童,這個目錄用戶是不給用戶看的姆怪,你的手機(jī)如果沒有root通過文件管理看不到。

Android給操作內(nèi)部儲存文件提供了一個簡便的方法也在Context這個類中澡绩,FileOutputStream openFileOutput (String name, int mode)片效。這個方法返回一個FileOutputStream方便我們操作。后面那個mode參數(shù)默認(rèn)操作使用0或MODE_PRIVATE表示會創(chuàng)建一個文件或者直接替換原有的文件英古。也可以使用MODE_APPEND直接加到現(xiàn)有文件中淀衣。

下面是操作內(nèi)部儲存的Demo:

/** *  寫在內(nèi)部儲存Demo */private void createPrivateFile(){    
String filename = "myfile.txt";    
String string = "Hello world!";   
FileOutputStream outputStream;    
  try {      
      outputStream = openFileOutput(filename, Context.MODE_PRIVATE);                
      outputStream.write(string.getBytes());    
      outputStream.close();      
      Toast.makeText(MainActivity.this,"文件位置在"+getFilesDir().getAbsolutePath(),Toast.LENGTH_LONG).show();   
   } catch (Exception e) {       
     e.printStackTrace();     
     Toast.makeText(MainActivity.this,"出錯誤了哦",Toast.LENGTH_SHORT).show();   
 }
}

操作外部儲存

外部儲存呢分為兩個部分但是操作是一樣的,功能上有一些區(qū)別:

  • 公共文件
    獲得這些目錄的方法都在Environment這個類中

getExternalStoragePublicDirectory(String type):這是用戶通常放置和管理自己的文件的地方召调,所以你應(yīng)該小心放在這里膨桥,以確保你不要刪除他們的文件或妨礙自己的組織的方式。這里的type可以用DIRECTORY_MUSIC,DIRECTORY_MOVIES這些Environment中的常量唠叛。

Log.e("File","外部儲存的根目錄一般不用:"+Environment.getExternalStorageDirectory());
Log.e("File","外部儲存的公共文件根目錄: "+Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
Log.e("File","外部儲存的狀態(tài): "+Environment.getExternalStorageState());
Log.e("File","下載緩存目錄 :"+Environment.getDownloadCacheDirectory());

Log:

 E/File: 外部儲存的根目錄一般不用:/storage/emulated/0
 E/File: 外部儲存的公共文件根目錄: /storage/emulated/0/Pictures
E/File: 外部儲存的狀態(tài): mounted
 E/File: 下載緩存目錄 :/cache

要寫在這部分需要讀寫的權(quán)限只嚣,并且官方建議用之前先判斷一下外部儲存的狀態(tài),下面是從官方抄來的:

/* 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;
}
  • 私有部分
    獲得這些目錄的方法都在Context艺沼。怎么又回到Context了呢册舞。因為這些文件也會在應(yīng)用刪除的時候跟隨這一起刪掉~只是和內(nèi)部儲存不同的是這個部分可以給用戶和其他應(yīng)用訪問。所以才叫外部儲存的私有部分嘛障般。
Log.e("File","外部儲存私有部分根目錄 : "+ getExternalFilesDir(null));
Log.e("File","外部儲存私有部分音樂 : "+ getExternalFilesDir(Environment.DIRECTORY_MUSIC));
Log.e("File","外部儲存私有部分緩存 : "+getExternalCacheDir());

Log:

E/File: 外部儲存私有部分根目錄 : /storage/emulated/0/Android/data/com.kachidoki.learnfiletest/files
E/File: 外部儲存私有部分音樂 : /storage/emulated/0/Android/data/com.kachidoki.learnfiletest/files/Music
E/File: 外部儲存私有部分緩存 : /storage/emulated/0/Android/data/com.kachidoki.learnfiletest/cache

以下是操作外部儲存的Demo:

/**    
*    寫在外部儲存應(yīng)用專屬位置demo 
*/
public void createExternalStoragePrivateFile(){  
       File file = new File(getExternalFilesDir(null),"demoFile.jpg");  
       try {       
             InputStream is =getResources().openRawResource(R.drawable.supreme);      
             OutputStream os = new FileOutputStream(file);   
             byte[] data = new byte[is.available()];
             is.read(data);
             os.write(data); 
             os.close();
             is.close(); 
             Toast.makeText(MainActivity.this,"圖片位置在"+getExternalFilesDir(null),Toast.LENGTH_LONG).show();
      } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(MainActivity.this,"出錯誤了哦",Toast.LENGTH_SHORT).show();
      }
}
public void deleteExternalStoragePrivateFile() {
    // Get path for the file on external storage.  If external   
   // storage is not currently mounted this will fail.    
  File file = new File(getExternalFilesDir(null), "demoFile.jpg");
      if (file != null) { 
         file.delete();
      }
}

最后呢再給大家摘一段API在介紹Environment.getExternalStorageDirectory()這個方法的時候特地寫的一段文字來加深大家對外部儲存的理解调鲸,大家自己翻譯體會一下:

Note: don't be confused by the word "external" here. This directory can better be thought as media/shared storage. It is a filesystem that can hold a relatively large amount of data and that is shared across all applications (does not enforce permissions). Traditionally this is an SD card, but it may also be implemented as built-in storage in a device that is distinct from the protected internal storage and can be mounted as a filesystem on a computer.

權(quán)限問題

涉及到操作文件肯定會有權(quán)限的問題盛杰,所以這里我是一定要講滴。

  • 內(nèi)部儲存:無需任何權(quán)限藐石,即可在內(nèi)部存儲中保存文件即供。 您的應(yīng)用始終具有在其內(nèi)部存儲目錄中進(jìn)行讀寫的權(quán)限。

  • 外部儲存私有部分:以Context.getExternalFilesDir為首的一系列方法于微,從Android4.4開始不需要WRITE_EXTERNAL_STORAGE和/或READ_EXTERNAL_STORAGE權(quán)限逗嫡。(之前的版本還是需要)
    但是,如果要訪問其他軟件的外部儲存的私有部分那就還是需要這兩個權(quán)限(前提是你要知道別的包名株依,就是路徑嘛)驱证。

  • 外部儲存公共部分:這部分肯定是需要權(quán)限的嘛~~~~就是需要在manifest聲明READ_EXTERNAL_STORAGE,WRITE_EXTERNAL_STORAGE
    :如果您的應(yīng)用使用 WRITE_EXTERNAL_STORAGE權(quán)限恋腕,那么它也隱含讀取外部存儲的權(quán)限雷滚。

還有一點要注意,如果你的app的targetSdkVersion大于等于23吗坚,也就是說大于Android6.0的版本那你還要在讀寫文件之前還要動態(tài)申請Runtime權(quán)限祈远,否則你的程序默認(rèn)是沒有讀寫權(quán)限的。關(guān)于這個如果不了解的可以看一下下面這篇文章寫的比較易懂:
聊一聊Android 6.0的運行時權(quán)限

最后

放一下文章里的Demo商源,我的Demo里面還有運用Retrofit+Rxjava實現(xiàn)下載文件并顯示進(jìn)度的例子哦车份,如果也有小伙伴正在為這個苦惱的話可以參考一下~
Demo下載地址

本人水平有限,如果文章中出現(xiàn)什么問題歡迎及時指出大家一起探討一同進(jìn)步牡彻,有什么問題可以留言扫沼,可以私信。


參考文檔:
保存文件
Context.Class
Environment.Class
File.Class
android中的文件操作詳解以及內(nèi)部存儲和外部存儲

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庄吼,一起剝皮案震驚了整個濱河市缎除,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌总寻,老刑警劉巖器罐,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異渐行,居然都是意外死亡轰坊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進(jìn)店門祟印,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肴沫,“玉大人,你說我怎么就攤上這事蕴忆〔遥” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長站蝠。 經(jīng)常有香客問我汰具,道長,這世上最難降的妖魔是什么沉衣? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮减牺,結(jié)果婚禮上豌习,老公的妹妹穿的比我還像新娘。我一直安慰自己拔疚,他們只是感情好肥隆,可當(dāng)我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稚失,像睡著了一般栋艳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上句各,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天吸占,我揣著相機(jī)與錄音,去河邊找鬼凿宾。 笑死矾屯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的初厚。 我是一名探鬼主播件蚕,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼产禾!你這毒婦竟也來了排作?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤亚情,失蹤者是張志新(化名)和其女友劉穎妄痪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體楞件,經(jīng)...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡拌夏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了履因。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片障簿。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖栅迄,靈堂內(nèi)的尸體忽然破棺而出站故,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布西篓,位于F島的核電站愈腾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏岂津。R本人自食惡果不足惜虱黄,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吮成。 院中可真熱鬧橱乱,春花似錦、人聲如沸粱甫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茶宵。三九已至危纫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間乌庶,已是汗流浹背种蝶。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留瞒大,地道東北人蛤吓。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像糠赦,于是被迫代替她去往敵國和親会傲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,446評論 2 359

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