diff options
Diffstat (limited to 'docs/html/training/basics')
| -rw-r--r-- | docs/html/training/basics/data-storage/databases.jd | 322 | ||||
| -rw-r--r-- | docs/html/training/basics/data-storage/files.jd | 382 | ||||
| -rw-r--r-- | docs/html/training/basics/data-storage/index.jd | 55 | ||||
| -rw-r--r-- | docs/html/training/basics/data-storage/shared-preferences.jd | 121 |
4 files changed, 880 insertions, 0 deletions
diff --git a/docs/html/training/basics/data-storage/databases.jd b/docs/html/training/basics/data-storage/databases.jd new file mode 100644 index 0000000..3a717dd --- /dev/null +++ b/docs/html/training/basics/data-storage/databases.jd @@ -0,0 +1,322 @@ +page.title=Saving Data in SQL Databases +parent.title=Data Storage +parent.link=index.html + +trainingnavtop=true +previous.title=Saving Data in Files +previous.link=files.html + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#DefineContract">Define a Schema and Contract</a></li> + <li><a href="#DbHelper">Create a Database Using a SQL Helper</a></li> + <li><a href="#WriteDbRow">Put Information into a Database</a></li> + <li><a href="#ReadDbRow">Read Information from a Database</a></li> + <li><a href="#DeleteDbRow">Delete Information from a Database</a></li> + <li><a href="#UpdateDbRow">Update a Database</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#db">Using Databases</a></li> +</ul> + +<!-- +<h2>Try it out</h2> + +<div class="download-box"> + <a href="{@docRoot}shareables/training/Sample.zip" class="button">Download the sample</a> + <p class="filename">Sample.zip</p> +</div> +--> + +</div> +</div> + + +<p>Saving data to a database is ideal for repeating or structured data, +such as contact information. This class assumes that you are +familiar with SQL databases in general and helps you get started with +SQLite databases on Android. The APIs you'll need to use a database +on Android are available in the {@link android.database.sqlite} package.</p> + + +<h2 id="DefineContract">Define a Schema and Contract</h2> + +<p>One of the main principles of SQL databases is the schema: a formal +declaration of how the database is organized. The schema is reflected in the SQL +statements that you use to create your database. You may find it helpful to +create a companion class, known as a <em>contract</em> class, which explicitly specifies +the layout of your schema in a systematic and self-documenting way.</p> + +<p>A contract class is a container for constants that define names for URIs, +tables, and columns. The contract class allows you to use the same constants +across all the other classes in the same package. This lets you change a column +name in one place and have it propagate throughout your code.</p> + +<p>A good way to organize a contract class is to put definitions that are +global to your whole database in the root level of the class. Then create an inner +class for each table that enumerates its columns.</p> + +<p class="note"><strong>Note:</strong> By implementing the {@link +android.provider.BaseColumns} interface, your inner class can inherit a primary +key field called {@code _ID} that some Android classes such as cursor adaptors +will expect it to have. It's not required, but this can help your database +work harmoniously with the Android framework.</p> + +<p>For example, this snippet defines the table name and column names for a +single table:</p> + + +<pre> +public static abstract class FeedEntry implements BaseColumns { + public static final String TABLE_NAME = "entry"; + public static final String COLUMN_NAME_ENTRY_ID = "entryid"; + public static final String COLUMN_NAME_TITLE = "title"; + public static final String COLUMN_NAME_SUBTITLE = "subtitle"; + ... +} +</pre> + + +<p>To prevent someone from accidentally instantiating the contract class, give +it an empty constructor. </p> + +<pre> +// Prevents the FeedReaderContract class from being instantiated. +private FeedReaderContract() {} +</pre> + + + +<h2 id="DbHelper">Create a Database Using a SQL Helper</h2> + +<p>Once you have defined how your database looks, you should implement methods +that create and maintain the database and tables. Here are some typical +statements that create and delete a table:</P> + +<pre> +private static final String TEXT_TYPE = " TEXT"; +private static final String COMMA_SEP = ","; +private static final String SQL_CREATE_ENTRIES = + "CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" + + FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," + + FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + + FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + + ... // Any other options for the CREATE command + " )"; + +private static final String SQL_DELETE_ENTRIES = + "DROP TABLE IF EXISTS " + TABLE_NAME_ENTRIES; +</pre> + +<p>Just like files that you save on the device's <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal +storage</a>, Android stores your database in private disk space that's associated +application. Your data is secure, because by default this area is not +accessible to other applications.</p> + +<p>A useful set of APIs is available in the {@link +android.database.sqlite.SQLiteOpenHelper} class. +When you use this class to obtain references to your database, the system +performs the potentially +long-running operations of creating and updating the database only when +needed and <em>not during app startup</em>. All you need to do is call +{@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} or +{@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}.</p> + +<p class="note"><strong>Note:</strong> Because they can be long-running, +be sure that you call {@link +android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} or {@link +android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} in a background thread, +such as with {@link android.os.AsyncTask} or {@link android.app.IntentService}.</p> + +<p>To use {@link android.database.sqlite.SQLiteOpenHelper}, create a subclass that +overrides the {@link +android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link +android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} and {@link +android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()} callback methods. You may also +want to implement {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}, +but it's not required.</p> + +<p>For example, here's an implementation of {@link +android.database.sqlite.SQLiteOpenHelper} that uses some of the commands shown above:</p> + +<pre> +public class FeedReaderDbHelper extends SQLiteOpenHelper { + // If you change the database schema, you must increment the database version. + public static final int DATABASE_VERSION = 1; + public static final String DATABASE_NAME = "FeedReader.db"; + + public FeedReaderDbHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + public void onCreate(SQLiteDatabase db) { + db.execSQL(SQL_CREATE_ENTRIES); + } + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // This database is only a cache for online data, so its upgrade policy is + // to simply to discard the data and start over + db.execSQL(SQL_DELETE_ENTRIES); + onCreate(db); + } + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + onUpgrade(db, oldVersion, newVersion); + } +} +</pre> + +<p>To access your database, instantiate your subclass of {@link +android.database.sqlite.SQLiteOpenHelper}:</p> + +<pre> +FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); +</pre> + + + + +<h2 id="WriteDbRow">Put Information into a Database</h2> + +<p>Insert data into the database by passing a {@link android.content.ContentValues} +object to the {@link android.database.sqlite.SQLiteDatabase#insert insert()} method:</p> + +<pre> +// Gets the data repository in write mode +SQLiteDatabase db = mDbHelper.getWritableDatabase(); + +// Create a new map of values, where column names are the keys +ContentValues values = new ContentValues(); +values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id); +values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title); +values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content); + +// Insert the new row, returning the primary key value of the new row +long newRowId; +newRowId = db.insert( + FeedReaderContract.FeedEntry.TABLE_NAME, + FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE, + values); +</pre> + +<p>The first argument for {@link android.database.sqlite.SQLiteDatabase#insert insert()} +is simply the table name. The second argument provides +the name of a column in which the framework can insert NULL in the event that the +{@link android.content.ContentValues} is empty (if you instead set this to {@code "null"}, +then the framework will not insert a row when there are no values).</p> + + + + +<h2 id="ReadDbRow">Read Information from a Database</h2> + +<p>To read from a database, use the {@link android.database.sqlite.SQLiteDatabase#query query()} +method, passing it your selection criteria and desired columns. +The method combines elements of {@link android.database.sqlite.SQLiteDatabase#insert insert()} +and {@link android.database.sqlite.SQLiteDatabase#update update()}, except the column list +defines the data you want to fetch, rather than the data to insert. The results of the query +are returned to you in a {@link android.database.Cursor} object.</p> + +<pre> +SQLiteDatabase db = mDbHelper.getReadableDatabase(); + +// Define a <em>projection</em> that specifies which columns from the database +// you will actually use after this query. +String[] projection = { + FeedReaderContract.FeedEntry._ID, + FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, + FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED, + ... + }; + +// How you want the results sorted in the resulting Cursor +String sortOrder = + FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + " DESC"; + +Cursor c = db.query( + FeedReaderContract.FeedEntry.TABLE_NAME, // The table to query + projection, // The columns to return + selection, // The columns for the WHERE clause + selectionArgs, // The values for the WHERE clause + null, // don't group the rows + null, // don't filter by row groups + sortOrder // The sort order + ); +</pre> + +<p>To look at a row in the cursor, use one of the {@link android.database.Cursor} move +methods, which you must always call before you begin reading values. Generally, you should start +by calling {@link android.database.Cursor#moveToFirst}, which places the "read position" on the +first entry in the results. For each row, you can read a column's value by calling one of the +{@link android.database.Cursor} get methods, such as {@link android.database.Cursor#getString +getString()} or {@link android.database.Cursor#getLong getLong()}. For each of the get methods, +you must pass the index position of the column you desire, which you can get by calling +{@link android.database.Cursor#getColumnIndex getColumnIndex()} or +{@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}. +For example:</p> + +<pre> +cursor.moveToFirst(); +long itemId = cursor.getLong( + cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID) +); +</pre> + + + + +<h2 id="DeleteDbRow">Delete Information from a Database</h2> + +<p>To delete rows from a table, you need to provide selection criteria that +identify the rows. The database API provides a mechanism for creating selection +criteria that protects against SQL injection. The mechanism divides the +selection specification into a selection clause and selection arguments. The +clause defines the columns to look at, and also allows you to combine column +tests. The arguments are values to test against that are bound into the clause. +Because the result isn't handled the same as a regular SQL statement, it is +immune to SQL injection.</p> + +<pre> +// Define 'where' part of query. +String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; +// Specify arguments in placeholder order. +String[] selelectionArgs = { String.valueOf(rowId) }; +// Issue SQL statement. +db.delete(table_name, mySelection, selectionArgs); +</pre> + + + +<h2 id="UpdateDbRow">Update a Database</h2> + +<p>When you need to modify a subset of your database values, use the {@link +android.database.sqlite.SQLiteDatabase#update update()} method.</p> + +<p>Updating the table combines the content values syntax of {@link +android.database.sqlite.SQLiteDatabase#insert insert()} with the {@code where} syntax +of {@link android.database.sqlite.SQLiteDatabase#delete delete()}.</p> + +<pre> +SQLiteDatabase db = mDbHelper.getReadableDatabase(); + +// New value for one column +ContentValues values = new ContentValues(); +values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title); + +// Which row to update, based on the ID +String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; +String[] selelectionArgs = { String.valueOf(rowId) }; + +int count = db.update( + FeedReaderDbHelper.FeedEntry.TABLE_NAME, + values, + selection, + selectionArgs); +</pre> + diff --git a/docs/html/training/basics/data-storage/files.jd b/docs/html/training/basics/data-storage/files.jd new file mode 100644 index 0000000..dd081a6 --- /dev/null +++ b/docs/html/training/basics/data-storage/files.jd @@ -0,0 +1,382 @@ +page.title=Saving Files +parent.title=Data Storage +parent.link=index.html + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#InternalVsExternalStorage">Choose Internal or External Storage</a></li> + <li><a href="#GetWritePermission">Obtain Permissions for External Storage</a></li> + <li><a href="#WriteInternalStorage">Save a File on Internal Storage</a></li> + <li><a href="#WriteExternalStorage">Save a File on External Storage</a></li> + <li><a href="#GetFreeSpace">Query Free Space</a></li> + <li><a href="#DeleteFile">Delete a File</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">Using the Internal +Storage</a></li> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">Using the External +Storage</a></li> +</ul> + +</div> +</div> + +<p>Android uses a file system that's +similar to disk-based file systems on other platforms. This lesson describes +how to work with the Android file system to read and write files with the {@link java.io.File} +APIs.</p> + +<p>A {@link java.io.File} object is suited to reading or writing large amounts of data in +start-to-finish order without skipping around. For example, it's good for image files or +anything exchanged over a network.</p> + +<p>This lesson shows how to perform basic file-related tasks in your app. +The lesson assumes that you are familiar with the basics of the Linux file system and the +standard file input/output APIs in {@link java.io}.</p> + + +<h2 id="InternalVsExternalStorage">Choose Internal or External Storage</h2> + +<p>All Android devices have two file storage areas: "internal" and "external" storage. These names +come from the early days of Android, when most devices offered built-in non-volatile memory +(internal storage), plus a removable storage medium such as a micro SD card (external storage). +Some devices divide the permanent storage space into "internal" and "external" partitions, so even +without a removable storage medium, there are always two storage spaces and +the API behavior is the same whether the external storage is removable or not. +The following lists summarize the facts about each storage space.</p> + +<div class="col-5" style="margin-left:0"> +<p><b>Internal storage:</b></p> +<ul> +<li>It's always available.</li> +<li>Files saved here are accessible by only your app by default.</li> +<li>When the user uninstalls your app, the system removes all your app's files from +internal storage.</li> +</ul> +<p>Internal storage is best when you want to be sure that neither the user nor other apps can +access your files.</p> +</div> + +<div class="col-7" style="margin-right:0"> +<p><b>External storage:</b></p> +<ul> +<li>It's not always available, because the user can mount the external storage as USB storage +and in some cases remove it from the device.</li> +<li>It's world-readable, so +files saved here may be read outside of your control.</li> +<li>When the user uninstalls your app, the system removes your app's files from here +only if you save them in the directory from {@link android.content.Context#getExternalFilesDir +getExternalFilesDir()}.</li> +</ul> +<p>External storage is the best +place for files that don't require access restrictions and for files that you want to share +with other apps or allow the user to access with a computer.</p> +</div> + + +<p class="note" style="clear:both"> +<strong>Tip:</strong> Although apps are installed onto the internal storage by +default, you can specify the <a +href="{@docRoot}guide/topics/manifest/manifest-element.html#install">{@code +android:installLocation}</a> attribute in your manifest so your app may +be installed on external storage. Users appreciate this option when the APK size is very large and +they have an external storage space that's larger than the internal storage. For more +information, see <a +href="{@docRoot}guide/topics/data/install-location.html">App Install Location</a>.</p> + + +<h2 id="GetWritePermission">Obtain Permissions for External Storage</h2> + +<p>To write to the external storage, you must request the + {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission in your <a +href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest file</a>:</p> + +<pre> +<manifest ...> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> + +<div class="caution"><p><strong>Caution:</strong> +Currently, all apps have the ability to read the external storage +without a special permission. However, this will change in a future release. If your app needs +to read the external storage (but not write to it), then you will need to declare the {@link +android.Manifest.permission#READ_EXTERNAL_STORAGE} permission. To ensure that your app continues +to work as expected, you should declare this permission now, before the change takes effect.</p> +<pre> +<manifest ...> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + ... +</manifest> +</pre> +<p>However, if your app uses the {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} +permission, then it implicitly has permission to read the external storage as well.</p> +</div> + +<p>You don’t need any permissions to save files on the internal +storage. Your application always has permission to read and +write files in its internal storage directory.</p> + + + + + +<h2 id="WriteInternalStorage">Save a File on Internal Storage</h2> + +<p>When saving a file to internal storage, you can acquire the appropriate directory as a +{@link java.io.File} by calling one of two methods:</p> + +<dl> + <dt>{@link android.content.Context#getFilesDir}</dt> + <dd>Returns a {@link java.io.File} representing an internal directory for your app.</dd> + <dt>{@link android.content.Context#getCacheDir}</dt> + <dd>Returns a {@link java.io.File} representing an internal directory for your app's temporary +cache files. Be sure to delete each file once it is no +longer needed and implement a reasonable size limit for the amount of memory you use at any given +time, such as 1MB. If the system begins running low on storage, it may delete your cache files +without warning.</dd> +</dl> + +<p>To create a new file in one of these directories, you can use the {@link +java.io.File#File(File,String) File()} constructor, passing the {@link java.io.File} provided by one +of the above methods that specifies your internal storage directory. For example:</p> + +<pre> +File file = new File(context.getFilesDir(), filename); +</pre> + +<p>Alternatively, you can call {@link +android.content.Context#openFileOutput openFileOutput()} to get a {@link java.io.FileOutputStream} +that writes to a file in your internal directory. For example, here's +how to write some text to a file:</p> + +<pre> +String filename = "myfile"; +String string = "Hello world!"; +FileOutputStream outputStream; + +try { + outputStream = openFileOutput(filename, Context.MODE_PRIVATE); + outputStream.write(string.getBytes()); + outputStream.close(); +} catch (Exception e) { + e.printStackTrace(); +} +</pre> + +<p>Or, if you need to cache some files, you should instead use {@link +java.io.File#createTempFile createTempFile()}. For example, the following method extracts the +file name from a {@link java.net.URL} and creates a file with that name +in your app's internal cache directory:</p> + +<pre> +public File getTempFile(Context context, String url) { + File file; + try { + String fileName = Uri.parse(url).getLastPathSegment(); + file = File.createTempFile(fileName, null, context.getCacheDir()); + catch (IOException e) { + // Error while creating file + } + return file; +} +</pre> + +<p class="note"><strong>Note:</strong> +Your app's internal storage directory is specified +by your app's package name in a special location of the Android file system. +Technically, another app can read your internal files if you set +the file mode to be readable. However, the other app would also need to know your app package +name and file names. Other apps cannot browse your internal directories and do not have +read or write access unless you explicitly set the files to be readable or writable. So as long +as you use {@link android.content.Context#MODE_PRIVATE} for your files on the internal storage, +they are never accessible to other apps.</p> + + + + + +<h2 id="WriteExternalStorage">Save a File on External Storage</h2> + +<p>Because the external storage may be unavailable—such as when the user has mounted the +storage to a PC or has removed the SD card that provides the external storage—you +should always verify that the volume is available before accessing it. You can query the external +storage state by calling {@link android.os.Environment#getExternalStorageState}. If the returned +state is equal to {@link android.os.Environment#MEDIA_MOUNTED}, then you can read and +write your files. For example, the following methods are useful to determine the storage +availability:</p> + +<pre> +/* Checks if external storage is available for read and write */ +public boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state)) { + return true; + } + return false; +} + +/* Checks if external storage is available to at least read */ +public boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state) || + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + return true; + } + return false; +} +</pre> + +<p>Although the external storage is modifiable by the user and other apps, there are two +categories of files you might save here:</p> + +<dl> + <dt>Public files</dt> + <dd>Files that +should be freely available to other apps and to the user. When the user uninstalls your app, +these files should remain available to the user. + <p>For example, photos captured by your app or other downloaded files.</p> + </dd> + <dt>Private files</dt> + <dd>Files that rightfully belong to your app and should be deleted when the user uninstalls + your app. Although these files are technically accessible by the user and other apps because they + are on the external storage, they are files that realistically don't provide value to the user + outside your app. When the user uninstalls your app, the system deletes + all files in your app's external private directory. + <p>For example, additional resources downloaded by your app or temporary media files.</p> + </dd> +</dl> + +<p>If you want to save public files on the external storage, use the +{@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()} method to get a {@link java.io.File} representing +the appropriate directory on the external storage. The method takes an argument specifying +the type of file you want to save so that they can be logically organized with other public +files, such as {@link android.os.Environment#DIRECTORY_MUSIC} or {@link +android.os.Environment#DIRECTORY_PICTURES}. For example:</p> + +<pre> +public File getAlbumStorageDir(String albumName) { + // Get the directory for the user's public pictures directory. + File file = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES), albumName); + if (!file.mkdirs()) { + Log.e(LOG_TAG, "Directory not created"); + } + return file; +} +</pre> + + +<p>If you want to save files that are private to your app, you can acquire the +appropriate directory by calling {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()} and passing it a name indicating +the type of directory you'd like. Each directory created this way is added to a parent +directory that encapsulates all your app's external storage files, which the system deletes when the +user uninstalls your app.</p> + +<p>For example, here's a method you can use to create a directory for an individual photo album:</p> + +<pre> +public File getAlbumStorageDir(Context context, String albumName) { + // Get the directory for the app's private pictures directory. + File file = new File(context.getExternalFilesDir( + Environment.DIRECTORY_PICTURES), albumName); + if (!file.mkdirs()) { + Log.e(LOG_TAG, "Directory not created"); + } + return file; +} +</pre> + +<p>If none of the pre-defined sub-directory names suit your files, you can instead call {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()} and pass {@code null}. This +returns the root directory for your app's private directory on the external storage.</p> + +<p>Remember that {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} +creates a directory inside a directory that is deleted when the user uninstalls your app. +If the files you're saving should remain available after the user uninstalls your +app—such as when your app is a camera and the user will want to keep the photos—you +should instead use {@link android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()}.</p> + + +<p>Regardless of whether you use {@link +android.os.Environment#getExternalStoragePublicDirectory +getExternalStoragePublicDirectory()} for files that are shared or +{@link android.content.Context#getExternalFilesDir +getExternalFilesDir()} for files that are private to your app, it's important that you use +directory names provided by API constants like +{@link android.os.Environment#DIRECTORY_PICTURES}. These directory names ensure +that the files are treated properly by the system. For instance, files saved in {@link +android.os.Environment#DIRECTORY_RINGTONES} are categorized by the system media scanner as ringtones +instead of music.</p> + + + + +<h2 id="GetFreeSpace">Query Free Space</h2> + +<p>If you know ahead of time how much data you're saving, you can find out +whether sufficient space is available without causing an {@link +java.io.IOException} by calling {@link java.io.File#getFreeSpace} or {@link +java.io.File#getTotalSpace}. These methods provide the current available space and the +total space in the storage volume, respectively. This information is also useful to avoid filling +the storage volume above a certain threshold.</p> + +<p>However, the system does not guarantee that you can write as many bytes as are +indicated by {@link java.io.File#getFreeSpace}. If the number returned is a +few MB more than the size of the data you want to save, or if the file system +is less than 90% full, then it's probably safe to proceed. +Otherwise, you probably shouldn't write to storage.</p> + +<p class="note"><strong>Note:</strong> You aren't required to check the amount of available space +before you save your file. You can instead try writing the file right away, then +catch an {@link java.io.IOException} if one occurs. You may need to do +this if you don't know exactly how much space you need. For example, if you +change the file's encoding before you save it by converting a PNG image to +JPEG, you won't know the file's size beforehand.</p> + + + + +<h2 id="DeleteFile">Delete a File</h2> + +<p>You should always delete files that you no longer need. The most straightforward way to delete a +file is to have the opened file reference call {@link java.io.File#delete} on itself.</p> + +<pre> +myFile.delete(); +</pre> + +<p>If the file is saved on internal storage, you can also ask the {@link android.content.Context} to locate and +delete a file by calling {@link android.content.Context#deleteFile deleteFile()}:</p> + +<pre> +myContext.deleteFile(fileName); +</pre> + +<div class="note"> +<p><strong>Note:</strong> When the user uninstalls your app, the Android system deletes +the following:</p> +<ul> +<li>All files you saved on internal storage</li> +<li>All files you saved on external storage using {@link +android.content.Context#getExternalFilesDir getExternalFilesDir()}.</li> +</ul> +<p>However, you should manually delete all cached files created with +{@link android.content.Context#getCacheDir()} on a regular basis and also regularly delete +other files you no longer need.</p> +</div> + diff --git a/docs/html/training/basics/data-storage/index.jd b/docs/html/training/basics/data-storage/index.jd new file mode 100644 index 0000000..99bb261 --- /dev/null +++ b/docs/html/training/basics/data-storage/index.jd @@ -0,0 +1,55 @@ +page.title=Saving Data + +trainingnavtop=true +startpage=true + +@jd:body + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Dependencies and prerequisites</h2> +<ul> + <li>Android 1.6 (API Level 4) or higher</li> + <li>Familiarity with Map key-value collections</li> + <li>Familiarity with the Java file I/O API</li> + <li>Familiarity with SQL databases</li> +</ul> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html">Storage Options</a></li> +</ul> + +</div> +</div> + +<p>Most Android apps need to save data, even if only to save information about the app state +during {@link android.app.Activity#onPause onPause()} so the user's progress is not lost. Most +non-trivial apps also need to save user settings, and some apps must manage large +amounts of information in files and databases. This class introduces you to the +principal data storage options in Android, including:</p> + +<ul> + <li>Saving key-value pairs of simple data types in a shared preferences +file</li> + <li>Saving arbitrary files in Android's file system</li> + <li>Using databases managed by SQLite</li> +</ul> + + +<h2>Lessons</h2> + +<dl> + <dt><b><a href="shared-preferences.html">Saving Data in Shared Preferences</a></b></dt> + <dd>Learn to use a shared preferences file for storing small amounts of information in +key-value pairs.</dd> + + <dt><b><a href="files.html">Saving Data in Files</a></b></dt> + <dd>Learn to save a basic file, such as to store long sequences of data that + are generally read in order.</dd> + + <dt><b><a href="databases.html">Saving Data in SQL Databases</a></b></dt> + <dd>Learn to use a SQLite database to read and write structured data.</dd> + +</dl> diff --git a/docs/html/training/basics/data-storage/shared-preferences.jd b/docs/html/training/basics/data-storage/shared-preferences.jd new file mode 100644 index 0000000..67f45cb --- /dev/null +++ b/docs/html/training/basics/data-storage/shared-preferences.jd @@ -0,0 +1,121 @@ +page.title=Saving Key-Value Sets +parent.title=Data Storage +parent.link=index.html + +trainingnavtop=true + +@jd:body + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#GetSharedPreferences">Get a Handle to a SharedPreferences</a></li> + <li><a href="#WriteSharedPreference">Write to Shared Preferences</a></li> + <li><a href="#ReadSharedPreference">Read from Shared Preferences</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/data/data-storage.html#pref">Using Shared Preferences</a></li> +</ul> + +</div> +</div> + + +<p>If you have a relatively small collection of key-values that you'd like to save, +you should use the {@link android.content.SharedPreferences} APIs. +A {@link android.content.SharedPreferences} object points to a file containing +key-value pairs and provides simple methods to read and write them. Each +{@link android.content.SharedPreferences} file is +managed by the framework and can be private or shared.</p> + +<p>This class shows you how to use the {@link android.content.SharedPreferences} APIs to store and +retrieve simple values.</p> + +<p class="note"><strong>Note:</strong> The {@link android.content.SharedPreferences} APIs are +only for reading and writing key-value pairs and you should not confuse them with the +{@link android.preference.Preference} APIs, which help you build a user interface +for your app settings (although they use {@link android.content.SharedPreferences} as their +implementation to save the app settings). For information about using the {@link +android.preference.Preference} APIs, see the <a href="{@docRoot}guide/topics/ui/settings.html" +>Settings</a> guide.</p> + +<h2 id="GetSharedPreferences">Get a Handle to a SharedPreferences</h2> + +<p>You can create a new shared preference file or access an existing +one by calling one of two methods:</p> +<ul> + <li>{@link android.content.Context#getSharedPreferences(String,int) +getSharedPreferences()} — Use this if you need multiple shared preference files identified +by name, which you specify with the first parameter. You can call this from any +{@link android.content.Context} in your app.</li> + <li>{@link android.app.Activity#getPreferences(int) getPreferences()} — Use this from an +{@link android.app.Activity} if you need +to use only one shared preference file for the activity. Because this retrieves a default shared +preference file that belongs to the activity, you don't need to supply a name.</li> +</ul> + +<p>For example, the following code is executed inside a {@link android.app.Fragment}. +It accesses the shared preferences file that's +identified by the resource string {@code R.string.preference_file_key} and opens it using +the private mode so the file is accessible by only your app.</p> + +<pre> +Context context = getActivity(); +SharedPreferences sharedPref = context.getSharedPreferences( + getString(R.string.preference_file_key), Context.MODE_PRIVATE); +</pre> + +<p>When naming your shared preference files, you should use a name that's uniquely identifiable +to your app, such as {@code "com.example.myapp.PREFERENCE_FILE_KEY"}</p> + +<p>Alternatively, if you need just one shared preference file for your activity, you can use the +{@link android.app.Activity#getPreferences(int) getPreferences()} method:</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +</pre> + +<p class="caution"><strong>Caution:</strong> If you create a shared preferences file +with {@link android.content.Context#MODE_WORLD_READABLE} or {@link +android.content.Context#MODE_WORLD_WRITEABLE}, then any other apps that know the file identifier +can access your data.</p> + + +<h2 id="WriteSharedPreference">Write to Shared Preferences</h2> + +<p>To write to a shared preferences file, create a {@link +android.content.SharedPreferences.Editor} by calling {@link +android.content.SharedPreferences#edit} on your {@link android.content.SharedPreferences}.</p> + +<p>Pass the keys and values you want to write with methods such as {@link +android.content.SharedPreferences.Editor#putInt putInt()} and {@link +android.content.SharedPreferences.Editor#putString putString()}. Then call {@link +android.content.SharedPreferences.Editor#commit} to save the changes. For example:</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +SharedPreferences.Editor editor = sharedPref.edit(); +editor.putInt(getString(R.string.saved_high_score), newHighScore); +editor.commit(); +</pre> + + +<h2 id="ReadSharedPreference">Read from Shared Preferences</h2> + +<p>To retrieve values from a shared preferences file, call methods such as {@link +android.content.SharedPreferences#getInt getInt()} and {@link +android.content.SharedPreferences#getString getString()}, providing the key for the value +you want, and optionally a default value to return if the key isn't +present. For example:</p> + +<pre> +SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); +long default = getResources().getInteger(R.string.saved_high_score_default)); +long highScore = sharedPref.getInt(getString(R.string.saved_high_score), default); +</pre> + |
