External Storage

Introduction

In the realm of Android development, handling external storage is a crucial skill. External storage refers to the shared storage space on an Android device where apps can save and access files. This storage is typically found in directories like /sdcard or /storage, and files saved here are accessible to users and other applications.

Understanding how to read from and write to external storage is essential for creating applications that require data persistence, such as note-taking apps, file managers, or any app that handles user-generated content. In this tutorial, we’ll cover how to perform these operations using Kotlin, along with necessary permissions and best practices.

Concept Explanation

Imagine external storage as a communal library where anyone can put books (files) and take them out. Just as you need permission to enter the library and borrow a book, your app needs specific permissions to access external storage.

In Kotlin, reading from and writing to external storage involves interacting with the filesystem using the java.io package. You'll often use classes like File, FileInputStream, and FileOutputStream to perform these operations.

Why Access External Storage?

  • User Data Management: Users expect their data to be readily available even if they uninstall or reinstall the app.
  • Inter-app Communication: External storage allows multiple apps to access shared files.
  • Large File Handling: External storage typically offers more space than internal storage.
  • Syntax Section

Before diving into the code, let’s understand the basic syntax involved in file operations:

Writing to a File

You will use FileOutputStream to write content to a file. Here’s the breakdown:

Example

val externalFile = File(getExternalFilesDir(filepath), fileName)
val fileOutputStream = FileOutputStream(externalFile)
fileOutputStream.write(fileContent.toByteArray())
fileOutputStream.close()
  • File: Represents the file you will be writing to.
  • getExternalFilesDir(filepath): Gets the directory for your app’s private files on external storage.
  • FileOutputStream: Opens a stream to write data to the file.
  • Reading from a File

To read content, you will use FileInputStream combined with a BufferedReader:

Example

val externalFile = File(getExternalFilesDir(filepath), fileName)
val fileInputStream = FileInputStream(externalFile)
val bufferedReader = BufferedReader(InputStreamReader(fileInputStream))
val content = bufferedReader.readLine()
bufferedReader.close()
  • BufferedReader: Reads text from an input stream efficiently.
  • readLine: Reads a line of text.
  • Multiple Working Examples

Let's walk through several complete examples, gradually increasing complexity.

Example 1: Writing to External Storage

Example

fun main() {
    val fileName = "example.txt"
    val content = "Hello, this is a test file."
    val externalFile = File(getExternalFilesDir(null), fileName)

    try {
        val fileOutputStream = FileOutputStream(externalFile)
        fileOutputStream.write(content.toByteArray())
        fileOutputStream.close()
        println("File written successfully.")
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

Output:

Output

File written successfully.

Example 2: Reading from External Storage

Example

fun main() {
    val fileName = "example.txt"
    val externalFile = File(getExternalFilesDir(null), fileName)

    try {
        val fileInputStream = FileInputStream(externalFile)
        val bufferedReader = BufferedReader(InputStreamReader(fileInputStream))
        val content = bufferedReader.readLine()
        bufferedReader.close()
        println("File content: $content")
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

Output:

Output

File content: Hello, this is a test file.

Example 3: Writing and Reading with User Input

Example

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import java.io.*

class MainActivity : AppCompatActivity() {
    private val filepath = "MyFileStorage"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val fileNameInput = findViewById<EditText>(R.id.editTextFile)
        val fileDataInput = findViewById<EditText>(R.id.editTextData)
        val saveButton = findViewById<Button>(R.id.button_save)
        val viewButton = findViewById<Button>(R.id.button_view)

        saveButton.setOnClickListener {
            val fileName = fileNameInput.text.toString()
            val fileContent = fileDataInput.text.toString()
            writeFile(fileName, fileContent)
        }

        viewButton.setOnClickListener {
            val fileName = fileNameInput.text.toString()
            readFile(fileName)
        }
    }

    private fun writeFile(fileName: String, content: String) {
        val externalFile = File(getExternalFilesDir(filepath), fileName)
        try {
            FileOutputStream(externalFile).use { it.write(content.toByteArray()) }
            Toast.makeText(this, "File saved successfully", Toast.LENGTH_SHORT).show()
        } catch (e: IOException) {
            e.printStackTrace()
            Toast.makeText(this, "Error saving file", Toast.LENGTH_SHORT).show()
        }
    }

    private fun readFile(fileName: String) {
        val externalFile = File(getExternalFilesDir(filepath), fileName)
        try {
            val fileInputStream = FileInputStream(externalFile)
            val content = BufferedReader(InputStreamReader(fileInputStream)).use { it.readLine() }
            Toast.makeText(this, "File content: $content", Toast.LENGTH_SHORT).show()
        } catch (e: IOException) {
            e.printStackTrace()
            Toast.makeText(this, "Error reading file", Toast.LENGTH_SHORT).show()
        }
    }
}

Expected Output:

  • When saving: File saved successfully
  • When viewing: File content: [content of the file]
  • Example 4: Handling Non-Existent Files

    Example
    
    fun main() {
        val fileName = "non_existent.txt"
        val externalFile = File(getExternalFilesDir(null), fileName)
    
        try {
            val fileInputStream = FileInputStream(externalFile)
            val bufferedReader = BufferedReader(InputStreamReader(fileInputStream))
            val content = bufferedReader.readLine()
            bufferedReader.close()
            println("File content: $content")
        } catch (e: FileNotFoundException) {
            println("File not found: ${e.message}")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
    

Output:

Output

File not found: [specific error message]

Common Mistakes

1. Forgetting Permissions

Mistake: Not declaring necessary permissions in AndroidManifest.xml.

Example

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

Correct Approach: Always include these permissions to access external storage.

2. Not Closing Streams

Mistake: Forgetting to close the FileOutputStream or FileInputStream.

Correct Approach: Use Kotlin's use function to automatically close streams.

Example

FileOutputStream(myExternalFile).use { it.write(fileContent.toByteArray()) }

Best Practices

  • Use try-catch: Always handle exceptions to avoid crashes.
  • Check for Availability: Before reading or writing, check if external storage is available and writable.
  • Use Appropriate File Paths: Utilize app-specific directories with getExternalFilesDir to avoid conflicts with other apps.
  • Practice Exercises

  1. Create a Note-taking App: Build a simple app that allows users to save and view notes using external storage.
  2. File Overwriting: Modify your app to allow users to overwrite existing files or append to them.
  3. File List Viewer: Create a feature that lists all files in your app's external storage directory.

By practicing these exercises, you'll solidify your understanding of reading and writing to external storage in Kotlin. Happy coding!

Input Required

This code uses input(). Please provide values below:

🤖 Coding Mentor
🤖

Hi! I'm your coding mentor

Ask me anything about programming:

• Python, Java, C++, JavaScript

• Algorithms & Data Structures

• Debugging & Code Help