You want to store data on the external storage. Also, you want to be able to easily determine when the external storage is and isn’t available.
create layout file external_storage.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/external_storage_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10px"
android:text="Enter some text to write on the external storage, then read back:" />
<EditText android:id="@+id/external_storage_input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10px" />
<Button android:id="@+id/external_storage_write_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Write" />
<Button android:id="@+id/external_storage_read_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Read" />
<TextView android:id="@+id/external_storage_output"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10px" />
</LinearLayout>
create activity class ExternalStorage
public class ExternalStorage extends Activity {
// wrap some operations that are likely to be needed in more than one place in FileUtil
private EditText input;
private TextView output;
private Button write;
private Button read;
@Override
public void onCreate(final Bundle icicle) {
super.onCreate(icicle);
this.setContentView(R.layout.external_storage);
this.input = (EditText) findViewById(R.id.external_storage_input);
this.output = (TextView) findViewById(R.id.external_storage_output);
this.write = (Button) findViewById(R.id.external_storage_write_button);
this.write.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
write();
}
});
this.read = (Button) findViewById(R.id.external_storage_read_button);
this.read.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
read();
}
});
}
private void write() {
if(FileUtil.isExternalStorageWritable()){
File dir=FileUtil.getExternalFilesDirAllApiLevels(this.getPackageName());
File file=new File(dir,"test.txt");
FileUtil.writeStringAsFile(input.getText().toString(), file);
Toast.makeText(this, "File written", Toast.LENGTH_SHORT).show();
input.setText("");
output.setText("");
}
else{
Toast.makeText(this, "External storage not writable", Toast.LENGTH_SHORT).show();
}
}
private void read() {
if(FileUtil.isExternalStorageReadable()){
File dir=FileUtil.getExternalFilesDirAllApiLevels(this.getPackageName());
File file=new File(dir,"test.txt");
if(file.exists()&&file.canRead()){
output.setText(FileUtil.readFileAsString(file));
Toast.makeText(this, "File read", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "External storage not readable", Toast.LENGTH_SHORT).show();
}
}
}
}
create Util class FileUtil:
public final class FileUtil {
// from the Android docs, these are the recommended paths
private static final String EXT_STORAGE_PATH_PREFIX = "/Android/data/";
private static final String EXT_STORAGE_FILES_PATH_SUFFIX = "/files/";
private static final String EXT_STORAGE_CACHE_PATH_SUFFIX = "/cache/";
// Object for intrinsic lock use as a lock for synchronized blocks
/**
* these utility methods may be accessed by different threads and could possibly touch
the same files, we’ll synchronize them to avoid concurrent modification problems.
*/
public static final Object[] DATA_LOCK = new Object[0];
private FileUtil() {
}
/**
* Use Environment to check if external storage is writable.
*
* @return
*/
public static boolean isExternalStorageWritable() {
/**
* We could call Environment from our activities (and sometimes that makes sense),
* but here we chose to put the logic in one place so as not to have to repeat it.
*/
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
/**
* Use environment to check if external storage is readable.
*/
public static boolean isExternalStorageReadable() {
if (isExternalStorageWritable()) {
return true;
}
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
}
/**
* Return the recommended external files directory, whether using API level 8 or lower.
* (Uses getExternalStorageDirectory and then appends the recommended path.)
*
* @param packageName
* @return
*/
public static File getExternalFilesDirAllApiLevels(final String packageName) {
return FileUtil.getExternalDirAllApiLevels(packageName, EXT_STORAGE_FILES_PATH_SUFFIX);
}
/**
* Return the recommended external cache directory, whether using API level 8 or lower.
* (Uses getExternalStorageDirectory and then appends the recommended path.)
*
* @param packageName
* @return
*/
public static File getExternalCacheDirAllApiLevels(final String packageName) {
return FileUtil.getExternalDirAllApiLevels(packageName, EXT_STORAGE_CACHE_PATH_SUFFIX);
}
private static File getExternalDirAllApiLevels(final String packageName, final String suffixType) {
File dir =
new File(Environment.getExternalStorageDirectory() + EXT_STORAGE_PATH_PREFIX + packageName + suffixType);
synchronized(FileUtil.DATA_LOCK){
try{
dir.mkdirs();
dir.createNewFile();
}catch (IOException e) {
Log.e(Constants.LOG_TAG, "Error creating file", e);
}
}
return dir;
}
/**
* Copy file, return true on success, false on failure.
*
*/
public static boolean copyFile(final File src, final File dst) {
boolean result=false;
FileChannel inChannel=null;
FileChannel outChannel=null;
synchronized(FileUtil.DATA_LOCK){
try{
inChannel=new FileInputStream(src).getChannel();
outChannel=new FileOutputStream(dst).getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
result=true;
}catch (IOException e) {
}finally{
if(inChannel!=null&&inChannel.isOpen()){
try{
inChannel.close();
}catch(IOException e){
}
}
if(outChannel!=null&&outChannel.isOpen()){
try{
outChannel.close();
}catch(IOException e){
}
}
}
}
return result;
}
/**
* Replace entire File with contents of String, return true on success, false on failure.
*/
public static boolean writeStringAsFile(final String fileContents, final File file) {
boolean result = false;
try {
synchronized (FileUtil.DATA_LOCK) {
if (file != null) {
file.createNewFile(); // ok if returns false, overwrite
Writer out = new BufferedWriter(new FileWriter(file), 1024);
out.write(fileContents);
out.close();
result = true;
}
}
} catch (IOException e) {
Log.e(Constants.LOG_TAG, "Error writing string data to file " + e.getMessage(), e);
}
return result;
}
/**
* Append String to end of File, return true on success, false on failure.
*/
public static boolean appendStringToFile(final String appendContents, final File file) {
boolean result = false;
try {
synchronized (FileUtil.DATA_LOCK) {
if ((file != null) && file.canWrite()) {
file.createNewFile(); // ok if returns false, overwrite
Writer out = new BufferedWriter(new FileWriter(file, true), 1024);
out.write(appendContents);
out.close();
result = true;
}
}
} catch (IOException e) {
Log.e(Constants.LOG_TAG, "Error appending string data to file " + e.getMessage(), e);
}
return result;
}
/**
* Read file as String, return null if file is not present or not readable.
*/
public static String readFileAsString(final File file) {
StringBuilder sb = null;
try {
synchronized (FileUtil.DATA_LOCK) {
if ((file != null) && file.canRead()) {
sb = new StringBuilder();
String line = null;
BufferedReader in = new BufferedReader(new FileReader(file), 1024);
while ((line = in.readLine()) != null) {
sb.append(line + System.getProperty("line.separator"));
}
}
}
} catch (IOException e) {
Log.e(Constants.LOG_TAG, "Error reading file " + e.getMessage(), e);
}
if (sb != null) {
return sb.toString();
}
return null;
}
}
FileWriter G and FileReader,if we needed more control, such as specifying the file encoding, we could’ve used the lower-level FileInputStream and or FileOutputStream classes.