Smartphones

Android gives developers direct I/O to the SD card

Follow this tutorial on how to access the SD card on an Android device, and perform simple read and write operations.

One of the best things about the Android platform is that it tends to be less restrictive than other smartphone operating systems. Sure, Android has an easy to use preference system that allows you to keep track of key value pairs, but if you need a more flexible approach, Android also allows you to read and write directly to a removable SD card. Take that iOS!

The example that follows shows both how to access the SD card on an Android device, and perform simple read and write operations. You can follow along with the step-by-step tutorial, or download and import the entire project into Eclipse. While the code may look a little intimidating to beginners, those familiar with Java will notice that very little of the code is Android specific. In other words, manipulating files on Android is very much like manipulating files on any platform where Java runs.

1. Start a new Android project in Eclipse. Target Android 1.6 or higher. Be sure to rename the startup file Main.java.

2. Add permissions to the manifest. Open /res/AndroidManifest.xml and add both read and write permissions to the project.

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.authorwjf.direct_file_io"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="4"
        android:targetSdkVersion="15" />
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
   <application
       android:icon="@drawable/ic_launcher"
       android:label="@string/app_name"
       android:theme="@style/AppTheme" >
       <activity
           android:name=".Main"
           android:label="@string/title_activity_main" >
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />
               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
        </activity>
    </application>
</manifest>

3. Moving on to the /res/layout folder, you will want to define your main.xml layout file. The layout for this project is a straightforward linear layout that contains two edit text fields and three buttons.

main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="#000000" >
    <EditText
        android:layout_margin="10dip"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:hint="enter some text"
        android:id="@+id/first_input_line"/>
    <EditText
        android:layout_margin="10dip"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:hint="enter some more text"
        android:id="@+id/second_input_line"/>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal" >
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/clear_button"
        android:text="Clear"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/load_button"
        android:text="Load"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/save_button"
        android:text="Save"/>
</LinearLayout>
</LinearLayout>

4. Now its time to write the Main.java code. Start with some includes, overriding the on start to wire up the three buttons, and a call back to handle the button presses.

Main.java

package com.authorwjf.direct_file_io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.Toast;

public class Main extends Activity implements OnClickListener{
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
       findViewById(R.id.clear_button).setOnClickListener(this);
       findViewById(R.id.save_button).setOnClickListener(this);
       findViewById(R.id.load_button).setOnClickListener(this);
   }
       @Override
       public void onClick(View v) {
              switch(v.getId()) {
              case R.id.clear_button:
                     ((EditText) findViewById(R.id.first_input_line)).setText("");
                 ((EditText) findViewById(R.id.second_input_line)).setText("");
                    break;
              case R.id.load_button:
                     load();
                     break;
              case R.id.save_button:
                      save();
                      break;
              }
       }
}

5. We will implement three private functions to handle the file I/O. Except for the calls to get the external storage system directory, these are standard Java techniques for creating a file if it doesn't exist (ensure), and then reading and writing.

Main.java

private void ensure() {
      File sdcard = Environment.getExternalStorageDirectory();
          File file = new File(sdcard,"/my_file.txt");
          if(!file.exists()) {
             try {
                             file.createNewFile();
                      } catch (IOException e) {
                             Toast.makeText(getApplicationContext(),"Error creating file!",Toast.LENGTH_LONG).show();
          }
      }
   }
private void load() {
      ensure();
      File sdcard = Environment.getExternalStorageDirectory();
      File file = new File(sdcard,"/my_file.txt");
      try {
              BufferedReader br = new BufferedReader(new FileReader(file));
              String line;
              if ((line = br.readLine()) != null) {
                     String[] content=line.toString().split(":");
                         ((EditText)
findViewById(R.id.first_input_line)).setText(content[0]);
                         ((EditText)
findViewById(R.id.second_input_line)).setText(content[1]);
               }
               br.close();
          }
          catch (IOException e) {
             Toast.makeText(getApplicationContext(),"Error reading file!",Toast.LENGTH_LONG).show();
           }
      }
private void save() {
       ensure();
       String firstLine = ((EditText)
findViewById(R.id.first_input_line)).getText().toString().trim();
       String secondLine = ((EditText)
findViewById(R.id.second_input_line)).getText().toString().trim();
       File sdcard = Environment.getExternalStorageDirectory();
       File file = new File(sdcard,"/my_file.txt");
       try {
              FileWriter fw = new FileWriter(file);
              BufferedWriter bw = new BufferedWriter(fw);
              bw.write(firstLine+":"+secondLine);
              bw.close();
       } catch (IOException e) {
             Toast.makeText(getApplicationContext(),"Error writing file!",Toast.LENGTH_LONG).show();
     }
}
We are almost ready to run the project. If you are loading it directly to your device, you don't need to do anything else. If you plan to test it on the emulator, you will want to go in and edit your AVD profile to include SD storage (Figure A). Figure A

Once you have the app on your phone or emulator, the rest should be pretty self-explanatory. You can type some input into either of the two edit text fields and then press Save. Pressing Clear, (you guessed it) clears the fields. You can use the Load button to repopulate the form with previously saved values. Of course saving two lines of text directly to the SD card is overkill, but the file I/O concepts will scale easily to meet any number of real-world scenarios. (Figure B) Figure B

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

2 comments
nhoeller
nhoeller

Does this technique work with Android 4? Google created a new WRITE_MEDIA_STORAGE permission to control write access to external SD cards but only appears to grant the permission to systems applications like File Manager and Gallery.