,"Data Backup","Data Backup","
Android's backup
service allows you to copy your persistent application data to remote "cloud" storage, in order to provide a restore point for the application data and settings. If a user performs a factory reset or converts to a new Android-powered device, the system automatically restores your backup data when the application is re-installed. This way, your users don't need to reproduce their previous data or application settings. This process is completely transparent to the user and does not affect the functionality or user experience in your application.
During a backup operation (which your application can request), Android's Backup Manager (BackupManager
) queries your application for backup data, then hands it to a backup transport, which then delivers the data to the cloud storage. During a restore operation, the Backup Manager retrieves the backup data from the backup transport and returns it to your application so your application can restore the data to the device. It's possible for your application to request a restore, but that shouldn't be necessary!Android automatically performs a restore operation when your application is installed and there exists backup data associated with the user. The primary scenario in which backup data is restored is when a user resets their device or upgrades to a new device and their previously installed applications are re-installed.
Note: The backup service is not designed for synchronizing application data with other clients or saving data that you'd like to access during the normal application lifecycle. You cannot read or write backup data on demand and cannot access it in any way other than through the APIs provided by the Backup Manager.
The backup transport is the client-side component of Android's backup framework, which is customizable by the device manufacturer and service provider. The backup transport may differ from device to device and which backup transport is available on any given device is transparent to your application. The Backup Manager APIs isolate your application from the actual backup transport available on a given device!your application communicates with the Backup Manager through a fixed set of APIs, regardless of the underlying transport.
Data backup is not guaranteed to be available on all Android-powered devices. However, your application is not adversely affected in the event that a device does not provide a backup transport. If you believe that users will benefit from data backup in your application, then you can implement it as described in this document, test it, then publish your application without any concern about which devices actually perform backup. When your application runs on a device that does not provide a backup transport, your application operates normally, but will not receive callbacks from the Backup Manager to backup data.
Although you cannot know what the current transport is, you are always assured that your backup data cannot be read by other applications on the device. Only the Backup Manager and backup transport have access to the data you provide during a backup operation.
Caution: Because the cloud storage and transport service can differ from device to device, Android makes no guarantees about the security of your data while using backup. You should always be cautious about using backup to store sensitive data, such as usernames and passwords.
","BackupManager","android.app.backup.BackupManager","class",0 ,"Extending BackupAgent","Performing backup","When it's time to back up your application data, the Backup Manager calls your onBackup()
method. This is where you must provide your application data to the Backup Manager so it can be saved to cloud storage.
Only the Backup Manager can call your backup agent's onBackup()
method. Each time that your application data changes and you want to perform a backup, you must request a backup operation by calling dataChanged()
(see Requesting Backup for more information). A backup request does not result in an immediate call to your onBackup()
method. Instead, the Backup Manager waits for an appropriate time, then performs backup for all applications that have requested a backup since the last backup was performed.
Tip: While developing your application, you can initiate an immediate backup operation from the Backup Manager with the bmgr
tool.
When the Backup Manager calls your onBackup()
method, it passes three parameters:
oldState
ParcelFileDescriptor
pointing to the last backup state provided by your application. This is not the backup data from cloud storage, but a local representation of the data that was backed up the last time
onBackup()
was called (as defined by
newState
, below, or from
onRestore()
!more about this in the next section). Because
onBackup()
does not allow you to read existing backup data in the cloud storage, you can use this local representation to determine whether your data has changed since the last backup.
data
BackupDataOutput
object, which you use to deliver your backup data to the Backup Manager.
newState
ParcelFileDescriptor
pointing to a file in which you must write a representation of the data that you delivered to
data
(a representation can be as simple as the last-modified timestamp for your file). This object is returned as
oldState
the next time the Backup Manager calls your
onBackup()
method. If you do not write your backup data to
newState
, then
oldState
will point to an empty file next time Backup Manager calls
onBackup()
.
Using these parameters, you should implement your onBackup()
method to do the following:
oldState
to your current data. How you read data in oldState
depends on how you originally wrote it to newState
(see step 3). The easiest way to record the state of a file is with its last-modified timestamp. For example, here's how you can read and compare a timestamp from oldState
: // Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = mDataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
If nothing has changed and you don't need to back up, skip to step 3.
oldState
, write the current data to data
to back it up to the cloud storage. You must write each chunk of data as an "entity" in the BackupDataOutput
. An entity is a flattened binary data record that is identified by a unique key string. Thus, the data set that you back up is conceptually a set of key-value pairs.
To add an entity to your backup data set, you must:
writeEntityHeader()
, passing a unique string key for the data you're about to write and the data size.writeEntityData()
, passing a byte buffer that contains your data and the number of bytes to write from the buffer (which should match the size passed to writeEntityHeader()
).For example, the following code flattens some data into a byte stream and writes it into a single entity:
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeUTF(mPlayerName); outWriter.writeInt(mPlayerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); data.writeEntityData(buffer, len);
Perform this for each piece of data that you want to back up. How you divide your data into entities is up to you (and you might use just one entity).
newState
ParcelFileDescriptor
. The Backup Manager retains this object locally as a representation of the data that is currently backed up. It passes this back to you as oldState
the next time it calls onBackup()
so you can determine whether another backup is necessary (as handled in step 1). If you do not write the current data state to this file, then oldState
will be empty during the next callback. The following example saves a representation of the current data into newState
using the file's last-modified timestamp:
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = mDataFile.lastModified(); out.writeLong(modified);
Caution: If your application data is saved to a file, make sure that you use synchronized statements while accessing the file so that your backup agent does not read the file while an Activity in your application is also writing the file.
","BackupManager","android.app.backup.BackupManager","class",0 ,"Extending BackupAgent","Performing restore","When it's time to restore your application data, the Backup Manager calls your backup agent's onRestore()
method. When it calls this method, the Backup Manager delivers your backup data so you can restore it onto the device.
Only the Backup Manager can call onRestore()
, which happens automatically when the system installs your application and finds existing backup data. However, you can request a restore operation for your application by calling requestRestore()
(see Requesting restore for more information).
Note: While developing your application, you can also request a restore operation with the bmgr
tool.
When the Backup Manager calls your onRestore()
method, it passes three parameters:
data
BackupDataInput
, which allows you to read your backup data.
appVersionCode
android:versionCode
manifest attribute, as it was when this data was backed up. You can use this to cross-check the current application version and determine if the data format is compatible. For more information about using this to handle different versions of restore data, see the section below about
Checking the Restore Data Version.
newState
ParcelFileDescriptor
pointing to a file in which you must write the final backup state that was provided with
data
. This object is returned as
oldState
the next time
onBackup()
is called. Recall that you must also write the same
newState
object in the
onBackup()
callback!also doing it here ensures that the
oldState
object given to
onBackup()
is valid even the first time
onBackup()
is called after the device is restored.
In your implementation of onRestore()
, you should call readNextHeader()
on the data
to iterate through all entities in the data set. For each entity found, do the following:
getKey()
.BackupAgent
class. When the key matches one of your known key strings, enter into a statement to extract the entity data and save it to the device:
getDataSize()
and create a byte array of that size.readEntityData()
and pass it the byte array, which is where the data will go, and specify the start offset and the size to read.newState
parameter the same as you do during onBackup()
. For example, here's how you can restore the data backed up by the example in the previous section:
@Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // There should be only one entity, but the safest // way to consume it is using a while loop while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); // If the key is ours (for saving top score). Note this key was used when // we wrote the backup entity header if (TOPSCORE_BACKUP_KEY.equals(key)) { // Create an input stream for the BackupDataInput byte[] dataBuf = new byte[dataSize]; data.readEntityData(dataBuf, 0, dataSize); ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); DataInputStream in = new DataInputStream(baStream); // Read the player name and score from the backup data mPlayerName = in.readUTF(); mPlayerScore = in.readInt(); // Record the score on the device (to a file or something) recordScore(mPlayerName, mPlayerScore); } else { // We don't know this entity key. Skip it. (Shouldn't happen.) data.skipEntityData(); } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); out.writeUTF(mPlayerName); out.writeInt(mPlayerScore); }
In this example, the appVersionCode
parameter passed to onRestore()
is not used. However, you might want to use it if you've chosen to perform backup when the user's version of the application has actually moved backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see the section about Checking the Restore Data Version.
For an example implementation of BackupAgent
, see the ExampleAgent
class in the Backup and Restore sample application.
You can request a backup operation at any time by calling dataChanged()
. This method notifies the Backup Manager that you'd like to backup your data using your backup agent. The Backup Manager then calls your backup agent's onBackup()
method at an opportune time in the future. Typically, you should request a backup each time your data changes (such as when the user changes an application preference that you'd like to back up). If you call dataChanged()
several times consecutively, before the Backup Manager requests a backup from your agent, your agent still receives just one call to onBackup()
.
Note: While developing your application, you can request a backup and initiate an immediate backup operation with the bmgr
tool.
During the normal life of your application, you shouldn't need to request a restore operation. They system automatically checks for backup data and performs a restore when your application is installed. However, you can manually request a restore operation by calling requestRestore()
, if necessary. In which case, the Backup Manager calls your onRestore()
implementation, passing the data from the current set of backup data.
Note: While developing your application, you can request a restore operation with the bmgr
tool.
Once you've implemented your backup agent, you can test the backup and restore functionality with the following procedure, using bmgr
.
tools/
path: adb shell bmgr enable true
If you've properly implemented backup in your application, then it should request a backup each time the data changes. For example, each time the user changes some data, your app should call dataChanged()
, which adds a backup request to the Backup Manager queue. For testing purposes, you can also make a request with the following bmgr
command:
adb shell bmgr backup your.package.name
adb shell bmgr run
This forces the Backup Manager to perform all backup requests that are in its queue.
adb uninstall your.package.name
If your backup agent is successful, all the data you initialized in step 4 is restored.
","BackupManager","android.app.backup.BackupManager","class",0 ,"Data Backup","Data Backup","Android's backup
service allows you to copy your persistent application data to remote "cloud" storage, in order to provide a restore point for the application data and settings. If a user performs a factory reset or converts to a new Android-powered device, the system automatically restores your backup data when the application is re-installed. This way, your users don't need to reproduce their previous data or application settings. This process is completely transparent to the user and does not affect the functionality or user experience in your application.
During a backup operation (which your application can request), Android's Backup Manager (BackupManager
) queries your application for backup data, then hands it to a backup transport, which then delivers the data to the cloud storage. During a restore operation, the Backup Manager retrieves the backup data from the backup transport and returns it to your application so your application can restore the data to the device. It's possible for your application to request a restore, but that shouldn't be necessary!Android automatically performs a restore operation when your application is installed and there exists backup data associated with the user. The primary scenario in which backup data is restored is when a user resets their device or upgrades to a new device and their previously installed applications are re-installed.
Note: The backup service is not designed for synchronizing application data with other clients or saving data that you'd like to access during the normal application lifecycle. You cannot read or write backup data on demand and cannot access it in any way other than through the APIs provided by the Backup Manager.
The backup transport is the client-side component of Android's backup framework, which is customizable by the device manufacturer and service provider. The backup transport may differ from device to device and which backup transport is available on any given device is transparent to your application. The Backup Manager APIs isolate your application from the actual backup transport available on a given device!your application communicates with the Backup Manager through a fixed set of APIs, regardless of the underlying transport.
Data backup is not guaranteed to be available on all Android-powered devices. However, your application is not adversely affected in the event that a device does not provide a backup transport. If you believe that users will benefit from data backup in your application, then you can implement it as described in this document, test it, then publish your application without any concern about which devices actually perform backup. When your application runs on a device that does not provide a backup transport, your application operates normally, but will not receive callbacks from the Backup Manager to backup data.
Although you cannot know what the current transport is, you are always assured that your backup data cannot be read by other applications on the device. Only the Backup Manager and backup transport have access to the data you provide during a backup operation.
Caution: Because the cloud storage and transport service can differ from device to device, Android makes no guarantees about the security of your data while using backup. You should always be cautious about using backup to store sensitive data, such as usernames and passwords.
","package-summary","android.app.backup.package-summary","class",1 ,"Data Backup","The Basics","To backup your application data, you need to implement a backup agent. Your backup agent is called by the Backup Manager to provide the data you want to back up. It is also called to restore your backup data when the application is re-installed. The Backup Manager handles all your data transactions with the cloud storage (using the backup transport) and your backup agent handles all your data transactions on the device.
To implement a backup agent, you must:
android:backupAgent
attribute.The BackupAgent
class provides the central interface with which your application communicates with the Backup Manager. If you extend this class directly, you must override onBackup()
and onRestore()
to handle the backup and restore operations for your data.
Or
The BackupAgentHelper
class provides a convenient wrapper around the BackupAgent
class, which minimizes the amount of code you need to write. In your BackupAgentHelper
, you must use one or more "helper" objects, which automatically backup and restore certain types of data, so that you do not need to implement onBackup()
and onRestore()
.
Android currently provides backup helpers that will backup and restore complete files from SharedPreferences
and internal storage.
Most applications shouldn't need to extend the BackupAgent
class directly, but should instead extend BackupAgentHelper to take advantage of the built-in helper classes that automatically backup and restore your files. However, you might want to extend BackupAgent
directly if you need to:
BackupAgent
that reads the appropriate data during a backup operation, then create your table and insert the data during a restore operation.If you don't need to perform any of the tasks above and want to back up complete files from SharedPreferences
or internal storage, you should skip to Extending BackupAgentHelper.
You should build your backup agent using BackupAgentHelper
if you want to back up complete files (from either SharedPreferences
or internal storage). Building your backup agent with BackupAgentHelper
requires far less code than extending BackupAgent
, because you don't have to implement onBackup()
and onRestore()
.
Your implementation of BackupAgentHelper
must use one or more backup helpers. A backup helper is a specialized component that BackupAgentHelper
summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
SharedPreferencesBackupHelper
to backup SharedPreferences
files.FileBackupHelper
to backup files from internal storage.You can include multiple helpers in your BackupAgentHelper
, but only one helper is needed for each data type. That is, if you have multiple SharedPreferences
files, then you need only one SharedPreferencesBackupHelper
.
For each helper you want to add to your BackupAgentHelper
, you must do the following during your onCreate()
method:
addHelper()
to add the helper to your BackupAgentHelper
.The following sections describe how to create a backup agent using each of the available helpers.
","SharedPreferences","android.content.SharedPreferences","class",1 ,"Extending BackupAgentHelper","Backing up SharedPreferences","When you instantiate a SharedPreferencesBackupHelper
, you must include the name of one or more SharedPreferences
files.
For example, to back up a SharedPreferences
file named "user_preferences", a complete backup agent using BackupAgentHelper
looks like this:
public class MyPrefsBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String PREFS = "user_preferences"; // A key to uniquely identify the set of backup data static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
That's it! That's your entire backup agent. The SharedPreferencesBackupHelper
includes all the code needed to backup and restore a SharedPreferences
file.
When the Backup Manager calls onBackup()
and onRestore()
, BackupAgentHelper
calls your backup helpers to perform backup and restore for your specified files.
Note: SharedPreferences
are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.
The SharedPreferences
class provides a general framework that allows you to save and retrieve persistent key-value pairs of primitive data types. You can use SharedPreferences
to save any primitive data: booleans, floats, ints, longs, and strings. This data will persist across user sessions (even if your application is killed).
Shared preferences are not strictly for saving "user preferences," such as what ringtone a user has chosen. If you're interested in creating user preferences for your application, see PreferenceActivity
, which provides an Activity framework for you to create user preferences, which will be automatically persisted (using shared preferences).
To get a SharedPreferences
object for your application, use one of two methods:
getSharedPreferences()
- Use this if you need multiple preferences files identified by name, which you specify with the first parameter.getPreferences()
- Use this if you need only one preferences file for your Activity. Because this will be the only preferences file for your Activity, you don't supply a name.To write values:
edit()
to get a SharedPreferences.Editor
.putBoolean()
and putString()
.commit()
To read values, use SharedPreferences
methods such as getBoolean()
and getString()
.
Here is an example that saves a preference for silent keypress mode in a calculator:
public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); . . . // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); // Commit the edits! editor.commit(); } }","SharedPreferences","android.content.SharedPreferences","class",1 ,"Data Backup","The Basics","
To backup your application data, you need to implement a backup agent. Your backup agent is called by the Backup Manager to provide the data you want to back up. It is also called to restore your backup data when the application is re-installed. The Backup Manager handles all your data transactions with the cloud storage (using the backup transport) and your backup agent handles all your data transactions on the device.
To implement a backup agent, you must:
android:backupAgent
attribute.The BackupAgent
class provides the central interface with which your application communicates with the Backup Manager. If you extend this class directly, you must override onBackup()
and onRestore()
to handle the backup and restore operations for your data.
Or
The BackupAgentHelper
class provides a convenient wrapper around the BackupAgent
class, which minimizes the amount of code you need to write. In your BackupAgentHelper
, you must use one or more "helper" objects, which automatically backup and restore certain types of data, so that you do not need to implement onBackup()
and onRestore()
.
Android currently provides backup helpers that will backup and restore complete files from SharedPreferences
and internal storage.
You should build your backup agent using BackupAgentHelper
if you want to back up complete files (from either SharedPreferences
or internal storage). Building your backup agent with BackupAgentHelper
requires far less code than extending BackupAgent
, because you don't have to implement onBackup()
and onRestore()
.
Your implementation of BackupAgentHelper
must use one or more backup helpers. A backup helper is a specialized component that BackupAgentHelper
summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
SharedPreferencesBackupHelper
to backup SharedPreferences
files.FileBackupHelper
to backup files from internal storage.You can include multiple helpers in your BackupAgentHelper
, but only one helper is needed for each data type. That is, if you have multiple SharedPreferences
files, then you need only one SharedPreferencesBackupHelper
.
For each helper you want to add to your BackupAgentHelper
, you must do the following during your onCreate()
method:
addHelper()
to add the helper to your BackupAgentHelper
.The following sections describe how to create a backup agent using each of the available helpers.
","BackupAgentHelper","android.app.backup.BackupAgentHelper","class",0 ,"Extending BackupAgentHelper","Backing up SharedPreferences","When you instantiate a SharedPreferencesBackupHelper
, you must include the name of one or more SharedPreferences
files.
For example, to back up a SharedPreferences
file named "user_preferences", a complete backup agent using BackupAgentHelper
looks like this:
public class MyPrefsBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String PREFS = "user_preferences"; // A key to uniquely identify the set of backup data static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
That's it! That's your entire backup agent. The SharedPreferencesBackupHelper
includes all the code needed to backup and restore a SharedPreferences
file.
When the Backup Manager calls onBackup()
and onRestore()
, BackupAgentHelper
calls your backup helpers to perform backup and restore for your specified files.
Note: SharedPreferences
are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.
When you instantiate a FileBackupHelper
, you must include the name of one or more files that are saved to your application's internal storage (as specified by getFilesDir()
, which is the same location where openFileOutput()
writes files).
For example, to backup two files named "scores" and "stats," a backup agent using BackupAgentHelper
looks like this:
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
The FileBackupHelper
includes all the code necessary to backup and restore files that are saved to your application's internal storage..
However, reading and writing to files on internal storage is not threadsafe. To ensure that your backup agent does not read or write your files at the same time as your activities, you must use synchronized statements each time you perform a read or write. For example, in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:
// Object for intrinsic lock static final Object sDataLock = new Object();
Then create a synchronized statement with this lock each time you read or write the files. For example, here's a synchronized statement for writing the latest score in a game to a file:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir()
, TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
You should synchronize your read statements with the same lock.
Then, in your BackupAgentHelper
, you must override onBackup()
and onRestore()
to synchronize the backup and restore operations with the same intrinsic lock. For example, the MyFileBackupAgent
example from above needs the following methods:
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs backup synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
That's it. All you need to do is add your FileBackupHelper
in the onCreate()
method and override onBackup()
and onRestore()
to synchronize read and write operations.
For an example implementation of BackupAgentHelper
with FileBackupHelper
, see the FileHelperExampleAgent
class in the Backup and Restore sample application.
To backup your application data, you need to implement a backup agent. Your backup agent is called by the Backup Manager to provide the data you want to back up. It is also called to restore your backup data when the application is re-installed. The Backup Manager handles all your data transactions with the cloud storage (using the backup transport) and your backup agent handles all your data transactions on the device.
To implement a backup agent, you must:
android:backupAgent
attribute.The BackupAgent
class provides the central interface with which your application communicates with the Backup Manager. If you extend this class directly, you must override onBackup()
and onRestore()
to handle the backup and restore operations for your data.
Or
The BackupAgentHelper
class provides a convenient wrapper around the BackupAgent
class, which minimizes the amount of code you need to write. In your BackupAgentHelper
, you must use one or more "helper" objects, which automatically backup and restore certain types of data, so that you do not need to implement onBackup()
and onRestore()
.
Android currently provides backup helpers that will backup and restore complete files from SharedPreferences
and internal storage.
Most applications shouldn't need to extend the BackupAgent
class directly, but should instead extend BackupAgentHelper to take advantage of the built-in helper classes that automatically backup and restore your files. However, you might want to extend BackupAgent
directly if you need to:
BackupAgent
that reads the appropriate data during a backup operation, then create your table and insert the data during a restore operation.If you don't need to perform any of the tasks above and want to back up complete files from SharedPreferences
or internal storage, you should skip to Extending BackupAgentHelper.
When you create a backup agent by extending BackupAgent
, you must implement the following callback methods:
onBackup()
onRestore()
When it's time to back up your application data, the Backup Manager calls your onBackup()
method. This is where you must provide your application data to the Backup Manager so it can be saved to cloud storage.
Only the Backup Manager can call your backup agent's onBackup()
method. Each time that your application data changes and you want to perform a backup, you must request a backup operation by calling dataChanged()
(see Requesting Backup for more information). A backup request does not result in an immediate call to your onBackup()
method. Instead, the Backup Manager waits for an appropriate time, then performs backup for all applications that have requested a backup since the last backup was performed.
Tip: While developing your application, you can initiate an immediate backup operation from the Backup Manager with the bmgr
tool.
When the Backup Manager calls your onBackup()
method, it passes three parameters:
oldState
ParcelFileDescriptor
pointing to the last backup state provided by your application. This is not the backup data from cloud storage, but a local representation of the data that was backed up the last time
onBackup()
was called (as defined by
newState
, below, or from
onRestore()
!more about this in the next section). Because
onBackup()
does not allow you to read existing backup data in the cloud storage, you can use this local representation to determine whether your data has changed since the last backup.
data
BackupDataOutput
object, which you use to deliver your backup data to the Backup Manager.
newState
ParcelFileDescriptor
pointing to a file in which you must write a representation of the data that you delivered to
data
(a representation can be as simple as the last-modified timestamp for your file). This object is returned as
oldState
the next time the Backup Manager calls your
onBackup()
method. If you do not write your backup data to
newState
, then
oldState
will point to an empty file next time Backup Manager calls
onBackup()
.
Using these parameters, you should implement your onBackup()
method to do the following:
oldState
to your current data. How you read data in oldState
depends on how you originally wrote it to newState
(see step 3). The easiest way to record the state of a file is with its last-modified timestamp. For example, here's how you can read and compare a timestamp from oldState
: // Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = mDataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
If nothing has changed and you don't need to back up, skip to step 3.
oldState
, write the current data to data
to back it up to the cloud storage. You must write each chunk of data as an "entity" in the BackupDataOutput
. An entity is a flattened binary data record that is identified by a unique key string. Thus, the data set that you back up is conceptually a set of key-value pairs.
To add an entity to your backup data set, you must:
writeEntityHeader()
, passing a unique string key for the data you're about to write and the data size.writeEntityData()
, passing a byte buffer that contains your data and the number of bytes to write from the buffer (which should match the size passed to writeEntityHeader()
).For example, the following code flattens some data into a byte stream and writes it into a single entity:
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeUTF(mPlayerName); outWriter.writeInt(mPlayerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); data.writeEntityData(buffer, len);
Perform this for each piece of data that you want to back up. How you divide your data into entities is up to you (and you might use just one entity).
newState
ParcelFileDescriptor
. The Backup Manager retains this object locally as a representation of the data that is currently backed up. It passes this back to you as oldState
the next time it calls onBackup()
so you can determine whether another backup is necessary (as handled in step 1). If you do not write the current data state to this file, then oldState
will be empty during the next callback. The following example saves a representation of the current data into newState
using the file's last-modified timestamp:
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = mDataFile.lastModified(); out.writeLong(modified);
Caution: If your application data is saved to a file, make sure that you use synchronized statements while accessing the file so that your backup agent does not read the file while an Activity in your application is also writing the file.
","BackupAgent","android.app.backup.BackupAgent","class",0 ,"Extending BackupAgent","Performing restore","When it's time to restore your application data, the Backup Manager calls your backup agent's onRestore()
method. When it calls this method, the Backup Manager delivers your backup data so you can restore it onto the device.
Only the Backup Manager can call onRestore()
, which happens automatically when the system installs your application and finds existing backup data. However, you can request a restore operation for your application by calling requestRestore()
(see Requesting restore for more information).
Note: While developing your application, you can also request a restore operation with the bmgr
tool.
When the Backup Manager calls your onRestore()
method, it passes three parameters:
data
BackupDataInput
, which allows you to read your backup data.
appVersionCode
android:versionCode
manifest attribute, as it was when this data was backed up. You can use this to cross-check the current application version and determine if the data format is compatible. For more information about using this to handle different versions of restore data, see the section below about
Checking the Restore Data Version.
newState
ParcelFileDescriptor
pointing to a file in which you must write the final backup state that was provided with
data
. This object is returned as
oldState
the next time
onBackup()
is called. Recall that you must also write the same
newState
object in the
onBackup()
callback!also doing it here ensures that the
oldState
object given to
onBackup()
is valid even the first time
onBackup()
is called after the device is restored.
In your implementation of onRestore()
, you should call readNextHeader()
on the data
to iterate through all entities in the data set. For each entity found, do the following:
getKey()
.BackupAgent
class. When the key matches one of your known key strings, enter into a statement to extract the entity data and save it to the device:
getDataSize()
and create a byte array of that size.readEntityData()
and pass it the byte array, which is where the data will go, and specify the start offset and the size to read.newState
parameter the same as you do during onBackup()
. For example, here's how you can restore the data backed up by the example in the previous section:
@Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // There should be only one entity, but the safest // way to consume it is using a while loop while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); // If the key is ours (for saving top score). Note this key was used when // we wrote the backup entity header if (TOPSCORE_BACKUP_KEY.equals(key)) { // Create an input stream for the BackupDataInput byte[] dataBuf = new byte[dataSize]; data.readEntityData(dataBuf, 0, dataSize); ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); DataInputStream in = new DataInputStream(baStream); // Read the player name and score from the backup data mPlayerName = in.readUTF(); mPlayerScore = in.readInt(); // Record the score on the device (to a file or something) recordScore(mPlayerName, mPlayerScore); } else { // We don't know this entity key. Skip it. (Shouldn't happen.) data.skipEntityData(); } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); out.writeUTF(mPlayerName); out.writeInt(mPlayerScore); }
In this example, the appVersionCode
parameter passed to onRestore()
is not used. However, you might want to use it if you've chosen to perform backup when the user's version of the application has actually moved backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see the section about Checking the Restore Data Version.
For an example implementation of BackupAgent
, see the ExampleAgent
class in the Backup and Restore sample application.
You should build your backup agent using BackupAgentHelper
if you want to back up complete files (from either SharedPreferences
or internal storage). Building your backup agent with BackupAgentHelper
requires far less code than extending BackupAgent
, because you don't have to implement onBackup()
and onRestore()
.
Your implementation of BackupAgentHelper
must use one or more backup helpers. A backup helper is a specialized component that BackupAgentHelper
summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
SharedPreferencesBackupHelper
to backup SharedPreferences
files.FileBackupHelper
to backup files from internal storage.You can include multiple helpers in your BackupAgentHelper
, but only one helper is needed for each data type. That is, if you have multiple SharedPreferences
files, then you need only one SharedPreferencesBackupHelper
.
For each helper you want to add to your BackupAgentHelper
, you must do the following during your onCreate()
method:
addHelper()
to add the helper to your BackupAgentHelper
.The following sections describe how to create a backup agent using each of the available helpers.
","BackupAgent","android.app.backup.BackupAgent","class",0 ,"Extending BackupAgentHelper","Backing up SharedPreferences","When you instantiate a SharedPreferencesBackupHelper
, you must include the name of one or more SharedPreferences
files.
For example, to back up a SharedPreferences
file named "user_preferences", a complete backup agent using BackupAgentHelper
looks like this:
public class MyPrefsBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String PREFS = "user_preferences"; // A key to uniquely identify the set of backup data static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
That's it! That's your entire backup agent. The SharedPreferencesBackupHelper
includes all the code needed to backup and restore a SharedPreferences
file.
When the Backup Manager calls onBackup()
and onRestore()
, BackupAgentHelper
calls your backup helpers to perform backup and restore for your specified files.
Note: SharedPreferences
are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.
When you instantiate a FileBackupHelper
, you must include the name of one or more files that are saved to your application's internal storage (as specified by getFilesDir()
, which is the same location where openFileOutput()
writes files).
For example, to backup two files named "scores" and "stats," a backup agent using BackupAgentHelper
looks like this:
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
The FileBackupHelper
includes all the code necessary to backup and restore files that are saved to your application's internal storage..
However, reading and writing to files on internal storage is not threadsafe. To ensure that your backup agent does not read or write your files at the same time as your activities, you must use synchronized statements each time you perform a read or write. For example, in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:
// Object for intrinsic lock static final Object sDataLock = new Object();
Then create a synchronized statement with this lock each time you read or write the files. For example, here's a synchronized statement for writing the latest score in a game to a file:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir()
, TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
You should synchronize your read statements with the same lock.
Then, in your BackupAgentHelper
, you must override onBackup()
and onRestore()
to synchronize the backup and restore operations with the same intrinsic lock. For example, the MyFileBackupAgent
example from above needs the following methods:
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs backup synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
That's it. All you need to do is add your FileBackupHelper
in the onCreate()
method and override onBackup()
and onRestore()
to synchronize read and write operations.
For an example implementation of BackupAgentHelper
with FileBackupHelper
, see the FileHelperExampleAgent
class in the Backup and Restore sample application.
When the Backup Manager saves your data to cloud storage, it automatically includes the version of your application, as defined by your manifest file's android:versionCode
attribute. Before the Backup Manager calls your backup agent to restore your data, it looks at the android:versionCode
of the installed application and compares it to the value recorded in the restore data set. If the version recorded in the restore data set is newer than the application version on the device, then the user has downgraded their application. In this case, the Backup Manager will abort the restore operation for your application and not call your onRestore()
method, because the restore set is considered meaningless to an older version.
You can override this behavior with the android:restoreAnyVersion
attribute. This attribute is either "true
" or "false
" to indicate whether you want to restore the application regardless of the restore set version. The default value is "false
". If you define this to be "true
" then the Backup Manager will ignore the android:versionCode
and call your onRestore()
method in all cases. In doing so, you can manually check for the version difference in your onRestore()
method and take any steps necessary to make the data compatible if the versions conflict.
To help you handle different versions during a restore operation, the onRestore()
method passes you the version code included with the restore data set as the appVersionCode
parameter. You can then query the current application's version code with the PackageInfo.versionCode
field. For example:
PackageInfo info; try { String name =getPackageName
(); info =getPackageManager
().getPackageInfo
(name,0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Then simply compare the version
acquired from PackageInfo
to the appVersionCode
passed into onRestore()
.
Caution: Be certain you understand the consequences of setting android:restoreAnyVersion
to "true
" for your application. If each version of your application that supports backup does not properly account for variations in your data format during onRestore()
, then the data on the device could be saved in a format incompatible with the version currently installed on the device.
You can request a backup operation at any time by calling dataChanged()
. This method notifies the Backup Manager that you'd like to backup your data using your backup agent. The Backup Manager then calls your backup agent's onBackup()
method at an opportune time in the future. Typically, you should request a backup each time your data changes (such as when the user changes an application preference that you'd like to back up). If you call dataChanged()
several times consecutively, before the Backup Manager requests a backup from your agent, your agent still receives just one call to onBackup()
.
Note: While developing your application, you can request a backup and initiate an immediate backup operation with the bmgr
tool.
During the normal life of your application, you shouldn't need to request a restore operation. They system automatically checks for backup data and performs a restore when your application is installed. However, you can manually request a restore operation by calling requestRestore()
, if necessary. In which case, the Backup Manager calls your onRestore()
implementation, passing the data from the current set of backup data.
Note: While developing your application, you can request a restore operation with the bmgr
tool.
When it's time to back up your application data, the Backup Manager calls your onBackup()
method. This is where you must provide your application data to the Backup Manager so it can be saved to cloud storage.
Only the Backup Manager can call your backup agent's onBackup()
method. Each time that your application data changes and you want to perform a backup, you must request a backup operation by calling dataChanged()
(see Requesting Backup for more information). A backup request does not result in an immediate call to your onBackup()
method. Instead, the Backup Manager waits for an appropriate time, then performs backup for all applications that have requested a backup since the last backup was performed.
Tip: While developing your application, you can initiate an immediate backup operation from the Backup Manager with the bmgr
tool.
When the Backup Manager calls your onBackup()
method, it passes three parameters:
oldState
ParcelFileDescriptor
pointing to the last backup state provided by your application. This is not the backup data from cloud storage, but a local representation of the data that was backed up the last time
onBackup()
was called (as defined by
newState
, below, or from
onRestore()
!more about this in the next section). Because
onBackup()
does not allow you to read existing backup data in the cloud storage, you can use this local representation to determine whether your data has changed since the last backup.
data
BackupDataOutput
object, which you use to deliver your backup data to the Backup Manager.
newState
ParcelFileDescriptor
pointing to a file in which you must write a representation of the data that you delivered to
data
(a representation can be as simple as the last-modified timestamp for your file). This object is returned as
oldState
the next time the Backup Manager calls your
onBackup()
method. If you do not write your backup data to
newState
, then
oldState
will point to an empty file next time Backup Manager calls
onBackup()
.
Using these parameters, you should implement your onBackup()
method to do the following:
oldState
to your current data. How you read data in oldState
depends on how you originally wrote it to newState
(see step 3). The easiest way to record the state of a file is with its last-modified timestamp. For example, here's how you can read and compare a timestamp from oldState
: // Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = mDataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
If nothing has changed and you don't need to back up, skip to step 3.
oldState
, write the current data to data
to back it up to the cloud storage. You must write each chunk of data as an "entity" in the BackupDataOutput
. An entity is a flattened binary data record that is identified by a unique key string. Thus, the data set that you back up is conceptually a set of key-value pairs.
To add an entity to your backup data set, you must:
writeEntityHeader()
, passing a unique string key for the data you're about to write and the data size.writeEntityData()
, passing a byte buffer that contains your data and the number of bytes to write from the buffer (which should match the size passed to writeEntityHeader()
).For example, the following code flattens some data into a byte stream and writes it into a single entity:
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeUTF(mPlayerName); outWriter.writeInt(mPlayerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); data.writeEntityData(buffer, len);
Perform this for each piece of data that you want to back up. How you divide your data into entities is up to you (and you might use just one entity).
newState
ParcelFileDescriptor
. The Backup Manager retains this object locally as a representation of the data that is currently backed up. It passes this back to you as oldState
the next time it calls onBackup()
so you can determine whether another backup is necessary (as handled in step 1). If you do not write the current data state to this file, then oldState
will be empty during the next callback. The following example saves a representation of the current data into newState
using the file's last-modified timestamp:
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = mDataFile.lastModified(); out.writeLong(modified);
Caution: If your application data is saved to a file, make sure that you use synchronized statements while accessing the file so that your backup agent does not read the file while an Activity in your application is also writing the file.
","BackupDataOutput","android.app.backup.BackupDataOutput","class",0 ,"Extending BackupAgent","Performing backup","When it's time to back up your application data, the Backup Manager calls your onBackup()
method. This is where you must provide your application data to the Backup Manager so it can be saved to cloud storage.
Only the Backup Manager can call your backup agent's onBackup()
method. Each time that your application data changes and you want to perform a backup, you must request a backup operation by calling dataChanged()
(see Requesting Backup for more information). A backup request does not result in an immediate call to your onBackup()
method. Instead, the Backup Manager waits for an appropriate time, then performs backup for all applications that have requested a backup since the last backup was performed.
Tip: While developing your application, you can initiate an immediate backup operation from the Backup Manager with the bmgr
tool.
When the Backup Manager calls your onBackup()
method, it passes three parameters:
oldState
ParcelFileDescriptor
pointing to the last backup state provided by your application. This is not the backup data from cloud storage, but a local representation of the data that was backed up the last time
onBackup()
was called (as defined by
newState
, below, or from
onRestore()
!more about this in the next section). Because
onBackup()
does not allow you to read existing backup data in the cloud storage, you can use this local representation to determine whether your data has changed since the last backup.
data
BackupDataOutput
object, which you use to deliver your backup data to the Backup Manager.
newState
ParcelFileDescriptor
pointing to a file in which you must write a representation of the data that you delivered to
data
(a representation can be as simple as the last-modified timestamp for your file). This object is returned as
oldState
the next time the Backup Manager calls your
onBackup()
method. If you do not write your backup data to
newState
, then
oldState
will point to an empty file next time Backup Manager calls
onBackup()
.
Using these parameters, you should implement your onBackup()
method to do the following:
oldState
to your current data. How you read data in oldState
depends on how you originally wrote it to newState
(see step 3). The easiest way to record the state of a file is with its last-modified timestamp. For example, here's how you can read and compare a timestamp from oldState
: // Get the oldState input stream FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); DataInputStream in = new DataInputStream(instream); try { // Get the last modified timestamp from the state file and data file long stateModified = in.readLong(); long fileModified = mDataFile.lastModified(); if (stateModified != fileModified) { // The file has been modified, so do a backup // Or the time on the device changed, so be safe and do a backup } else { // Don't back up because the file hasn't changed return; } } catch (IOException e) { // Unable to read state file... be safe and do a backup }
If nothing has changed and you don't need to back up, skip to step 3.
oldState
, write the current data to data
to back it up to the cloud storage. You must write each chunk of data as an "entity" in the BackupDataOutput
. An entity is a flattened binary data record that is identified by a unique key string. Thus, the data set that you back up is conceptually a set of key-value pairs.
To add an entity to your backup data set, you must:
writeEntityHeader()
, passing a unique string key for the data you're about to write and the data size.writeEntityData()
, passing a byte buffer that contains your data and the number of bytes to write from the buffer (which should match the size passed to writeEntityHeader()
).For example, the following code flattens some data into a byte stream and writes it into a single entity:
// Create buffer stream and data output stream for our data ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); DataOutputStream outWriter = new DataOutputStream(bufStream); // Write structured data outWriter.writeUTF(mPlayerName); outWriter.writeInt(mPlayerScore); // Send the data to the Backup Manager via the BackupDataOutput byte[] buffer = bufStream.toByteArray(); int len = buffer.length; data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len); data.writeEntityData(buffer, len);
Perform this for each piece of data that you want to back up. How you divide your data into entities is up to you (and you might use just one entity).
newState
ParcelFileDescriptor
. The Backup Manager retains this object locally as a representation of the data that is currently backed up. It passes this back to you as oldState
the next time it calls onBackup()
so you can determine whether another backup is necessary (as handled in step 1). If you do not write the current data state to this file, then oldState
will be empty during the next callback. The following example saves a representation of the current data into newState
using the file's last-modified timestamp:
FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); long modified = mDataFile.lastModified(); out.writeLong(modified);
Caution: If your application data is saved to a file, make sure that you use synchronized statements while accessing the file so that your backup agent does not read the file while an Activity in your application is also writing the file.
","ParcelFileDescriptor","android.os.ParcelFileDescriptor","class",0 ,"Extending BackupAgent","Performing restore","When it's time to restore your application data, the Backup Manager calls your backup agent's onRestore()
method. When it calls this method, the Backup Manager delivers your backup data so you can restore it onto the device.
Only the Backup Manager can call onRestore()
, which happens automatically when the system installs your application and finds existing backup data. However, you can request a restore operation for your application by calling requestRestore()
(see Requesting restore for more information).
Note: While developing your application, you can also request a restore operation with the bmgr
tool.
When the Backup Manager calls your onRestore()
method, it passes three parameters:
data
BackupDataInput
, which allows you to read your backup data.
appVersionCode
android:versionCode
manifest attribute, as it was when this data was backed up. You can use this to cross-check the current application version and determine if the data format is compatible. For more information about using this to handle different versions of restore data, see the section below about
Checking the Restore Data Version.
newState
ParcelFileDescriptor
pointing to a file in which you must write the final backup state that was provided with
data
. This object is returned as
oldState
the next time
onBackup()
is called. Recall that you must also write the same
newState
object in the
onBackup()
callback!also doing it here ensures that the
oldState
object given to
onBackup()
is valid even the first time
onBackup()
is called after the device is restored.
In your implementation of onRestore()
, you should call readNextHeader()
on the data
to iterate through all entities in the data set. For each entity found, do the following:
getKey()
.BackupAgent
class. When the key matches one of your known key strings, enter into a statement to extract the entity data and save it to the device:
getDataSize()
and create a byte array of that size.readEntityData()
and pass it the byte array, which is where the data will go, and specify the start offset and the size to read.newState
parameter the same as you do during onBackup()
. For example, here's how you can restore the data backed up by the example in the previous section:
@Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // There should be only one entity, but the safest // way to consume it is using a while loop while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); // If the key is ours (for saving top score). Note this key was used when // we wrote the backup entity header if (TOPSCORE_BACKUP_KEY.equals(key)) { // Create an input stream for the BackupDataInput byte[] dataBuf = new byte[dataSize]; data.readEntityData(dataBuf, 0, dataSize); ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); DataInputStream in = new DataInputStream(baStream); // Read the player name and score from the backup data mPlayerName = in.readUTF(); mPlayerScore = in.readInt(); // Record the score on the device (to a file or something) recordScore(mPlayerName, mPlayerScore); } else { // We don't know this entity key. Skip it. (Shouldn't happen.) data.skipEntityData(); } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); out.writeUTF(mPlayerName); out.writeInt(mPlayerScore); }
In this example, the appVersionCode
parameter passed to onRestore()
is not used. However, you might want to use it if you've chosen to perform backup when the user's version of the application has actually moved backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see the section about Checking the Restore Data Version.
For an example implementation of BackupAgent
, see the ExampleAgent
class in the Backup and Restore sample application.
When it's time to restore your application data, the Backup Manager calls your backup agent's onRestore()
method. When it calls this method, the Backup Manager delivers your backup data so you can restore it onto the device.
Only the Backup Manager can call onRestore()
, which happens automatically when the system installs your application and finds existing backup data. However, you can request a restore operation for your application by calling requestRestore()
(see Requesting restore for more information).
Note: While developing your application, you can also request a restore operation with the bmgr
tool.
When the Backup Manager calls your onRestore()
method, it passes three parameters:
data
BackupDataInput
, which allows you to read your backup data.
appVersionCode
android:versionCode
manifest attribute, as it was when this data was backed up. You can use this to cross-check the current application version and determine if the data format is compatible. For more information about using this to handle different versions of restore data, see the section below about
Checking the Restore Data Version.
newState
ParcelFileDescriptor
pointing to a file in which you must write the final backup state that was provided with
data
. This object is returned as
oldState
the next time
onBackup()
is called. Recall that you must also write the same
newState
object in the
onBackup()
callback!also doing it here ensures that the
oldState
object given to
onBackup()
is valid even the first time
onBackup()
is called after the device is restored.
In your implementation of onRestore()
, you should call readNextHeader()
on the data
to iterate through all entities in the data set. For each entity found, do the following:
getKey()
.BackupAgent
class. When the key matches one of your known key strings, enter into a statement to extract the entity data and save it to the device:
getDataSize()
and create a byte array of that size.readEntityData()
and pass it the byte array, which is where the data will go, and specify the start offset and the size to read.newState
parameter the same as you do during onBackup()
. For example, here's how you can restore the data backed up by the example in the previous section:
@Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // There should be only one entity, but the safest // way to consume it is using a while loop while (data.readNextHeader()) { String key = data.getKey(); int dataSize = data.getDataSize(); // If the key is ours (for saving top score). Note this key was used when // we wrote the backup entity header if (TOPSCORE_BACKUP_KEY.equals(key)) { // Create an input stream for the BackupDataInput byte[] dataBuf = new byte[dataSize]; data.readEntityData(dataBuf, 0, dataSize); ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); DataInputStream in = new DataInputStream(baStream); // Read the player name and score from the backup data mPlayerName = in.readUTF(); mPlayerScore = in.readInt(); // Record the score on the device (to a file or something) recordScore(mPlayerName, mPlayerScore); } else { // We don't know this entity key. Skip it. (Shouldn't happen.) data.skipEntityData(); } } // Finally, write to the state blob (newState) that describes the restored data FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor()); DataOutputStream out = new DataOutputStream(outstream); out.writeUTF(mPlayerName); out.writeInt(mPlayerScore); }
In this example, the appVersionCode
parameter passed to onRestore()
is not used. However, you might want to use it if you've chosen to perform backup when the user's version of the application has actually moved backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see the section about Checking the Restore Data Version.
For an example implementation of BackupAgent
, see the ExampleAgent
class in the Backup and Restore sample application.
You should build your backup agent using BackupAgentHelper
if you want to back up complete files (from either SharedPreferences
or internal storage). Building your backup agent with BackupAgentHelper
requires far less code than extending BackupAgent
, because you don't have to implement onBackup()
and onRestore()
.
Your implementation of BackupAgentHelper
must use one or more backup helpers. A backup helper is a specialized component that BackupAgentHelper
summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
SharedPreferencesBackupHelper
to backup SharedPreferences
files.FileBackupHelper
to backup files from internal storage.You can include multiple helpers in your BackupAgentHelper
, but only one helper is needed for each data type. That is, if you have multiple SharedPreferences
files, then you need only one SharedPreferencesBackupHelper
.
For each helper you want to add to your BackupAgentHelper
, you must do the following during your onCreate()
method:
addHelper()
to add the helper to your BackupAgentHelper
.The following sections describe how to create a backup agent using each of the available helpers.
","SharedPreferencesBackupHelper","android.app.backup.SharedPreferencesBackupHelper","class",0 ,"Extending BackupAgentHelper","Backing up SharedPreferences","When you instantiate a SharedPreferencesBackupHelper
, you must include the name of one or more SharedPreferences
files.
For example, to back up a SharedPreferences
file named "user_preferences", a complete backup agent using BackupAgentHelper
looks like this:
public class MyPrefsBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String PREFS = "user_preferences"; // A key to uniquely identify the set of backup data static final String PREFS_BACKUP_KEY = "prefs"; // Allocate a helper and add it to the backup agent @Override public void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS); addHelper(PREFS_BACKUP_KEY, helper); } }
That's it! That's your entire backup agent. The SharedPreferencesBackupHelper
includes all the code needed to backup and restore a SharedPreferences
file.
When the Backup Manager calls onBackup()
and onRestore()
, BackupAgentHelper
calls your backup helpers to perform backup and restore for your specified files.
Note: SharedPreferences
are threadsafe, so you can safely read and write the shared preferences file from your backup agent and other activities.
You should build your backup agent using BackupAgentHelper
if you want to back up complete files (from either SharedPreferences
or internal storage). Building your backup agent with BackupAgentHelper
requires far less code than extending BackupAgent
, because you don't have to implement onBackup()
and onRestore()
.
Your implementation of BackupAgentHelper
must use one or more backup helpers. A backup helper is a specialized component that BackupAgentHelper
summons to perform backup and restore operations for a particular type of data. The Android framework currently provides two different helpers:
SharedPreferencesBackupHelper
to backup SharedPreferences
files.FileBackupHelper
to backup files from internal storage.You can include multiple helpers in your BackupAgentHelper
, but only one helper is needed for each data type. That is, if you have multiple SharedPreferences
files, then you need only one SharedPreferencesBackupHelper
.
For each helper you want to add to your BackupAgentHelper
, you must do the following during your onCreate()
method:
addHelper()
to add the helper to your BackupAgentHelper
.The following sections describe how to create a backup agent using each of the available helpers.
","FileBackupHelper","android.app.backup.FileBackupHelper","class",0 ,"Extending BackupAgentHelper","Backing up other files","When you instantiate a FileBackupHelper
, you must include the name of one or more files that are saved to your application's internal storage (as specified by getFilesDir()
, which is the same location where openFileOutput()
writes files).
For example, to backup two files named "scores" and "stats," a backup agent using BackupAgentHelper
looks like this:
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
The FileBackupHelper
includes all the code necessary to backup and restore files that are saved to your application's internal storage..
However, reading and writing to files on internal storage is not threadsafe. To ensure that your backup agent does not read or write your files at the same time as your activities, you must use synchronized statements each time you perform a read or write. For example, in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:
// Object for intrinsic lock static final Object sDataLock = new Object();
Then create a synchronized statement with this lock each time you read or write the files. For example, here's a synchronized statement for writing the latest score in a game to a file:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir()
, TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
You should synchronize your read statements with the same lock.
Then, in your BackupAgentHelper
, you must override onBackup()
and onRestore()
to synchronize the backup and restore operations with the same intrinsic lock. For example, the MyFileBackupAgent
example from above needs the following methods:
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs backup synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
That's it. All you need to do is add your FileBackupHelper
in the onCreate()
method and override onBackup()
and onRestore()
to synchronize read and write operations.
For an example implementation of BackupAgentHelper
with FileBackupHelper
, see the FileHelperExampleAgent
class in the Backup and Restore sample application.
When you instantiate a FileBackupHelper
, you must include the name of one or more files that are saved to your application's internal storage (as specified by getFilesDir()
, which is the same location where openFileOutput()
writes files).
For example, to backup two files named "scores" and "stats," a backup agent using BackupAgentHelper
looks like this:
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
The FileBackupHelper
includes all the code necessary to backup and restore files that are saved to your application's internal storage..
However, reading and writing to files on internal storage is not threadsafe. To ensure that your backup agent does not read or write your files at the same time as your activities, you must use synchronized statements each time you perform a read or write. For example, in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:
// Object for intrinsic lock static final Object sDataLock = new Object();
Then create a synchronized statement with this lock each time you read or write the files. For example, here's a synchronized statement for writing the latest score in a game to a file:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir()
, TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
You should synchronize your read statements with the same lock.
Then, in your BackupAgentHelper
, you must override onBackup()
and onRestore()
to synchronize the backup and restore operations with the same intrinsic lock. For example, the MyFileBackupAgent
example from above needs the following methods:
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs backup synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
That's it. All you need to do is add your FileBackupHelper
in the onCreate()
method and override onBackup()
and onRestore()
to synchronize read and write operations.
For an example implementation of BackupAgentHelper
with FileBackupHelper
, see the FileHelperExampleAgent
class in the Backup and Restore sample application.
Shared preferences are not strictly for saving "user preferences," such as what ringtone a user has chosen. If you're interested in creating user preferences for your application, see PreferenceActivity
, which provides an Activity framework for you to create user preferences, which will be automatically persisted (using shared preferences).
To get a SharedPreferences
object for your application, use one of two methods:
getSharedPreferences()
- Use this if you need multiple preferences files identified by name, which you specify with the first parameter.getPreferences()
- Use this if you need only one preferences file for your Activity. Because this will be the only preferences file for your Activity, you don't supply a name.To write values:
edit()
to get a SharedPreferences.Editor
.putBoolean()
and putString()
.commit()
To read values, use SharedPreferences
methods such as getBoolean()
and getString()
.
Here is an example that saves a preference for silent keypress mode in a calculator:
public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); . . . // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); // Commit the edits! editor.commit(); } }","Context","android.content.Context","class",0 ,"Storage Options","Using the Internal Storage","
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user). When the user uninstalls your application, these files are removed.
To create and write a private file to the internal storage:
openFileOutput()
with the name of the file and the operating mode. This returns a FileOutputStream
.write()
.close()
.For example:
String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close();
MODE_PRIVATE
will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND
, MODE_WORLD_READABLE
, and MODE_WORLD_WRITEABLE
.
To read a file from internal storage:
openFileInput()
and pass it the name of the file to read. This returns a FileInputStream
.read()
.close()
.Tip: If you want to save a static file in your application at compile time, save the file in your project res/raw/
directory. You can open it with openRawResource()
, passing the R.raw.<filename>
resource ID. This method returns an InputStream
that you can use to read the file (but you cannot write to the original file).
If you'd like to cache some data, rather than store it persistently, you should use getCacheDir()
to open a File
that represents the internal directory where your application should save temporary cache files.
When the device is low on internal storage space, Android may delete these cache files to recover space. However, you should not rely on the system to clean up these files for you. You should always maintain the cache files yourself and stay within a reasonable limit of space consumed, such as 1MB. When the user uninstalls your application, these files are removed.
","Context","android.content.Context","class",0 ,"Using the Internal Storage","Other useful methods","getFilesDir()
getDir()
deleteFile()
fileList()
If you're using API Level 8 or greater, use getExternalFilesDir()
to open a File
that represents the external storage directory where you should save your files. This method takes a type
parameter that specifies the type of subdirectory you want, such as DIRECTORY_MUSIC
and DIRECTORY_RINGTONES
(pass null
to receive the root of your application's file directory). This method will create the appropriate directory if necessary. By specifying the type of directory, you ensure that the Android's media scanner will properly categorize your files in the system (for example, ringtones are identified as ringtones and not music). If the user uninstalls your application, this directory and all its contents will be deleted.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
, to open a File
representing the root of the external storage. You should then write your data in the following directory:
/Android/data/<package_name>/files/
The <package_name>
is your Java-style package name, such as "com.example.android.app
". If the user's device is running API Level 8 or greater and they uninstall your application, this directory and all its contents will be deleted.
If you're using API Level 8 or greater, use getExternalCacheDir()
to open a File
that represents the external storage directory where you should save cache files. If the user uninstalls your application, these files will be automatically deleted. However, during the life of your application, you should manage these cache files and remove those that aren't needed in order to preserve file space.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
to open a File
that represents the root of the external storage, then write your cache data in the following directory:
/Android/data/<package_name>/cache/
The <package_name>
is your Java-style package name, such as "com.example.android.app
".
When you instantiate a FileBackupHelper
, you must include the name of one or more files that are saved to your application's internal storage (as specified by getFilesDir()
, which is the same location where openFileOutput()
writes files).
For example, to backup two files named "scores" and "stats," a backup agent using BackupAgentHelper
looks like this:
public class MyFileBackupAgent extends BackupAgentHelper { // The name of the file static final String TOP_SCORES = "scores"; static final String PLAYER_STATS = "stats"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, TOP_SCORES, PLAYER_STATS); addHelper(FILES_BACKUP_KEY, helper); } }
The FileBackupHelper
includes all the code necessary to backup and restore files that are saved to your application's internal storage..
However, reading and writing to files on internal storage is not threadsafe. To ensure that your backup agent does not read or write your files at the same time as your activities, you must use synchronized statements each time you perform a read or write. For example, in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:
// Object for intrinsic lock static final Object sDataLock = new Object();
Then create a synchronized statement with this lock each time you read or write the files. For example, here's a synchronized statement for writing the latest score in a game to a file:
try {
synchronized (MyActivity.sDataLock) {
File dataFile = new File(getFilesDir()
, TOP_SCORES);
RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
raFile.writeInt(score);
}
} catch (IOException e) {
Log.e(TAG, "Unable to write to file");
}
You should synchronize your read statements with the same lock.
Then, in your BackupAgentHelper
, you must override onBackup()
and onRestore()
to synchronize the backup and restore operations with the same intrinsic lock. For example, the MyFileBackupAgent
example from above needs the following methods:
@Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper performs backup synchronized (MyActivity.sDataLock) { super.onBackup(oldState, data, newState); } } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // Hold the lock while the FileBackupHelper restores the file synchronized (MyActivity.sDataLock) { super.onRestore(data, appVersionCode, newState); } }
That's it. All you need to do is add your FileBackupHelper
in the onCreate()
method and override onBackup()
and onRestore()
to synchronize read and write operations.
For an example implementation of BackupAgentHelper
with FileBackupHelper
, see the FileHelperExampleAgent
class in the Backup and Restore sample application.
When the Backup Manager saves your data to cloud storage, it automatically includes the version of your application, as defined by your manifest file's android:versionCode
attribute. Before the Backup Manager calls your backup agent to restore your data, it looks at the android:versionCode
of the installed application and compares it to the value recorded in the restore data set. If the version recorded in the restore data set is newer than the application version on the device, then the user has downgraded their application. In this case, the Backup Manager will abort the restore operation for your application and not call your onRestore()
method, because the restore set is considered meaningless to an older version.
You can override this behavior with the android:restoreAnyVersion
attribute. This attribute is either "true
" or "false
" to indicate whether you want to restore the application regardless of the restore set version. The default value is "false
". If you define this to be "true
" then the Backup Manager will ignore the android:versionCode
and call your onRestore()
method in all cases. In doing so, you can manually check for the version difference in your onRestore()
method and take any steps necessary to make the data compatible if the versions conflict.
To help you handle different versions during a restore operation, the onRestore()
method passes you the version code included with the restore data set as the appVersionCode
parameter. You can then query the current application's version code with the PackageInfo.versionCode
field. For example:
PackageInfo info; try { String name =getPackageName
(); info =getPackageManager
().getPackageInfo
(name,0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Then simply compare the version
acquired from PackageInfo
to the appVersionCode
passed into onRestore()
.
Caution: Be certain you understand the consequences of setting android:restoreAnyVersion
to "true
" for your application. If each version of your application that supports backup does not properly account for variations in your data format during onRestore()
, then the data on the device could be saved in a format incompatible with the version currently installed on the device.
When the Backup Manager saves your data to cloud storage, it automatically includes the version of your application, as defined by your manifest file's android:versionCode
attribute. Before the Backup Manager calls your backup agent to restore your data, it looks at the android:versionCode
of the installed application and compares it to the value recorded in the restore data set. If the version recorded in the restore data set is newer than the application version on the device, then the user has downgraded their application. In this case, the Backup Manager will abort the restore operation for your application and not call your onRestore()
method, because the restore set is considered meaningless to an older version.
You can override this behavior with the android:restoreAnyVersion
attribute. This attribute is either "true
" or "false
" to indicate whether you want to restore the application regardless of the restore set version. The default value is "false
". If you define this to be "true
" then the Backup Manager will ignore the android:versionCode
and call your onRestore()
method in all cases. In doing so, you can manually check for the version difference in your onRestore()
method and take any steps necessary to make the data compatible if the versions conflict.
To help you handle different versions during a restore operation, the onRestore()
method passes you the version code included with the restore data set as the appVersionCode
parameter. You can then query the current application's version code with the PackageInfo.versionCode
field. For example:
PackageInfo info; try { String name =getPackageName
(); info =getPackageManager
().getPackageInfo
(name,0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Then simply compare the version
acquired from PackageInfo
to the appVersionCode
passed into onRestore()
.
Caution: Be certain you understand the consequences of setting android:restoreAnyVersion
to "true
" for your application. If each version of your application that supports backup does not properly account for variations in your data format during onRestore()
, then the data on the device could be saved in a format incompatible with the version currently installed on the device.
When the Backup Manager saves your data to cloud storage, it automatically includes the version of your application, as defined by your manifest file's android:versionCode
attribute. Before the Backup Manager calls your backup agent to restore your data, it looks at the android:versionCode
of the installed application and compares it to the value recorded in the restore data set. If the version recorded in the restore data set is newer than the application version on the device, then the user has downgraded their application. In this case, the Backup Manager will abort the restore operation for your application and not call your onRestore()
method, because the restore set is considered meaningless to an older version.
You can override this behavior with the android:restoreAnyVersion
attribute. This attribute is either "true
" or "false
" to indicate whether you want to restore the application regardless of the restore set version. The default value is "false
". If you define this to be "true
" then the Backup Manager will ignore the android:versionCode
and call your onRestore()
method in all cases. In doing so, you can manually check for the version difference in your onRestore()
method and take any steps necessary to make the data compatible if the versions conflict.
To help you handle different versions during a restore operation, the onRestore()
method passes you the version code included with the restore data set as the appVersionCode
parameter. You can then query the current application's version code with the PackageInfo.versionCode
field. For example:
PackageInfo info; try { String name =getPackageName
(); info =getPackageManager
().getPackageInfo
(name,0); } catch (NameNotFoundException nnfe) { info = null; } int version; if (info != null) { version = info.versionCode; }
Then simply compare the version
acquired from PackageInfo
to the appVersionCode
passed into onRestore()
.
Caution: Be certain you understand the consequences of setting android:restoreAnyVersion
to "true
" for your application. If each version of your application that supports backup does not properly account for variations in your data format during onRestore()
, then the data on the device could be saved in a format incompatible with the version currently installed on the device.
Shared preferences are not strictly for saving "user preferences," such as what ringtone a user has chosen. If you're interested in creating user preferences for your application, see PreferenceActivity
, which provides an Activity framework for you to create user preferences, which will be automatically persisted (using shared preferences).
To get a SharedPreferences
object for your application, use one of two methods:
getSharedPreferences()
- Use this if you need multiple preferences files identified by name, which you specify with the first parameter.getPreferences()
- Use this if you need only one preferences file for your Activity. Because this will be the only preferences file for your Activity, you don't supply a name.To write values:
edit()
to get a SharedPreferences.Editor
.putBoolean()
and putString()
.commit()
To read values, use SharedPreferences
methods such as getBoolean()
and getString()
.
Here is an example that saves a preference for silent keypress mode in a calculator:
public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); . . . // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); // Commit the edits! editor.commit(); } }","Editor","android.content.SharedPreferences.Editor","class",0 ,"Using Shared Preferences","User Preferences","
Shared preferences are not strictly for saving "user preferences," such as what ringtone a user has chosen. If you're interested in creating user preferences for your application, see PreferenceActivity
, which provides an Activity framework for you to create user preferences, which will be automatically persisted (using shared preferences).
To get a SharedPreferences
object for your application, use one of two methods:
getSharedPreferences()
- Use this if you need multiple preferences files identified by name, which you specify with the first parameter.getPreferences()
- Use this if you need only one preferences file for your Activity. Because this will be the only preferences file for your Activity, you don't supply a name.To write values:
edit()
to get a SharedPreferences.Editor
.putBoolean()
and putString()
.commit()
To read values, use SharedPreferences
methods such as getBoolean()
and getString()
.
Here is an example that saves a preference for silent keypress mode in a calculator:
public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); . . . // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); // Commit the edits! editor.commit(); } }","PreferenceActivity","android.preference.PreferenceActivity","class",0 ,"Using Shared Preferences","User Preferences","
Shared preferences are not strictly for saving "user preferences," such as what ringtone a user has chosen. If you're interested in creating user preferences for your application, see PreferenceActivity
, which provides an Activity framework for you to create user preferences, which will be automatically persisted (using shared preferences).
To get a SharedPreferences
object for your application, use one of two methods:
getSharedPreferences()
- Use this if you need multiple preferences files identified by name, which you specify with the first parameter.getPreferences()
- Use this if you need only one preferences file for your Activity. Because this will be the only preferences file for your Activity, you don't supply a name.To write values:
edit()
to get a SharedPreferences.Editor
.putBoolean()
and putString()
.commit()
To read values, use SharedPreferences
methods such as getBoolean()
and getString()
.
Here is an example that saves a preference for silent keypress mode in a calculator:
public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); . . . // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); // Commit the edits! editor.commit(); } }","Activity","android.app.Activity","class",0 ,"Storage Options","Using the Internal Storage","
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user). When the user uninstalls your application, these files are removed.
To create and write a private file to the internal storage:
openFileOutput()
with the name of the file and the operating mode. This returns a FileOutputStream
.write()
.close()
.For example:
String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close();
MODE_PRIVATE
will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND
, MODE_WORLD_READABLE
, and MODE_WORLD_WRITEABLE
.
To read a file from internal storage:
openFileInput()
and pass it the name of the file to read. This returns a FileInputStream
.read()
.close()
.Tip: If you want to save a static file in your application at compile time, save the file in your project res/raw/
directory. You can open it with openRawResource()
, passing the R.raw.<filename>
resource ID. This method returns an InputStream
that you can use to read the file (but you cannot write to the original file).
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user). When the user uninstalls your application, these files are removed.
To create and write a private file to the internal storage:
openFileOutput()
with the name of the file and the operating mode. This returns a FileOutputStream
.write()
.close()
.For example:
String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close();
MODE_PRIVATE
will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND
, MODE_WORLD_READABLE
, and MODE_WORLD_WRITEABLE
.
To read a file from internal storage:
openFileInput()
and pass it the name of the file to read. This returns a FileInputStream
.read()
.close()
.Tip: If you want to save a static file in your application at compile time, save the file in your project res/raw/
directory. You can open it with openRawResource()
, passing the R.raw.<filename>
resource ID. This method returns an InputStream
that you can use to read the file (but you cannot write to the original file).
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user). When the user uninstalls your application, these files are removed.
To create and write a private file to the internal storage:
openFileOutput()
with the name of the file and the operating mode. This returns a FileOutputStream
.write()
.close()
.For example:
String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close();
MODE_PRIVATE
will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND
, MODE_WORLD_READABLE
, and MODE_WORLD_WRITEABLE
.
To read a file from internal storage:
openFileInput()
and pass it the name of the file to read. This returns a FileInputStream
.read()
.close()
.Tip: If you want to save a static file in your application at compile time, save the file in your project res/raw/
directory. You can open it with openRawResource()
, passing the R.raw.<filename>
resource ID. This method returns an InputStream
that you can use to read the file (but you cannot write to the original file).
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user). When the user uninstalls your application, these files are removed.
To create and write a private file to the internal storage:
openFileOutput()
with the name of the file and the operating mode. This returns a FileOutputStream
.write()
.close()
.For example:
String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close();
MODE_PRIVATE
will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND
, MODE_WORLD_READABLE
, and MODE_WORLD_WRITEABLE
.
To read a file from internal storage:
openFileInput()
and pass it the name of the file to read. This returns a FileInputStream
.read()
.close()
.Tip: If you want to save a static file in your application at compile time, save the file in your project res/raw/
directory. You can open it with openRawResource()
, passing the R.raw.<filename>
resource ID. This method returns an InputStream
that you can use to read the file (but you cannot write to the original file).
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user). When the user uninstalls your application, these files are removed.
To create and write a private file to the internal storage:
openFileOutput()
with the name of the file and the operating mode. This returns a FileOutputStream
.write()
.close()
.For example:
String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close();
MODE_PRIVATE
will create the file (or replace a file of the same name) and make it private to your application. Other modes available are: MODE_APPEND
, MODE_WORLD_READABLE
, and MODE_WORLD_WRITEABLE
.
To read a file from internal storage:
openFileInput()
and pass it the name of the file to read. This returns a FileInputStream
.read()
.close()
.Tip: If you want to save a static file in your application at compile time, save the file in your project res/raw/
directory. You can open it with openRawResource()
, passing the R.raw.<filename>
resource ID. This method returns an InputStream
that you can use to read the file (but you cannot write to the original file).
If you'd like to cache some data, rather than store it persistently, you should use getCacheDir()
to open a File
that represents the internal directory where your application should save temporary cache files.
When the device is low on internal storage space, Android may delete these cache files to recover space. However, you should not rely on the system to clean up these files for you. You should always maintain the cache files yourself and stay within a reasonable limit of space consumed, such as 1MB. When the user uninstalls your application, these files are removed.
","File","java.io.File","class",1 ,"Using the External Storage","Accessing files on external storage","If you're using API Level 8 or greater, use getExternalFilesDir()
to open a File
that represents the external storage directory where you should save your files. This method takes a type
parameter that specifies the type of subdirectory you want, such as DIRECTORY_MUSIC
and DIRECTORY_RINGTONES
(pass null
to receive the root of your application's file directory). This method will create the appropriate directory if necessary. By specifying the type of directory, you ensure that the Android's media scanner will properly categorize your files in the system (for example, ringtones are identified as ringtones and not music). If the user uninstalls your application, this directory and all its contents will be deleted.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
, to open a File
representing the root of the external storage. You should then write your data in the following directory:
/Android/data/<package_name>/files/
The <package_name>
is your Java-style package name, such as "com.example.android.app
". If the user's device is running API Level 8 or greater and they uninstall your application, this directory and all its contents will be deleted.
If you want to save files that are not specific to your application and that should not be deleted when your application is uninstalled, save them to one of the public directories on the external storage. These directories lay at the root of the external storage, such as Music/
, Pictures/
, Ringtones/
, and others.
In API Level 8 or greater, use getExternalStoragePublicDirectory()
, passing it the type of public directory you want, such as DIRECTORY_MUSIC
, DIRECTORY_PICTURES
, DIRECTORY_RINGTONES
, or others. This method will create the appropriate directory if necessary.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
to open a File
that represents the root of the external storage, then save your shared files in one of the following directories:
Music/
- Media scanner classifies all media found here as user music.Podcasts/
- Media scanner classifies all media found here as a podcast.Ringtones/
- Media scanner classifies all media found here as a ringtone.Alarms/
- Media scanner classifies all media found here as an alarm sound.Notifications/
- Media scanner classifies all media found here as a notification sound.Pictures/
- All photos (excluding those taken with the camera).Movies/
- All movies (excluding those taken with the camcorder).Download/
- Miscellaneous downloads.If you're using API Level 8 or greater, use getExternalCacheDir()
to open a File
that represents the external storage directory where you should save cache files. If the user uninstalls your application, these files will be automatically deleted. However, during the life of your application, you should manage these cache files and remove those that aren't needed in order to preserve file space.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
to open a File
that represents the root of the external storage, then write your cache data in the following directory:
/Android/data/<package_name>/cache/
The <package_name>
is your Java-style package name, such as "com.example.android.app
".
Before you do any work with the external storage, you should always call getExternalStorageState()
to check whether the media is available. The media might be mounted to a computer, missing, read-only, or in some other state. For example, here's how you can check the availability:
boolean mExternalStorageAvailable = false; boolean mExternalStorageWriteable = false; String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { // We can read and write the media mExternalStorageAvailable = mExternalStorageWriteable = true; } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // We can only read the media mExternalStorageAvailable = true; mExternalStorageWriteable = false; } else { // Something else is wrong. It may be one of many other states, but all we need // to know is we can neither read nor write mExternalStorageAvailable = mExternalStorageWriteable = false; }
This example checks whether the external storage is available to read and write. The getExternalStorageState()
method returns other states that you might want to check, such as whether the media is being shared (connected to a computer), is missing entirely, has been removed badly, etc. You can use these to notify the user with more information when your application needs to access the media.
If you're using API Level 8 or greater, use getExternalFilesDir()
to open a File
that represents the external storage directory where you should save your files. This method takes a type
parameter that specifies the type of subdirectory you want, such as DIRECTORY_MUSIC
and DIRECTORY_RINGTONES
(pass null
to receive the root of your application's file directory). This method will create the appropriate directory if necessary. By specifying the type of directory, you ensure that the Android's media scanner will properly categorize your files in the system (for example, ringtones are identified as ringtones and not music). If the user uninstalls your application, this directory and all its contents will be deleted.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
, to open a File
representing the root of the external storage. You should then write your data in the following directory:
/Android/data/<package_name>/files/
The <package_name>
is your Java-style package name, such as "com.example.android.app
". If the user's device is running API Level 8 or greater and they uninstall your application, this directory and all its contents will be deleted.
If you want to save files that are not specific to your application and that should not be deleted when your application is uninstalled, save them to one of the public directories on the external storage. These directories lay at the root of the external storage, such as Music/
, Pictures/
, Ringtones/
, and others.
In API Level 8 or greater, use getExternalStoragePublicDirectory()
, passing it the type of public directory you want, such as DIRECTORY_MUSIC
, DIRECTORY_PICTURES
, DIRECTORY_RINGTONES
, or others. This method will create the appropriate directory if necessary.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
to open a File
that represents the root of the external storage, then save your shared files in one of the following directories:
Music/
- Media scanner classifies all media found here as user music.Podcasts/
- Media scanner classifies all media found here as a podcast.Ringtones/
- Media scanner classifies all media found here as a ringtone.Alarms/
- Media scanner classifies all media found here as an alarm sound.Notifications/
- Media scanner classifies all media found here as a notification sound.Pictures/
- All photos (excluding those taken with the camera).Movies/
- All movies (excluding those taken with the camcorder).Download/
- Miscellaneous downloads.If you're using API Level 8 or greater, use getExternalCacheDir()
to open a File
that represents the external storage directory where you should save cache files. If the user uninstalls your application, these files will be automatically deleted. However, during the life of your application, you should manage these cache files and remove those that aren't needed in order to preserve file space.
If you're using API Level 7 or lower, use getExternalStorageDirectory()
to open a File
that represents the root of the external storage, then write your cache data in the following directory:
/Android/data/<package_name>/cache/
The <package_name>
is your Java-style package name, such as "com.example.android.app
".
Android provides full support for SQLite databases. Any databases you create will be accessible by name to any class in the application, but not outside the application.
The recommended method to create a new SQLite database is to create a subclass of SQLiteOpenHelper
and override the onCreate()
method, in which you can execute a SQLite command to create tables in the database. For example:
public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2; private static final String DICTIONARY_TABLE_NAME = "dictionary"; private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT, " + KEY_DEFINITION + " TEXT);"; DictionaryOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DICTIONARY_TABLE_CREATE); } }
You can then get an instance of your SQLiteOpenHelper
implementation using the constructor you've defined. To write to and read from the database, call getWritableDatabase()
and getReadableDatabase()
, respectively. These both return a SQLiteDatabase
object that represents the database and provides methods for SQLite operations.
Android does not impose any limitations beyond the standard SQLite concepts. We do recommend including an autoincrement value key field that can be used as a unique ID to quickly find a record. This is not required for private data, but if you implement a content provider, you must include a unique ID using the BaseColumns._ID
constant.
You can execute SQLite queries using the SQLiteDatabase
query()
methods, which accept various query parameters, such as the table to query, the projection, selection, columns, grouping, and others. For complex queries, such as those that require column aliases, you should use SQLiteQueryBuilder
, which provides several convienent methods for building queries.
Every SQLite query will return a Cursor
that points to all the rows found by the query. The Cursor
is always the mechanism with which you can navigate results from a database query and read rows and columns.
For sample apps that demonstrate how to use SQLite databases in Android, see the Note Pad and Searchable Dictionary applications.
","SQLiteDatabase","android.database.sqlite.SQLiteDatabase","class",1 ,"Storage Options","Using Databases","Android provides full support for SQLite databases. Any databases you create will be accessible by name to any class in the application, but not outside the application.
The recommended method to create a new SQLite database is to create a subclass of SQLiteOpenHelper
and override the onCreate()
method, in which you can execute a SQLite command to create tables in the database. For example:
public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2; private static final String DICTIONARY_TABLE_NAME = "dictionary"; private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT, " + KEY_DEFINITION + " TEXT);"; DictionaryOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DICTIONARY_TABLE_CREATE); } }
You can then get an instance of your SQLiteOpenHelper
implementation using the constructor you've defined. To write to and read from the database, call getWritableDatabase()
and getReadableDatabase()
, respectively. These both return a SQLiteDatabase
object that represents the database and provides methods for SQLite operations.
Android does not impose any limitations beyond the standard SQLite concepts. We do recommend including an autoincrement value key field that can be used as a unique ID to quickly find a record. This is not required for private data, but if you implement a content provider, you must include a unique ID using the BaseColumns._ID
constant.
You can execute SQLite queries using the SQLiteDatabase
query()
methods, which accept various query parameters, such as the table to query, the projection, selection, columns, grouping, and others. For complex queries, such as those that require column aliases, you should use SQLiteQueryBuilder
, which provides several convienent methods for building queries.
Every SQLite query will return a Cursor
that points to all the rows found by the query. The Cursor
is always the mechanism with which you can navigate results from a database query and read rows and columns.
For sample apps that demonstrate how to use SQLite databases in Android, see the Note Pad and Searchable Dictionary applications.
","SQLiteQueryBuilder","android.database.sqlite.SQLiteQueryBuilder","class",0 ,"Storage Options","Using Databases","Android provides full support for SQLite databases. Any databases you create will be accessible by name to any class in the application, but not outside the application.
The recommended method to create a new SQLite database is to create a subclass of SQLiteOpenHelper
and override the onCreate()
method, in which you can execute a SQLite command to create tables in the database. For example:
public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2; private static final String DICTIONARY_TABLE_NAME = "dictionary"; private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT, " + KEY_DEFINITION + " TEXT);"; DictionaryOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DICTIONARY_TABLE_CREATE); } }
You can then get an instance of your SQLiteOpenHelper
implementation using the constructor you've defined. To write to and read from the database, call getWritableDatabase()
and getReadableDatabase()
, respectively. These both return a SQLiteDatabase
object that represents the database and provides methods for SQLite operations.
Android does not impose any limitations beyond the standard SQLite concepts. We do recommend including an autoincrement value key field that can be used as a unique ID to quickly find a record. This is not required for private data, but if you implement a content provider, you must include a unique ID using the BaseColumns._ID
constant.
You can execute SQLite queries using the SQLiteDatabase
query()
methods, which accept various query parameters, such as the table to query, the projection, selection, columns, grouping, and others. For complex queries, such as those that require column aliases, you should use SQLiteQueryBuilder
, which provides several convienent methods for building queries.
Every SQLite query will return a Cursor
that points to all the rows found by the query. The Cursor
is always the mechanism with which you can navigate results from a database query and read rows and columns.
For sample apps that demonstrate how to use SQLite databases in Android, see the Note Pad and Searchable Dictionary applications.
","SQLiteOpenHelper","android.database.sqlite.SQLiteOpenHelper","class",0 ,"Storage Options","Using Databases","Android provides full support for SQLite databases. Any databases you create will be accessible by name to any class in the application, but not outside the application.
The recommended method to create a new SQLite database is to create a subclass of SQLiteOpenHelper
and override the onCreate()
method, in which you can execute a SQLite command to create tables in the database. For example:
public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2; private static final String DICTIONARY_TABLE_NAME = "dictionary"; private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT, " + KEY_DEFINITION + " TEXT);"; DictionaryOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DICTIONARY_TABLE_CREATE); } }
You can then get an instance of your SQLiteOpenHelper
implementation using the constructor you've defined. To write to and read from the database, call getWritableDatabase()
and getReadableDatabase()
, respectively. These both return a SQLiteDatabase
object that represents the database and provides methods for SQLite operations.
Android does not impose any limitations beyond the standard SQLite concepts. We do recommend including an autoincrement value key field that can be used as a unique ID to quickly find a record. This is not required for private data, but if you implement a content provider, you must include a unique ID using the BaseColumns._ID
constant.
You can execute SQLite queries using the SQLiteDatabase
query()
methods, which accept various query parameters, such as the table to query, the projection, selection, columns, grouping, and others. For complex queries, such as those that require column aliases, you should use SQLiteQueryBuilder
, which provides several convienent methods for building queries.
Every SQLite query will return a Cursor
that points to all the rows found by the query. The Cursor
is always the mechanism with which you can navigate results from a database query and read rows and columns.
For sample apps that demonstrate how to use SQLite databases in Android, see the Note Pad and Searchable Dictionary applications.
","BaseColumns","android.provider.BaseColumns","class",1 ,"Storage Options","Using Databases","Android provides full support for SQLite databases. Any databases you create will be accessible by name to any class in the application, but not outside the application.
The recommended method to create a new SQLite database is to create a subclass of SQLiteOpenHelper
and override the onCreate()
method, in which you can execute a SQLite command to create tables in the database. For example:
public class DictionaryOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 2; private static final String DICTIONARY_TABLE_NAME = "dictionary"; private static final String DICTIONARY_TABLE_CREATE = "CREATE TABLE " + DICTIONARY_TABLE_NAME + " (" + KEY_WORD + " TEXT, " + KEY_DEFINITION + " TEXT);"; DictionaryOpenHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(DICTIONARY_TABLE_CREATE); } }
You can then get an instance of your SQLiteOpenHelper
implementation using the constructor you've defined. To write to and read from the database, call getWritableDatabase()
and getReadableDatabase()
, respectively. These both return a SQLiteDatabase
object that represents the database and provides methods for SQLite operations.
Android does not impose any limitations beyond the standard SQLite concepts. We do recommend including an autoincrement value key field that can be used as a unique ID to quickly find a record. This is not required for private data, but if you implement a content provider, you must include a unique ID using the BaseColumns._ID
constant.
You can execute SQLite queries using the SQLiteDatabase
query()
methods, which accept various query parameters, such as the table to query, the projection, selection, columns, grouping, and others. For complex queries, such as those that require column aliases, you should use SQLiteQueryBuilder
, which provides several convienent methods for building queries.
Every SQLite query will return a Cursor
that points to all the rows found by the query. The Cursor
is always the mechanism with which you can navigate results from a database query and read rows and columns.
For sample apps that demonstrate how to use SQLite databases in Android, see the Note Pad and Searchable Dictionary applications.
","Cursor","android.database.Cursor","class",0 ,"Storage Options","Using a Network Connection","You can use the network (when it's available) to store and retrieve data on your own web-based services. To do network operations, use classes in the following packages:
You can use the network (when it's available) to store and retrieve data on your own web-based services. To do network operations, use classes in the following packages:
When the user enables USB mass storage to share files with their computer (or otherwise unmounts or removes the external storage), any application installed on the external storage and currently running is killed. The system effectively becomes unaware of the application until mass storage is disabled and the external storage is remounted on the device. Besides killing the application and making it unavailable to the user, this can break some types of applications in a more serious way. In order for your application to consistently behave as expected, you should not allow your application to be installed on the external storage if it uses any of the following features, due to the cited consequences when the external storage is unmounted:
Service
will be killed and will not be restarted when external storage is remounted. You can, however, register for the
ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
AlarmManager
will be cancelled. You must manually re-register any alarms when external storage is remounted.
AccountManager
will disappear until external storage is remounted.
AbstractThreadedSyncAdapter
and all its sync functionality will not work until external storage is remounted.
DeviceAdminReceiver
and all its admin capabilities will be disabled, which can have unforeseeable consequences for the device functionality, which may persist after external storage is remounted.
ACTION_BOOT_COMPLETED
broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
If your application uses any of the features listed above, you should not allow your application to install on external storage. By default, the system will not allow your application to install on the external storage, so you don't need to worry about your existing applications. However, if you're certain that your application should never be installed on the external storage, then you should make this clear by declaring android:installLocation
with a value of "internalOnly
". Though this does not change the default behavior, it explicitly states that your application should only be installed on the internal storage and serves as a reminder to you and other developers that this decision has been made.
When the user enables USB mass storage to share files with their computer (or otherwise unmounts or removes the external storage), any application installed on the external storage and currently running is killed. The system effectively becomes unaware of the application until mass storage is disabled and the external storage is remounted on the device. Besides killing the application and making it unavailable to the user, this can break some types of applications in a more serious way. In order for your application to consistently behave as expected, you should not allow your application to be installed on the external storage if it uses any of the following features, due to the cited consequences when the external storage is unmounted:
Service
will be killed and will not be restarted when external storage is remounted. You can, however, register for the
ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
AlarmManager
will be cancelled. You must manually re-register any alarms when external storage is remounted.
AccountManager
will disappear until external storage is remounted.
AbstractThreadedSyncAdapter
and all its sync functionality will not work until external storage is remounted.
DeviceAdminReceiver
and all its admin capabilities will be disabled, which can have unforeseeable consequences for the device functionality, which may persist after external storage is remounted.
ACTION_BOOT_COMPLETED
broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
If your application uses any of the features listed above, you should not allow your application to install on external storage. By default, the system will not allow your application to install on the external storage, so you don't need to worry about your existing applications. However, if you're certain that your application should never be installed on the external storage, then you should make this clear by declaring android:installLocation
with a value of "internalOnly
". Though this does not change the default behavior, it explicitly states that your application should only be installed on the internal storage and serves as a reminder to you and other developers that this decision has been made.
When the user enables USB mass storage to share files with their computer (or otherwise unmounts or removes the external storage), any application installed on the external storage and currently running is killed. The system effectively becomes unaware of the application until mass storage is disabled and the external storage is remounted on the device. Besides killing the application and making it unavailable to the user, this can break some types of applications in a more serious way. In order for your application to consistently behave as expected, you should not allow your application to be installed on the external storage if it uses any of the following features, due to the cited consequences when the external storage is unmounted:
Service
will be killed and will not be restarted when external storage is remounted. You can, however, register for the
ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
AlarmManager
will be cancelled. You must manually re-register any alarms when external storage is remounted.
AccountManager
will disappear until external storage is remounted.
AbstractThreadedSyncAdapter
and all its sync functionality will not work until external storage is remounted.
DeviceAdminReceiver
and all its admin capabilities will be disabled, which can have unforeseeable consequences for the device functionality, which may persist after external storage is remounted.
ACTION_BOOT_COMPLETED
broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
If your application uses any of the features listed above, you should not allow your application to install on external storage. By default, the system will not allow your application to install on the external storage, so you don't need to worry about your existing applications. However, if you're certain that your application should never be installed on the external storage, then you should make this clear by declaring android:installLocation
with a value of "internalOnly
". Though this does not change the default behavior, it explicitly states that your application should only be installed on the internal storage and serves as a reminder to you and other developers that this decision has been made.
When the user enables USB mass storage to share files with their computer (or otherwise unmounts or removes the external storage), any application installed on the external storage and currently running is killed. The system effectively becomes unaware of the application until mass storage is disabled and the external storage is remounted on the device. Besides killing the application and making it unavailable to the user, this can break some types of applications in a more serious way. In order for your application to consistently behave as expected, you should not allow your application to be installed on the external storage if it uses any of the following features, due to the cited consequences when the external storage is unmounted:
Service
will be killed and will not be restarted when external storage is remounted. You can, however, register for the
ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
AlarmManager
will be cancelled. You must manually re-register any alarms when external storage is remounted.
AccountManager
will disappear until external storage is remounted.
AbstractThreadedSyncAdapter
and all its sync functionality will not work until external storage is remounted.
DeviceAdminReceiver
and all its admin capabilities will be disabled, which can have unforeseeable consequences for the device functionality, which may persist after external storage is remounted.
ACTION_BOOT_COMPLETED
broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
If your application uses any of the features listed above, you should not allow your application to install on external storage. By default, the system will not allow your application to install on the external storage, so you don't need to worry about your existing applications. However, if you're certain that your application should never be installed on the external storage, then you should make this clear by declaring android:installLocation
with a value of "internalOnly
". Though this does not change the default behavior, it explicitly states that your application should only be installed on the internal storage and serves as a reminder to you and other developers that this decision has been made.
When the user enables USB mass storage to share files with their computer (or otherwise unmounts or removes the external storage), any application installed on the external storage and currently running is killed. The system effectively becomes unaware of the application until mass storage is disabled and the external storage is remounted on the device. Besides killing the application and making it unavailable to the user, this can break some types of applications in a more serious way. In order for your application to consistently behave as expected, you should not allow your application to be installed on the external storage if it uses any of the following features, due to the cited consequences when the external storage is unmounted:
Service
will be killed and will not be restarted when external storage is remounted. You can, however, register for the
ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
AlarmManager
will be cancelled. You must manually re-register any alarms when external storage is remounted.
AccountManager
will disappear until external storage is remounted.
AbstractThreadedSyncAdapter
and all its sync functionality will not work until external storage is remounted.
DeviceAdminReceiver
and all its admin capabilities will be disabled, which can have unforeseeable consequences for the device functionality, which may persist after external storage is remounted.
ACTION_BOOT_COMPLETED
broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
If your application uses any of the features listed above, you should not allow your application to install on external storage. By default, the system will not allow your application to install on the external storage, so you don't need to worry about your existing applications. However, if you're certain that your application should never be installed on the external storage, then you should make this clear by declaring android:installLocation
with a value of "internalOnly
". Though this does not change the default behavior, it explicitly states that your application should only be installed on the internal storage and serves as a reminder to you and other developers that this decision has been made.
When the user enables USB mass storage to share files with their computer (or otherwise unmounts or removes the external storage), any application installed on the external storage and currently running is killed. The system effectively becomes unaware of the application until mass storage is disabled and the external storage is remounted on the device. Besides killing the application and making it unavailable to the user, this can break some types of applications in a more serious way. In order for your application to consistently behave as expected, you should not allow your application to be installed on the external storage if it uses any of the following features, due to the cited consequences when the external storage is unmounted:
Service
will be killed and will not be restarted when external storage is remounted. You can, however, register for the
ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
broadcast Intent, which will notify your application when applications installed on external storage have become available to the system again. At which time, you can restart your Service.
AlarmManager
will be cancelled. You must manually re-register any alarms when external storage is remounted.
AccountManager
will disappear until external storage is remounted.
AbstractThreadedSyncAdapter
and all its sync functionality will not work until external storage is remounted.
DeviceAdminReceiver
and all its admin capabilities will be disabled, which can have unforeseeable consequences for the device functionality, which may persist after external storage is remounted.
ACTION_BOOT_COMPLETED
broadcast before the external storage is mounted to the device. If your application is installed on the external storage, it can never receive this broadcast.
If your application uses any of the features listed above, you should not allow your application to install on external storage. By default, the system will not allow your application to install on the external storage, so you don't need to worry about your existing applications. However, if you're certain that your application should never be installed on the external storage, then you should make this clear by declaring android:installLocation
with a value of "internalOnly
". Though this does not change the default behavior, it explicitly states that your application should only be installed on the internal storage and serves as a reminder to you and other developers that this decision has been made.