如何在Android存讀檔案(kotlin)

如何在Android存讀檔案(kotlin)

Android記憶體

內部儲存記憶體通常是指快閃記憶體 (NAND FLASH),也就是平常我們所說的你的手機是 16G、32G 或 64G 的記憶體 (ROM),而手機另外一種的 1G, 2G, 3G 或 4G 記憶體 (RAM) 是指給 CPU 運算的,這類的記憶體速度會比較快,一些指令要運算會先將資料載入到記憶體內,CPU 可以直接從裡面存取,萬一,記憶體沒有才會從 ROM 裡面讀取,但是相對的速度會比較慢,RAM 在手機關機之後,資料會完全不見,而ROM即使你關機資料也不會消失,另外,還有一種 SDCard 的方式儲存,有些 Android 手機可以擴充 SDCard 來增加儲存空間。

完整程式碼

你可以直接到 GitHub 上面觀看或下載完整程式碼

程式碼說明

Android 檔案是有經過規劃的,通常會被放置在:

內部儲存記憶體

  • 自己的 apk 內。
  • 暫存區。

外部儲存記憶體

  • 公用資料夾 例如 DCIM, Pictures …等。
  • 私有資料夾 例如外部路徑的 Android 資料夾下。

所以我們如果要操作這些資料夾,可以根據以下步驟進行操作,如果要在 Android 內讀寫檔案, 會透過File類別來進行操作。

val = File("路徑", "檔案名稱")

在 Android 中可以選擇把檔案放置在 apk 內,以及系統外部路徑。

apk內部檔案讀取路徑方式

//apk內
context.getFilesDir()

如果你是選擇儲存在內部,只有你的程式才可以存取此檔案,且當你的程式移除後,該檔案也會跟著被移除
儲存空間大小亦受限制。

反之,如果你儲存在外部儲存空間,則無法對該檔案做一切權限上的管理,當程式移除後,資料並不會隨之消失。

但是如果你將資料儲存在外部的以下路徑:

context.getExternalFilesDir();

則在程式移除的時候,仍會將該資料移除,Android 設計一個方式可以讓你將 apk 安裝在外部空間 (如 sdcard )。

android:installLocation = "auto"

儲存在外部空間必須設定存取權限。

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

通常預設會是允許讀取,但是當你宣告寫入權限時,預設讀取權限也會被打開,但是避免後面版本有所變更,建議兩者還是都開啟比較,如果要把字串寫到 apk 內的檔案,可以這樣做。

val fileName = "my_file"
val data = "apk file"
try {
 val outputStream = openFileOutput(fileName, Context.MODE_PRIVATE)
 outputStream.write(data.getBytes())
 outputStream.close()
} catch (Exception e) {
 e.printStackTrace()
}

從模擬器上面 (手機上因為權限問題會看不到),可以看到 /data/data/your_apk/file/ 下面多了一個檔案。


將它 pull 出來的確是我們的 file。


取得該檔案字串。

val fileName = "my_file"
val data = "apk file"
try {  
 val inputStream = openFileInput(fileName)  
 val bytes = ByteArray(1024)  
 val sb = StringBuffer()  
 while (inputStream.read(bytes) != -1) {  
  sb.append(String(bytes))  
 }  
 display_text.text = sb.toString()  
 inputStream.close()  
} catch (e: Exception) {  
 e.printStackTrace()  
}

將 apk 內的檔案讀出來印在我們的 TextView 上面。


暫存的檔案儲存方式

如果要存到暫存資料夾要透過 getCacheDir 這個方法取得路徑,並且透過 createTempFile 方法建立檔案。

val f = File.createTempFile(fileName, null, getCacheDir())

透過這個方法會在 cache 資料夾下產生檔案。

try {  
    fileTmp = File.createTempFile(fileName, null, cacheDir)  
    val output = FileOutputStream(fileTmp)  
    output.write(tmpData.toByteArray())  
    output.close()  
} catch (e: Exception) {  
    e.printStackTrace()  
}

透過這種方式儲存,會存在放在 apk 內的 cache 資料夾,這個資料夾會根據檔案大小以及目前所能配置的記憶體大小來動態配置空間,當系統記憶體不夠配置的時候,會自動刪除該資料夾內的檔案,如果暫時存放的檔案可以透過這個方式進行存放。

先從 Device Monitor 看檔案是否存到 cache 資料夾了,可以看到 /data/data/your_apk/cache/ 下面多了一個檔案。


將它 pull 出來,的確是我們的file。


如果要從程式讀出來,你必須把File物件記住,因為 cache 檔名後面都會加上一些數字,所以,讀取會變成拿建立的 File 物件進行讀取。

try {  
    val inputStream = FileInputStream(fileTmp)  
    val bytes = ByteArray(1024)  
    val sb = StringBuffer()  
    while (inputStream.read(bytes) != -1) {  
        sb.append(String(bytes))  
    }  
    display_text.text = sb.toString()  
    inputStream.close()  
} catch (e: Exception) {  
    e.printStackTrace()  
}

建立外部資料夾

如果要將檔案寫入外部儲存空間,最好先判斷一下外部儲存空間是否可讀寫?

private val isExternalStorageWritable: Boolean  
 get() {  
  val state = Environment.getExternalStorageState()  
  return Environment.MEDIA_MOUNTED == state  
 }

也要判斷外部空間是否可以讀取

private val isExternalStorageReadable: Boolean  
 get() {  
  val state = Environment.getExternalStorageState()  
  return Environment.MEDIA_MOUNTED == state || Environment.MEDIA_MOUNTED_READ_ONLY == state  
 }

所以你可以這樣判斷。

when {  
 isExternalStorageWritable -> //可寫  
  extelnalPublicCreateFoler()  
 isExternalStorageReadable -> {  
  //可讀  
 }  
 else -> {  
  //不可寫不可讀  
 }  
}

由於每支手機的外部儲存空間都不一樣,因此,你不能把路徑寫死,必須透過以下程式碼進行存取。

val path = Environment.getExternalStorageDirectory().getPath()

但是 API 8 以後將外部資料夾分成公用跟私有的,因此你要改用 Environment.getExternalStoragePublicDirectorygetExternalFilesDir 來存取。

  • 建立公用資料夾
    一般會被放在外部路徑的 DCIM、ALARM、PICTURES …等。

Environment 有一些參數可以參考。


//公用
private fun getExtermalStoragePublicDir(albumName: String): File {  
 val file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)  
 if (file.mkdir()) {  
  val f = File(file, albumName)  
  if (f.mkdir()) {  
   return f  
  }  
 }  
 return File(file, albumName)  
}

當我們執行完下面的程式以後。

val dir = getExtermalStoragePublicDir("aa")  
val f = File(dir.path, fileName)  
try {  
 val outputStream = FileOutputStream(f)  
 outputStream.write(data.toByteArray())  
 outputStream.close()  
} catch (e: Exception) {  
 e.printStackTrace()  
}

就可以看到以下結果。


  • 建立私有資料夾
    會被放在外部路徑的 Android 資料夾內,再根據專案路徑/名稱進行分類。
    如下面程式碼所示:
//私有
private fun getExtermalStoragePrivateDir(albumName: String): File {  
 val file = File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), albumName)  
 if (!file.mkdirs()) {  
  Log.e("", "Directory not created or exist")  
 }  
 return file  
}
val dir = getExtermalStoragePrivateDir("bb")  
val f = File(dir, fileName)  
try {  
 val outputStream = FileOutputStream(f)  
 outputStream.write(data.toByteArray())  
 outputStream.close()  
} catch (e: Exception) {  
 e.printStackTrace()  
}

當我們執行完以上的程式以後,就可以看到以下結果。


如果你想在 app 被刪除時,儲存的檔案一起刪除,則使用外部私有資料夾,否則使用公用資料夾會是比較恰當。

檢查空間

要檢查儲存空間是否足夠,可以透過以下程式碼。

getFreeSpace()
getTotalSpace()

刪除檔案

若要刪除檔案可以執行。

myFile.delete()

若要刪除內部空間檔案則可以使用以下程式碼。

myContext.deleteFile(fileName)

以上就是簡單的檔案存讀方式了。