File based persistence in Android
This tutorial describes how to save key-value pairs using the preference API in Android. It also explains how to read and write files in Android. It is based on Eclipse 4.3, Java 1.6 and Android 4.3.
Table of Contents
Android allows to persists application data via the file system. For each application the Android system creates a
data/data/[application package]
directory.Android supports the following ways of storing data in the local file system:
- Files - You can create and update files
- Preferences - Android allows you to save and retrieve persistent key-value pairs of primitive data type.
- SQLite database - instances of SQLite databases are also stored on the local file system.
Files are saved in the
files
folder and application settings are saved as XML files in the shared_prefs
folder.If your application creates an SQLite database this database is saved in the main application directory under the
databases
folder.The following screenshot shows a file system which contains file, cache files and preferences.
Only the application can write into its application directory. It can create additional sub-directories in this application directory. For these sub-directories, the application can grant read or write permissions for other applications.
Android has internal storage and external storage. External storage is not private and may not always be available. If for example the Android device is connected with a computer, the computer may mount the external system via USB and that makes this external storage not avaiable for Android applications.
As of Android 8 SDK level it is possible to define that the application can or should be placed on external storage. For this set the
android:installLocation
to preferExternal or auto.In this case certain application components may be stored on an encrypted external mount point. Database and other private data will still be stored in the internal storage system.
Android supports the usage of the SharedPreferences class for persisting key-value pairs (preferences)of primitive data types in the Android file system.
The definition of these preferences can be done via an XML resource.
The
PreferenceManager
class provides methods to get access to preferences stored in a certain file. The following code shows how to access preferences from a certain file# getting preferences from a specified file
SharedPreferences settings = getSharedPreferences("Test", Context.MODE_PRIVATE);
The default preferences are available from any component via the
PreferenceManager.getDefaultSharedPreferences(this)
method call.Preference value are accessed via the key and the instance of the
SharedPreferences
class, as demonstrated in the following listing.String username = preferences.getString("username", "n/a");
To create or change preferences you have to call the
edit()
method on the SharedPreferences
object. Once you have changed the value you have to call the apply()
method to apply your asynchronously to the file system. The usage of the commit()
method is discouraged, as it write the changes synchronously to the file system.Editor edit = preferences.edit();
edit.putString("username", "new_value_for_user");
edit.apply();
You can listen to changes in the preferences via the
registerOnSharedPreferenceChangeListener()
method on SharedPreferences
.SharedPreferences prefs =
PreferenceManager.getDefaultSharedPreferences(this);
// Instance field for listener
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
// Your Implementation
}
};
prefs.registerOnSharedPreferenceChangeListener(listener);
One watch out is that
SharedPreferences
keeps listeners in a WeakHashMap
hence listener may be recycled if your code does not hold a reference to it.The following tutorial is based on the "de.vogella.android.socialapp" example from Android action bar tutorial .
This exercise assumes that you have created an Android project called de.vogella.android.social with one entry called Settings in the action bar.
Create an Android XML resource called preferences.xml of the PreferenceScreen type.
Open the file via right-click and
→ .Press the Add button and add a PreferenceCategory and add two preferences of typeEditTextPreferences. They should be called
User
and Password
.You can also enter values for other properties of
EditTextField
, e.g. the inputMethod.Add the following attribute to the XML definition of your password field to make the input quoted with
*
.android:inputType="textPassword"
Create the class
MyPreferencesActivity
which extends PreferenceActivity
. This activity loads the preference.xml
file and allows the user to change the values.package de.vogella.android.socialapp;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class MyPreferencesActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
Register this class as an activity in your
AndroidManifest.xml
file.To make use of our new preference activity and the preference values we adjust your main activity. Ensure that the layout of the activity contains two buttons.
The first button will show the current values of the preferences via a Toast and the second button will revert the maintained user name to demonstrate how you could change the preferences via code.
package de.vogella.android.socialapp;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class OverviewActivity extends Activity {
SharedPreferences preferences;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) findViewById(R.id.Button01);
// Initialize preferences
preferences = PreferenceManager.getDefaultSharedPreferences(this);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String username = preferences.getString("username", "n/a");
String password = preferences.getString("password", "n/a");
showPrefs(username, password);
}
});
Button buttonChangePreferences = (Button) findViewById(R.id.Button02);
buttonChangePreferences.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
updatePreferenceValue();
}
});
}
private void showPrefs(String username, String password){
Toast.makeText(OverviewActivity.this,
"Input: " + username + " and password: "
+ password, Toast.LENGTH_LONG).show();
}
private void updatePreferenceValue(){
Editor edit = preferences.edit();
String username = preferences.getString("username", "n/a");
// We will just revert the current user name and save again
StringBuffer buffer = new StringBuffer();
for (int i = username.length() - 1; i >= 0; i--) {
buffer.append(username.charAt(i));
}
edit.putString("username", buffer.toString());
edit.commit();
// A toast is a view containing a quick little message for the
// user. We give a little feedback
Toast.makeText(OverviewActivity.this,
"Reverted string sequence of user name.",
Toast.LENGTH_LONG).show();
}
Open the preference activity via the
onOptionsItemSelected()
method as demonstrated in the following listing.@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mainmenu, menu);
return true;
}
// This method is called once the menu is selected
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// We have only one menu option
case R.id.preferences:
// Launch Preference activity
Intent i = new Intent(OverviewActivity.this, MyPreferencesActivity.class);
startActivity(i);
// Some feedback to the user
Toast.makeText(OverviewActivity.this, "Enter your user credentials.",
Toast.LENGTH_LONG).show();
break;
}
return true;
}
Run your application. Select from your ActionBar the Preference menu entry. You should be able to enter your user settings then press the back hardware button to return to your main activity. The saved values should be displayed in a small message windows (Toast) if you press your first button. If you press the second button the username should be reversed.
Access to the file system is performed via the standard
java.io
classes.Android provides also helper classes for creating and accessing new files and directories. For example the
getDir(String, int)
method would create or access a directory. TheopenFileInput(String s)
method would open a file for input and openFileOutput(String s, int)
would create a file.int specifies the permissions which are:
- MODE_PRIVATE - No access for other applications
- MODE_WORLD_READABLE - Read access for other applications
- MODE_WORLD_WRITABLE - Write access for other applications
- MODE_WORLD_READABLE | MODE_WORLD_WRITABLE - Read / Write access
The following example shows the API usage.
private void writeFileToInternalStorage() {
String eol = System.getProperty("line.separator");
BufferedWriter writer = null;
try {
writer =
new BufferedWriter(new OutputStreamWriter(openFileOutput("myfile",
MODE_WORLD_WRITEABLE)));
writer.write("This is a test1." + eol);
writer.write("This is a test2." + eol);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void readFileFromInternalStorage() {
String eol = System.getProperty("line.separator");
BufferedReader input = null;
try {
input = new BufferedReader(new InputStreamReader(openFileInput("myfile")));
String line;
StringBuffer buffer = new StringBuffer();
while ((line = input.readLine()) != null) {
buffer.append(line + eol);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Another application can access a file, which has been created with the
MODE_WORLD_READABLE
model. For this, it need to knows the package and file name. The following example shows this.FileInputStream openFileInput =
createPackageContext("the_package", 0).
openFileInput("thefile");
Android supports also access to an external storage system e.g. the SD card. All files and directories on the external storage system are readable for all applications with the correct permission.
To read from external storage your application need to have the
android.permission.READ_EXTERNAL_STORAGE
permission.To write to the external storage system your application needs the
android.permission.WRITE_EXTERNAL_STORAGE
permission. You get the path to the external storage system via the Environment.getExternalStorageDirectory()
method.Via the following method call you can check the state of the external storage system. If the Android device is connected via USB to a computer, a SD card which might be used for the external storage system is not available.
Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
The following shows an example for reading from the external storage system.
private void readFileFromSDCard() {
File directory = Environment.getExternalStorageDirectory();
// assumes that a file article.rss is available on the SD card
File file = new File(directory + "/article.rss");
if (!file.exists()) {
throw new RuntimeException("File not found");
}
Log.e("Testing", "Starting to read");
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(file));
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
No comments:
Post a Comment