You are on page 1of 19

How to read a selected text file from sdcard on android

up vote54down votefavorite
22
I am new to Android development.
I need to read a text file from the SD card and display that text file. Is there
any way to view a text file directly in Android or else how can I read and disp
lay the contents of a text file?
android android-sdcard
shareimprove this question
edited Jul 1 '11 at 8:07
Miuranga
1,03672954
asked May 25 '10 at 7:12
RSSS
321245

do you want to know how to write a program that reads a txt file or do y
ou want to know how to do it as a user? SteelBytes May 25 '10 at 7:28
add a comment

4 Answers
activeoldestvotes
up vote113down vote
In your layout you'll need something to display the text
. A TextView is the obvious choice. So you'll have something like this:
<TextView
android:id="@+id/text_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
And your code will look like this:
//Find the directory for the SD Card using the API
//*Don't* hardcode "/sdcard"
File sdcard = Environment.getExternalStorageDirectory();
//Get the text file
File file = new File(sdcard,"file.txt");
//Read text from file
StringBuilder text = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while ((line = br.readLine()) != null) {
text.append(line);
text.append('\n');
}
br.close();
}
catch (IOException e) {
//You'll need to add proper error handling here

}
//Find the view by its id
TextView tv = (TextView)findViewById(R.id.text_view);
//Set the text
tv.setText(text);
This could go in the onCreate() method of your Activity, or somewhere else depen
ding on just what it is you want to do.
shareimprove this answer
edited May 13 '14 at 10:11
AlvaroSantisteban
1,84431938
answered May 25 '10 at 9:27
Dave Webb
127k40253269

Not working it returns text as null

Amit Thaper Jul 21 '11 at 8:27

1
@Android Developer - In the sample code it says "You'll need to add prop
er error handling here". Did you do that because that's probably the mostly like
place you'll find your problem. Dave Webb Jul 21 '11 at 15:01
@Dave Webb sorry for the not update my comment. it works for me also. Ac
tually when i put the file in the DDMS then it will clear all the data from the
file thats why it returns null at my end.
Amit Thaper Jul 22 '11 at 4:04
Will this work using a shared network file? I want to file a file in a s
hared file over the internet, the would the file directory look like, "192.168.1
97.84/hdd1", "ActivityLog.txt" Michael Zeuner Jul 23 '12 at 13:41
1
@DaveWebb: you should also add uses permission : READ_EXTERNAL_STORAGE i
n your manifest file Houcine Mar 15 '13 at 12:35
show 1 more comment

up vote5down vote
In response to
Don't hardcode /sdcard/
Sometimes we HAVE TO hardcode it as in some phone models the API method returns
the internal phone memory.
Known types: HTC One X and Samsung S3.
Environment.getExternalStorageDirectory().getAbsolutePath() gives a different pa
th - Android
shareimprove this answer
answered Jun 21 '13 at 6:16
Sibbs Gambling
3,4421047107

for me it gives some security agumentation junk /com.app.name/data/sdcar


d/ ... be aware. for logs for myself i hard code it at last it works.
Shimon Doo
dkin Jan 29 '15 at 0:43
add a comment

up vote0down vote

package com.example.readfilefromexternalresource;

import
import
import
import
import

java.io.BufferedReader;
java.io.File;
java.io.FileNotFoundException;
java.io.FileReader;
java.io.IOException;

import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.app.ActionBar;
android.app.Fragment;
android.os.Bundle;
android.os.Environment;
android.view.LayoutInflater;
android.view.Menu;
android.view.MenuItem;
android.view.View;
android.view.ViewGroup;
android.widget.TextView;
android.widget.Toast;
android.os.Build;

public class MainActivity extends Activity {


private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView = (TextView)findViewById(R.id.textView);
String state = Environment.getExternalStorageState();
if (!(state.equals(Environment.MEDIA_MOUNTED))) {
Toast.makeText(this, "There is no any sd card", Toast.LENGTH_LONG).s
how();
} else {
BufferedReader reader = null;
try {
Toast.makeText(this, "Sd card available", Toast.LENGTH_LONG).sho
w();
File file = Environment.getExternalStorageDirectory();
File textFile = new File(file.getAbsolutePath()+File.separator +
"chapter.xml");
reader = new BufferedReader(new FileReader(textFile));
StringBuilder textBuilder = new StringBuilder();
String line;
while((line = reader.readLine()) != null) {
textBuilder.append(line);
textBuilder.append("\n");
}
textView.setText(textBuilder);
} catch (FileNotFoundException e) {
// TODO: handle exception
e.printStackTrace();

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if(reader != null){
try {
reader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
shareimprove this answer
answered Aug 7 '14 at 6:04
vikseln
41438
add a comment
up vote0down vote
BufferedReader br = null;
try {
String fpath = Environment.getExternalStorageDirectory() + <your file na
me>;
try {
br = new BufferedReader(new FileReader(fpath));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
String line = "";
while ((line = br.readLine()) != null) {
//Do something here
}
shareimprove this answer
answered May 13 '15 at 11:11
RSH
135

Can you provide an explanation to your code please.. like where to use i
t, why does that do the job, etc.
Soma May 13 '15 at 11:32
add a comment

0 0 3

Each android device supports external storage (SD card) which can be used direct
ly by user to save multimedia content as well as by an app to save it's data. Si
nce the Internal storage of a device is very limited so it becomes a necessity f
or an app to transfer the large files to SD card.
Downloading Files in Android:
In Android version 2.3 Gingerbread (API level 9), a new service was added to and
roid OS "DownloadManager"specially for the purpose of downloading files througho
ut HTTP requests. It automatically manages the Progress bar in Notification, lar
ge sized Files, Retry Download if fail or even after the device reboots. So, cur
rently it's the best solution for downloading files. Below are the steps to crea
te a simple application for testing purpose:
1. Create a new Android project in Eclipse.
2. Select Blank Activity.
3. In Activity_main.xml file, Place the below code.
a.) One button to start downloading a file throughout internet.
b.) Another button to Generate the list of files present in app directory.
c.) Edit Text to display the list of files in app directory.
4. In your MainActivity.java file, paste the below code in OnCreate method of Ac
tivity
5. Paste the below two functions in same MainActivity class.
a) isDownloadManagerAvailable Function queries and returns a boolean indicating
whether the Download Manager Service for device's OS version or not (not availab
le in Android version less than 2.3).
b) IF the above function returns TRUE, only then we can use the downloadFile met
hod.
c) downloadFile function makes use of the DownloadManager manager to enqueue the
download request and the process starts.
d) Notice this line:
It simply places the file "sample.pdf" in your "/sdcard/Android/data/Your_Projec
ts_PackageName/files/" directory.This directory is private to your app only. It
will create this directory if doesn't exist already.
e) If You want the downloaded file to go to the default "Downloads" directory of
SD card, replace the above line with below line:
It's a public directory.
6. Now, Lastly, define the below two permissions in your AndroidManifest.xml fil
e:
7. Now, Either create Emulator with SD card or test it on device. After giving p
roper download URL and Clicking on "Download File" button, downloading will star
t and you can see the progress in Notification bar like screenshot below.
Retrieving Files from SD card:
1. There are several ways to retrieve the list of files present in a particular
directory of SD card. We'll be using the simplest approach.
2. Place the below code in your MainActivity.java file's OnCreate method of main
activity:
3. The Above code simply generates the list of files present in app's data folde
r and displays the list in Edit Text. (if there is no permission or access error
).

Accessing the File System


Android devices are computers, with file systems that can be accessed using many
of the same methods as for standard computers (see Transferring Files). In this
project we give two examples of using the Android file system.
1.
Writing to files on the SD card or other external media.
2.
Reading data at runtime from static (read-only) files that are packaged
with an application.
In later projects we shall use these methods and others to give additional examp
les of storing and retrieving information.
Creating the Project in Android
Following the general procedure
new Android Studio projectfrom
Studio interface choose File >
sulting screens as follows,

Studio
in Creating a New Project, either choose Start a
the Android Studio homepage, or from the Android
New > New Project. Fill out the fields in the re

Application Name: WriteSDCard


Company Domain:< YourNamespace >
Package Name: <YourNamespace> . writesdcard
Project Location: <ProjectPath> WriteSDCard
Target Devices: Phone and Tablet; Min SDK API 15
Add an Activity: Empty Activity
Activity Name: MainActivity (check the Generate Layout File box)
Layout Name: activity_main
where you should substitute your namespace for <YourNamespace> (com.lightcone in
my case) and<ProjectPath> is the path to the directory where you will store thi
s Android Studio Project (/home/guidry/StudioProjects/ in my case). If you have
chosen to use version control for your projects, go ahead and commit this projec
t to version control.
Filling out the Code
We first insert all the Java and XML that we shall need and then we shall explai
n its functionality.
The XML Files
Edit the res/values/strings.xml file to read
<resources>
<string name="app_name">WriteSDCard</string>
<string name="action_settings">Settings</string>
<string name="hello">FILE SYSTEM I/O</string>
</resources>
and edit the res/layout/activity_main.xml file so that it reads:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<TextView
android:id="@+id/TextView01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="15sp"
android:textColor="@color/colorPrimary"
android:text="@string/hello" />
</RelativeLayout>
The Manifest File
Next, we must modify the manifest file because we are going to need explicit per
mission to write to external files. Open AndroidManifest.xml and edit it to read
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="<YourNamespace>.writesdcard">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
where the added permission WRITE_EXTERNAL_STORAGE is highlighted in red.
Before Android 4.4 (API 19), reading files on the external file system required
theREAD_EXTERNAL_STORAGE permission, and writing to external storage required th
eWRITE_EXTERNAL_STORAGE. (If you needed to do both, the read permission is impli
cit in the write permission, so it was only necessary to acquire the write permi
ssion in that case.)
Beginning with Android 4.4, these permissions are not required if you are are re
ading or writing only files that are private to your app. This is why the androi
d:maxSdkVersion="18" attribute is included in the permission request in the mani
fest file. We shall explain this app-specific storage that does not require writ
e permission further below . You should be aware that there are some limitations
associated with this convenience. For example, if the user uninstalls your app,
all of these app-specific data directories are erased, and the system media sca

nner does not scan these directories, making them inaccessible to the MediaStore
content provider (see saving files that are app-private).
Hence you should not use these app-specific directories for media that belongs u
ltimately to the user. Examples: user photos captured or edited with your app, o
r music the user has purchased with your app. In that case, the files should be
saved in the shared public directories (see saving files that can be shared with
other apps for further information). This project will concentrate on how to sa
ve and read files in the data area specific to this app.
The Java Class Files
Next, open the file java/<YourNamespace>.writesdcard/MainActivity.java and edit
it to read
package <YourNamespace>.writesdcard;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import
import
import
import
import
import
import
import

java.io.BufferedReader;
java.io.File;
java.io.FileNotFoundException;
java.io.FileOutputStream;
java.io.IOException;
java.io.InputStream;
java.io.InputStreamReader;
java.io.PrintWriter;

import android.os.Environment;
import android.util.Log;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MEDIA";
private TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.TextView01);
checkExternalMedia();
writeToSDFile();
readRaw();
}
/**
* Method to check whether external media available and writable. This is ad
apted from
* http://developer.android.com/guide/topics/data/data-storage.html#filesExt
ernal
*/
private void checkExternalMedia() {
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();

if (Environment.MEDIA_MOUNTED.equals(state)) {
// Can read and write the media
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// Can only read the media
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// Can't read or write
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
tv.append("\n\nEXTERNAL MEDIA: readable="
+ mExternalStorageAvailable + " writable=" + mExternalStorageWri
teable);
}
/**
* Method to write ascii text characters to file on SD card. In earlier vers
ions of Android a
* WRITE_EXTERNAL_STORAGE permission must be added to the manifest file or t
his method will throw
* a FileNotFound Exception because you won't have write permission. But not
true after
* API 18 for files in storage area of app (then no write permission require
d).
*/
private void writeToSDFile() {
// Root of the external file system
File root0 = android.os.Environment.getExternalStorageDirectory();
/* Now find the root of the external storage for this app (where the app
can place
* persistent files that it owns internal to the application and not typi
cally visible
* to the user as media). See
*
*
http://developer.android.com/guide/topics/data/data-storage.html#fi
lesExternal
*
* The method getExternalFilesDir (string) returns the user storage assoc
iated with the
* app, which doesn't require write permissions after API 18. The string
argument specifies various
* regions of this storage. For example,
*
* - null specifies the root of the storage for this app
* - Environment.DIRECTORY_NOTIFICATIONS specifies the Notifications dire
ctory of app storage
* - Environvment.DIRECTORY_DOWNLOADS specifies standard directory for fi
les downloaded by user
* - Environment.DIRECTORY_PICTURES specifies standard directory for pict
ures available to the user
* - Environment.DIRECTORY_DOCUMENTS specifies standard directory for doc
uments produced by user
* etc.
*

* See the fields of the Environment class at


*
https://developer.android.com/reference/android/os/Environment.html
* for other possibilities. For example, on my phone (running Android 6.
0.1) the root of
* the user storage for this specific app is found at
*
*
/storage/emulated/0/Android/data/com.lightcone.writesdcard/files
* */
// Root of the data directories Documents subdirectory specific to this
app, for which no write
// permission is required for Android 4.4 and later.
File root = this.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
tv.append("\n\nEXTERNAL FILE SYSTEM ROOT DIRECTORY:\n" + root0);
tv.append("\n\nEXTERNAL APP DATA ROOT DIRECTORY:\n" + root);
// Create a Documents/download subdirectory in the data area for this ap
p
// See http://stackoverflow.com/questions/3551821/android-write-to-sd-ca
rd-folder
File dir = new File(root.getAbsolutePath() + "/download");
dir.mkdirs();
File file = new File(dir, "myData.txt");
// Must catch FileNotFoundException and IOException
try {
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
pw.println("Howdy do to you,");
pw.println("and the horse you rode in on.");
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i(TAG, "File not found");
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "I/O exception");
}
tv.append("\n\nFILE WRITTEN TO:\n" + file);
}
/**
* Method to read in a text file placed in the res/raw directory of the appl
ication. The
* method reads in all lines of the file sequentially.
*/
private void readRaw() {
tv.append("\n\nDATA READ FROM res/raw/textfile.txt:\n");
InputStream is = this.getResources().openRawResource(R.raw.textfile);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr, 8192);
// 2nd arg is buff
er size

// More efficient (less readable) implementation of above is the composi


te expression
//
BufferedReader br = new BufferedReader(new InputStreamReader(
//
this.getResources().openRawResource(R.raw.textfile)), 819
2);
try {
String test;
while (true) {
test = br.readLine();
// readLine() returns null if no more lines in the file
if (test == null) break;
tv.append("\n" + "
" + test);
}
isr.close();
is.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
tv.append("\n\nTHAT IS ALL");
}
}
Adding Resources
Finally, to test part of our application we need to create the res/raw directory
and place a file containing some lines of text in it.
1.
If res/raw does not exist in the project, right click on the res directo
ry in the left panel of Android Studio and select New > Directory. In the result
ing window, give the folder the name raw and click OK.
2.
Then, right-click on the new res/raw folder and select New > File. In th
e resulting window give the new file the name textfile.txt (with the .txt extens
ion) and click OK.
3.
Double click on the new res/raw/textfile.txt to open it in the editor an
d add several lines of text to it for testing purposes. For this example, I inse
rted into res/raw/textfile.txt
Now is the time
for all good men
to come to the aid
of their country.
but you can put whatever you wish.
That completes our application. Now let's test it and explain what it does.
Trying it Out
Execution of the application on a phone or emulator should produce a display lik
e the following figure

indicating that several tasks have been carried out:


1.
The external file system (generically the SD card) has been checked for
whether it is mounted and readable/writable.
2.
The root directory of the external file system has been identified.
3.
The root directory of the data directory for this app in the external fi
le system has been identified.
4.
A data file was written to
/storage/emulated/0/Android/data/com.lightcone.writesdcard/files/Documents/downl
oad/myData.txt

on the external file system.


5.
Data were read in and displayed from a file res/raw/textfile.txt.
Let us now explain how the code in our XML and Java files carries this out.
Overview of Basic Functionality
First, we set up the ability to output results to our main display:
1.
In a manner that should by now be familiar, a TextView object tv is defi
ned by using FindViewByID().
2.
Then, at various places, the append(CharSequence text) method of TextVie
w is used to append additional information to that already displayed on the scre
en.
Besides the onCreate() method, there are three methods in MainActivity.java. Eac
h implements a basic functionality:
1.
The method checkExternalMedia() checks whether the device has external m
edia installed and whether it is readable and writable.
2.
The method writeToSDFile() writes some output to a file on the external
SD card.
3.
The method readRaw() reads input from a file installed with the applicat
ion in the res/raw directory.
Let us now explain in turn how each of these methods works.
Checking External Media
To check the status of external storage media in the method checkExternalMedia()
, we first invoke thegetExternalStorageState() method of Environment to return a
string that is stored in the variable state. This string is then compared, usin
g the equals() method of the String class, with various String constants of theE
nvironment class to determine the state of the external media.
If state is equal to Environment.MEDIA_MOUNTED, the storage medium is present an
d mounted at its mount point, with read/write access.
If state is equal to Environment.MEDIA_MOUNTED_READ_ONLY, the storage medium is
present and mounted at its mount point, with read but not write access.
If state has any other value, the storage medium is neither readable nor writabl
e.
Notice that in Android, as in Java more generally, the logical comparison operat
or == cannot be used to compare two strings. Instead we must use an operator lik
e the equals() method to return a boolean indicating whether two strings are equ
ivalent.
If the check is successful, we display the line on the screen
EXTERNAL MEDIA: readable=true writable=true
Assuming this to be the case, in the next section we shall write to a file on th
e external storage medium.
"External" means media/shared storage. It is a filesystem with relatively large
capacity that does not enforce permissions (so it can be shared across applicati
ons). Traditionally this is an SD card, but it might also be built-in storage th
at is distinct from the protected internal storage and can be mounted as a files
ystem on the device. For a more extensive discussion of data storage in Android,
see theStorage Options document.
Writing to a File on the SD Card
Assuming that we have a writable external medium, the method writeSDCard() illus
trates how to output a file to that storage.
Finding the Root of External Storage
First, we use the getExternalStorageDirectory() method of Environment to return
the root of the file system for the external medium, assigning the returned stri

ng to the Java File object root.


A File is an abstract representation of a filesystem entity that is identified b
y a path. It can be a normal file, but it can also be a directory or some other
entity. For our usage it will typically be a file or directory that either exist
s or that we wish to create.
For example, when executed on a Nexus 6P phone running Android 6.0.1, the line
EXTERNAL FILE SYSTEM ROOT DIRECTORY: /storage/emulated/0
was written to the screen, indicating that the external medium was mounted and w
ritable, with the root of the filesystem at /storage/emulated/0.
Some earlier discussions of Android programming assume that the root of the exte
rnal storage will be /sdcard (since it commonly was on earlier devices) and give
examples of hardwiring that into an application. It is more bullet-proof to use
the getExternalStorageDirectory() method, as described above, to have the devic
e itself tell you the filesystem root. This is particularly true for the Android
file system beginning with Jelly Bean (Android 4.2) and following, which introd
uced the option of multiple users for the same device. This required the introdu
ction of various Linux symbolic links(symlinks) that produce symbolic directorie
s pointing to real physical directories. For the most part this is transparent t
o the ordinary user, but for applications such as those discussed here where we
are programatically manipulating the file system it becomes more relevant. This
will be discussed further below.
But what is more relevant to the present application is the location of the user
-writable storage allocated for this app (where the app can place persistent fil
es that it owns internal to the application and not typically visible to the use
r as media). This is obtained using the getExternalFilesDir(String string) metho
d of Contextto return the root directory for writing app-specific data to the ex
ternal medium, assigning the returned string to the Java File object root.
The method getExternalFilesDir(String string) returns the user storage associate
d with the app, which doesn't require write permissions after API 18. The string
argument specifies various regions of this storage. For example,
null specifies the root of the storage for this app.
Environment.DIRECTORY_NOTIFICATIONS specifies the Notifications directory of app
storage
Environvment.DIRECTORY_DOWNLOADS specifies a standard directory for files downlo
aded by user
Environment.DIRECTORY_PICTURES specifies a standard directory for images availab
le to the user
Environment.DIRECTORY_DOCUMENTS specifies a standard directory for documents pro
duced by user
and so on. See the public static String fields of the Environment class for othe
r options. For example, on my phone (running Android 6.0.1) the root of the user
storage for this specific app is found at
/storage/emulated/0/Android/data/com.lightcone.writesdcard/files ,
which was located by passing the null argument to getExternalFilesDir(string).
The following screenshot shows part of the file structure on my phone in the dir
ectory/storage/emulated/0/Android/data.

We see that /storage/emulated/0/Android/data contains a whole set of directories


associated with different package names. For example, there are six directories
associated with the com.lightcone package name that I use for my apps, each dis
tinguished by a different app name (including com.lightcone.writesdcard, which c
orresponds to the present app). Generally, for Android 4.4 and later external st

orage read/write permission is not required within the same package space but pa
ths belonging to other packages require read/write external media permissions (s
ee the documentation associated with getExternalFilesDir).
Setting Up the Path to the File
Now suppose that we wish create and write to a file called myData.txt in a subdi
rectory download of the root directory on the SD card for our app. The statement
s in MainActivity.java that set up the path up for that output are
File dir = new File (root.getAbsolutePath() + "/download");
dir.mkdirs();
File file = new File(dir, "myData.txt");
The first line above creates a new instance of File using the constructor File(S
tring path), where path is the path to the "file" (which in this case is actuall
y going to be a directory).
To construct this path we first use the getAbsolutePath() method that root inher
its from File to return the absolute path corresponding to root (the root of the
external-storage file system for the app), and then we concatenate that with th
e string "/download". The net effect in this case will be to pass to the File co
nstructor the path
/storage/emulated/0/Android/data/com.lightcone.writesdcard/files/Documents/down
load
Then in the second line above we use the mkdirs() method of File, which creates
the corresponding directory.
Finally, in the third line above we define another instance of File called file
(in this case it actually is a file and not a directory), creating it using a di
fferent version of the constructor File(File directory, String filename), where
directory is the directory where the file is to be stored and filename is the na
me of the file.
Thus file now specifies the absolute path
/storage/emulated/0/Android/data/com.lightcone.writesdcard/files/Documents/down
load/myData.txt
to the file into which we wish to write.
In the discussion above two different forms of the constructor for File were use
d that have the same name but different argument lists:
1.
File(String path)
2.
File(File directory, String filename)
This is an example of what is called method overloading in object oriented progr
amming, where two or more definitions of a method exist having the same name but
different numbers and/or data types for method arguments.
The name and list of argument types for a method is called its signature. Thus t
he first method above has the signature File(String) while the second has the si
gnature File(File, String). Even though the overloaded methods share the same na
me, they have different signatures and the compiler can distinguish among them b
y examining their argument lists.
NOTE: Java does not consider the return type in distinguishing methods, so you c
annot define an overloaded method having the same signature but different return
types.
Writing Using Output Streams and Writers
To write to the file standard Java i/o stream capability is used, implemented in
terms of the classes
1.
FileOutputStream, which subclasses the abstract class OutputStream (the
superclass of all classes representing an output stream of bytes) and generates
a byte output stream.
2.
PrintWriter, which is used to wrap the FileOutputStream and provide frie

ndly user i/o (capability to deal with lines of ascii text rather than a byte st
ream).
The relevant code excerpted from the method writeToSDFile() is
try {
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
pw.println("Howdy do to you.");
pw.println("Here is a second line.");
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i(TAG, "File not found");
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "I/O exception");
}
which creates a FileOutputStream, wraps it in a PrintWriter, and then appends li
nes to a file through this stream.
Handling Exceptions in the I/O
Note that all of the functional code above for the FileOuputStream is enclosed i
n a try-catch block. This is standard exception handling in Java.
1.
The constructor FileOutputStream(File file) for FileOutputStream throws
FileNotFoundException if theFile in its argument cannot be found.
2.
The method close() of FileOutputStream throws IOException if an error oc
curs in trying to close the stream.
Java requires that these exceptions be handled . The standard way to do that is
in a try-catch block, which has the general form
try {
// Code in which an exception might be thrown
.
.
} catch (ExceptionType name1) {
// Code to process exception type name1
.
.
} catch (ExceptionType name2) {
// Code to process exception type name2
.
.
}
where one or more appended catch blocks process the named exceptions thrown in t
he try block. For a more extensive discussion of try-catch blocks, start with th
e Java Tutorials. In the present code the two catchblocks deal respectively with
any FileNotFoundException or IOException that might be thrown in the tryblock.
For the catch block corresponding to FileNotFoundException we first invoke the p
rintStackTrace() method that FileNotFoundException inherits from Throwable, whic
h sends a human-readable form of theThrowable's stack trace to the System.err st
ream.
Throwable is the superclass of all Java errors and exceptions that can be thrown
by the virtual machine. It has two direct subclasses:
1.
Exception, which corresponds to recoverable errors, and
2.
Error, which corresponds to unrecoverable errors.

FileNotFoundException is a subclass of IOException, which is in turn a subclass


of Exception.
The alert Androider will object that there are two other exceptions thrown by co
de contained in thetry block that we have ignored. Indeed, if you check the docu
mentation you will find that
1.
The FileOutputStream constructor throws SecurityException in addition to
FileNotFoundException.
2.
The PrintWriter constructor throws NullPointerException.
So why have we ignored these? Well, there are two categories of exceptions in Ja
va:
1.
SecurityException and NullPointerException are subclasses of RuntimeExce
ption (the superclass of exceptions occurring as a result of executing an applic
ation in the virtual machine). The compiler does not require that a code handle
runtime exceptions.
2.
The checked exceptions (those that are not subclasses of RuntimeExceptio
n or Error---the superclass of all classes representing non-recoverable errors),
like FileNotFoundException andIOException, are checked by the compiler and must
be handled in your code.
Thus handling of the checked exceptions FileNotFoundException and IOException in
our code is mandatory, but handling of the runtime exceptions NullPointerExcept
ion and SecurityException is optional. For a discussion of the reasoning behind
this choice in the Java language, and of situations where you might choose to ha
ndle runtime exceptions in your code, see The Java Tutorials.
Within the try-catch block we then instantiate a FileOutputStream f using the co
nstructor with the File fileas argument, and create a PrintWriter pw using the j
ust-created FileOutputStream f as argument. Now we can write to the file using t
he print methods of PrintWriter. In this case we use println(String s) to output
lines of text to the file. When we are through writing, the flush() method of P
rintWriter is called to ensure that all pending data have been sent to the file,
and then the PrintWriter pw and FileOutputStream f are closed.
For output of large datasets, performance may be enhanced by using a BufferedOut
putStream to wrap an existingOutputStream and buffer the output. A typical const
ruction is
BufferedOutputStream b = new BufferedOutputStream(new FileOutputStream("file"));
Since most requests can be satisfied by accessing the buffer alone, this minimiz
es costly interaction with the underlying stream at the (usually smaller) expens
e of the extra space holding the buffer and time consumed in copying when the bu
ffer is flushed. Likewise, input streams can be wrapped in a BufferedInputStream
. We won't use BufferedOutputStreamin the present example, but below in readRaw(
) we will give an example of buffering an input stream.
Outputting a File
If this application is executed on a real device or emulator, the screen output
shown above should result and you should find that the file
/storage/emulated/0/Android/data/com.lightcone.writesdcard/files/Documents/down
load/myData.txt
has been written to the SD card (or equivalent external storage). You can check
the content of the file by copying it to your computer using ADB or Android Stud
io, as described in Transferring Files, or by using a file-management app like W
iFi File Explorer. For example, on my Linux Fedora 23 system connected to an And
roid phone I used ADB to give
[guidry@m33 ~]$ adb -d pull /storage/emulated/0/Android/data/com.lightcone.wr
itesdcard/files/Documents/download/myData.txt
0 KB/s (40 bytes in 0.081s)
[guidry@m33 ~]$ cat myData.txt
Howdy do to you,

and the horse you rode in on.


[guidry@m33 ~]$
indicating that our application has indeed written the requested output to the f
ile on the phone's external storage, and that this file is publicly accessible.
(The adb -d pull command copies the file from the device to the computer and the
Unix cat command displays the content of the file on the computer.)
The Android file hierarchy (which is a variant of the Linux file hierarchy) beca
me somewhat confusing with the advent of Jelly Bean (Android 4.2). The reason is
that Jelly Bean introduced the option of multiple users on a device, and to sep
arate those multiple users the file system was restructured so that each user ef
fectively has a different portion of the "sdcard" (external storage). For exampl
e, the address /storage/emulated/0/download/ represents a directory on external
storage allocated for the first user (user 0).
This restructuring employs various symbolic links. It is these symbolic links th
at cause different addresses to appear for different ways to access a file on th
e post-4.1 file system. For a more extensive (but I think not very clear) discus
sion, see
Using the External Storage
Why did /sdcard/ turn into /sdcard/0/ with 4.2?
Confused by the many locations of the virtual /sdcard/
Sdcard 0 folder?
4.2 /0 folder question
The bottom line seems to be that (1) These changes are transparent to the actual
users (at least of unrooted devices), but matter for programmers if they access
the file system directly, or for users with rooted devices for issues like back
up. (2) The Android SDK finds the appropriate file locations, whether on earlier
devices or newer devices, as long as methods like getExternalStorageDirectoryan
d not hard-wired addresses are used. (3) File-management apps like Astro File Ma
nager or WiFi File Explorer allow correct navigation to files on old and new dev
ices. (4) The ADB often follows the symbolic links properly to an address return
ed by getExternalStorageDirectory.
Appending Rather Than Overwriting
The default behavior of the output stream that we created above is to overwrite
the file if it already exists. We can instead append to the file if it already e
xists by substituting for the constructor FileOutputStream (File file) the const
ructor FileOutputStream(File file, boolean append), where the file will be overw
ritten ifappend is false and appended to if append is true.
Reading from a Static Resource File
The method readRaw() illustrates one way to set up an input stream to read in an
external file. In this example we assume the external file to be ascii text in
the res/raw directory of the application, and read it in line by line until no l
ines remain.
Input Streams and Readers
We begin by defining a buffered reader using the following code sequence.
InputStream is = this.getResources().openRawResource(R.raw.textfile);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr, 8192);
// 2nd arg is buffer size
The method getResources() is inherited by Activity from ContextWrapper. It retur
ns a Resources instance (Resources is the class that sits above the asset manage
r for the application and gives access to an application's resources).
The Resources openRawResource(int resourceID) method returns an InputStream name
d is, where resourceIDis of the form res.raw.myfile.txt, if the file to be input
is called myfile.txt and resides in the res/raw directory of the application.

A file that is stored in the res/raw directory of the project will be exported w
ith the application when it is packaged in the APK file and thus can be accessed
from the application by this approach. (Theres/raw directory is not compressed
when it is stored in the application.) The methodopenRawResource(int resourceID)
can be used to open only drawable, sound, and raw resources; it will fail if yo
u try to open string or color resources with it. A file stored in the res/raw di
rectory is readable by the application at runtime, but not writable.
An InputStreamReader instance isr is then created using the constructor with the
InputStream is as argument. AnInputStreamReader reads a buffer of bytes from th
e source stream and converts these into characters as needed.
Finally, we create a BufferedReader from isr using the constructor BufferedReade
r(Reader reader, int buffsize), where reader is an instance of the class Reader
and buffsize is the size of the read-in buffer in bytes. (Reader is the supercla
ss of all readers, which are means of reading data character-wise from a source;
for example,InputStreamReader is a subclass of Reader.)
Our BufferedReader is an example of the buffered streams discussed above. Note t
hat in production code we would commonly replace
InputStream is = this.getResources().openRawResource(R.raw.textfile);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr, 8192);
with the compound, chained expression
BufferedReader br = new BufferedReader(new InputStreamReader(
this.getResources().openRawResource(R.raw.textfile)), 8192);
since this is more compact and requires defining fewer variables. But it is also
less readable, and for our pedagogical purposes in the projects presented here
we often write expressions out as individual statements rather than compounding
them into more efficient expressions so that what is happening is clearer for th
e reader.
With our buffered input now set up we execute a while-loop, reading data in from
the file a line at a time using the readLine() method of BufferedReader.
Since readline() returns null when there are no more lines in the file, we check
for null and break from the while-loop when it is found. We then use the close(
) methods of the readers and streams to close them all.
Because readLine() and the close() methods throw the checked exception IOExcepti
on, we enclose all of this in atry-catch block to process any i/o exceptions.
Testing Read-In from a File
If you execute the application, you should find the lines that you put in the fi
le res/raw/textfile.txt displayed on the screen because of the tv.append() state
ment in the while-loop. As seen in the figure above, for my example I obtained
Now is the time
for all good men
to come to the aid
of their country.
which corresponds exactly to the four lines that I inserted in the file res/raw/
textfile.txt when it was created.
The complete project for the application described above is archived on GitHub a
t the linkWriteSDCard. Instructions for installing it in Android Studio may be f
ound in Packages for All Projects.
Last modified: July 8, 2016
Exercises

1. Use the methods of the File class to add a method to MainActivity that will l
ist the complete path to all files and directories on the SD card of the device,
and the size of the files in bytes. [Solution]