Вы находитесь на странице: 1из 10

Data Storage and Multithreading

Matthieu Weber (matthieu.weber@jyu.fi), 2011

Plan
Data Storage on Mobile Devices
Data Storage in Android
Content Providers
Processes and Multithreading

Data Storage on Mobile Devices


Low-Level Storage
Database:
collection of records (fixed or variable length)
records subdivided into fields
File:
stream of bytes
filesystem keeps track of file boundaries

Databases
Used in Palm OS, Java ME
Data hold in memory-backed RAM permanent data structure
Record structure defined by the application, fixed for a given database all records have the same structure
Other applications dont know how to read the database
No SQL in Palm OS/Java ME (database is rather like a single table)
Require little processing power to handle the database
Android uses SQLite, databases are not the lowest-level data storage

Files
Used in EPOC/Symbian OS, Maemo, iOS, Windows CE/Phone
Various filesystems (FAT, ext3, HFS+, NTFS)
Familiar API

High-level
SQL database using files as backend
Limited user-access to the file-system; concept of file absent from iOS UI

MW/2011/TIES425/Data Storage

Data Storage in Android


Permissions
Each application runs as a separate user
Private application data protected by Unix permissions from other applications
Sharing data between applications must be specified in AndroidManifest.xml

Storage Facilities

Shared preferences
Internal storage
External storage
SQLite database

Shared Preferences

Stores key/value pairs small amounts of data


For data shared between runs of the application
Context.getSharedPreferences(name, mode) retrieves preferences identified by name
Activity.getPreferences(mode) retrieves preferences with a default, activity-based name
mode specifies the preference files creation mode (Unix permissions)

Modifying Shared Preferences

SharedPreferences has only methods for reading values


Get a SharedPreferences.Editor object to add/modify a value by calling SharedPreferences.edit()
You must call SharedPreferences.Editor.commit() to save the changes
Android 2.2+ has a SharedPreferences.Editor.apply() that saves to the in-memory SharedPreference
object and starts an asynchronous commit

Shared Preferences Example


@Override
protected void onCreate ( Bundle state ){
super . onCreate ( state );
...
// Restore preferences
Shar edPreferences 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
Shar edPre ferences settings = getSharedPreferences ( PREFS_NAME , 0);
Shar edPre ferences . Editor editor = settings . edit ();
editor . putBoolean ( " silentMode " , mSilentMode );
// Commit the edits !
editor . commit ();
}

Shared Preference Modes


Context.MODE_PRIVATE : accessible only by the calling application (or applications sharing the same user ID)
Context.MODE_WORLD_READABLE/WRITABLE : accessible by any application for reading/writing
Context.MODE_MULTI_PROCESS : check the preference file for modifications when the application has multiple
Unix process writing to the same shared preferences (default in Android before 2.2, needs to be explicit for
newer versions)

MW/2011/TIES425/Data Storage

File Access
Two types of files:
Application-specific: meaningful only to the application (e.g., game scores, application preferences)
Shared: used by several different applications (e.g., pictures, sound clips, videos)
Application-specific data are hidden from other applications and from the user
Shared files must be accessible to all (also through USB mass-storage)

Internal Storage
Storage space by default private to the application
The user cannot access it
Cleared when the application is removed
Used for shared preferences
Default location, relative to the application no need to worry about absolute path or actual location on the
file system
Use this for large amounts of data

File Example
String FILENAME = " hello_file " ;
String string = " hello world ! " ;
FileOutputStream fos = openFileOutput ( FILENAME , Context . MODE_PRIVATE );
fos . write ( string . getBytes ());
fos . close ();
...
byte [] buffer = new byte [ BUFFER_SIZE ];
FileInputStream fis = openFineInput ( FILENAME );
if ( fis . read ( buffer ) > -1) {
...
}
fis . close ()

Files as Application Resources


Place the file in the res/raw/ directory of the applications source tree
Calling openRawResource(R.raw.filename ) on the Resources returned by Context.getResources()
returns an InputStream for the file named filename

Temporary Files
Context.getCacheDir() returns a File object representing the application-specific cache directory for
temporary files
Use File.createTempFile(...) to create a temporary file
Temporary files are removed automatically when storage space runs out, but do not rely on the system for
removing those, manage them yourself

Other Useful Methods


getFilesDir(): gets the absolute path of the directory internal files are saved
getDir(): creates/open a directory within the applications internal storage
deleteFile(): deletes a file from the applications internal storage
fileList(): lists (array of String) the files in the applications internal storage

MW/2011/TIES425/Data Storage

External Storage
Removable or non-removable flash memory, depending on the device
World-readable files (FAT has no permission)
May be temporarily inaccessible (mounted as USB mass-storage, or removed from the device) always check
for availability before accessing it

Mounting a Filesystem
Unix concept
Plug the content of a filesystem B into a directory of another filesystem A
For the user, files of B look like files of A

External Storage State


State as String from Environment.getExternalStorageState()
Compared against string constants in Environment class
States:
checking, mounted, mounted read-only, unmounted, removed
bad removal (removed while mounted), shared (mounted as USB mass-storage), no filesystem (not
formatted or unsupported filesystem type)
Environment.isExternalStorageRemovable() in Android 2.2+ indicates whether external storage is
removable or built-in

Availability of External Storage: Example


boolean e x t e r n a lS t o r ag e A v ai l a b le = false ;
boolean e x t e r n a lS t o r ag e W r it e a b le = false ;
String state = Environment . ge t Ex te r na l St o ra ge S ta t e ();
if ( Environment . MEDIA_MOUNTED . equals ( state )) {
// We can read and write the media
e x t e r n a l S t o r ag e A v ai l a b le = e x t er n a l St o r a ge W r i te a b l e = true ;
} else if ( Environment . M ED I A_ M OU NT E D_ R EA D _O NL Y . equals ( state )) {
// We can only read the media
e x t e r n a l S t o r ag e A v ai l a b le = true ;
e x t e r n a l S t o r ag e W r it e a b l e = 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
e x t e r n a l S t o r ag e A v ai l a b l e = e x t er n a l St o r a ge W r i te a b l e = false ;

Managing Files on External Storage


Environment.getExternalStorageDirectory() returns a File representing the root directory of the
external storage
Application files must be placed in Android/data/<package_name>/files/ relatively to that root
package_name is the applications Java package name
From Android 2.2, Context.getExternalFilesDir(type) returns the directory where the files must be
saved
type is e.g., Environment.DIRECTORY_MUSIC, Environment.DIRECTORY_DOWNLOADS or null

Shared Files
Files in Android/data/<package_name>/... are deleted when the application is removed
Files shared between applications should be put in one of the following directories: Music, Podcasts,
Ringtones, Alarms, Notifications, Pictures, Movies, Download
The path must be built by hand in Android < 2.2, but you can use
Environment.getExternalStoragePublicDirectory(type) in Android 2.2+

MW/2011/TIES425/Data Storage

Temporary Files on External Storage


Before Android 2.2: get the root directory with Environment.getExternalStorageDirectory() and write
the temporary file in Android/data/<package_name>/cache/
Android 2.2+: get the cache directory from Context.getExternalCacheDir() (those files are erased when
the application is removed)

Using Databases
Support for SQLite in Android
SQLite: SQL database, stored in a single file
File stored as a private file of the application

APIs
SQLiteDatabase class allows to run SQL queries on a database
Provides convenience methods for common operations (insert/update/delete a row, make a query)
Queries return a Cursor object
Cursors allow to travel a list of rows and get values from the columns in the row

SQLiteOpenHelper
A class that handles creating/opening/updating a database
getWritableDatabase()/getReadableDatabase() methods
Creates the database if it doesnt exist
Opens an existing database (possibly just created)
Updates the content of the database if a new version of the application uses a new schema

SQLiteOpenHelper Example
public class DictionaryOpenHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2;
private static final String DI CTIO NARY _TABL E_NA ME = " dictionary " ;
private static final String DI C TI ON A RY _ TA BL E _C R EA TE =
" CREATE TABLE " + DICT IONA RY_T ABLE_ NAME + " ( " +
KEY_WORD + " TEXT , " + KEY_DEFINITION + " TEXT ); " ;
D i c t i o n a r y OpenHelper ( Context context ) {
super ( context , DATABASE_NAME , null , DATABASE_VERSION );
}
@Override
public void onCreate ( SQLiteDatabase db ) {
db . execSQL ( DI C TI ON A RY _ TA BL E _C R EA T E );
}
@Override
public void onUpgrade ( SQLiteDatabase db , int oldVersion , int newVersion ) {
db . execSQL ( " DROP TABLE " + D ICTIO NARY _TAB LE_NA ME + " ; " );
onCreate ( db );
}
}

MW/2011/TIES425/Data Storage

Cursor, ListAdapter and ListView


A ListView can be filled with the data of a Cursor through a ListAdapter (e.g, a SimpleCursorAdapter)
Example
String columns [] = { " _id " , " NAME " , " SCORE " };
int listview_fields [] = {0 , R . id . high_score_player , R . id . high_score_value };
ListView list = ( ListView ) findViewById ( R . id . high_score_list );
Cursor cursor = db . query ( TABLE_NAME , columns , ...);
S im p l e Cu rsorAdapter adapter = new SimpleCursorAdapter ( context , listview_id ,
cursor , columns , listview_fields );
list . setAdapter ( adapter );

The Cursor must contain a numerical field named _id


_id can be created in SQLs CREATE TABLE as _id integer primary key autoincrement

Content Providers
Content Provider
Allows to share data between any application
Data server accessible using RPC
Application component, declared in AndroidManifest.xml
Examples: contacts, browser bookmarks/history, phone call log, media store, global user settings

Data Model
Content providers return data as tables
Handled through a Cursor
Every record in the table includes a numeric _ID field; can be used to match two rows in related tables

URIs
Data sets are identified with URIs starting with content://
System content provider classes contain constants holding the URI e.g.,
ContactsContract.Contacts.CONTENT_URI
Record ID can be appended to the URI to refer to that specific record:
Uri myPerson = ContentUris . withAppendedId ( People . CONTENT_URI , 23); // int
OR
Uri myPerson = Uri . withAppendedPath ( People . CONTENT_URI , " 23 " ); // String

A subtable name can appended after a record with ID, e.g., .../people/42/phones/3 is the 3rd record in
subtable phone of 42nd record in table people

Accessing a Content Provider


Needed information: providers URI, name and types of the data fields, possibly record ID
Get a ContentResolver by calling Context.getContentResolver()
Call ContentResolver.query() or Activity.managedQuery()
With managedQuery(), the cursors lifecycle is managed by the activity (unload on pause, refresh on restart)
Activity.startManagingCursor(cursor) starts managing cursor

Query Parameters
URI
Column names (array of String)
SQL WHERE clause (to filter the rows), or null
Argument for the WHERE clause, or null
SQL ORDER BY clause, or null

MW/2011/TIES425/Data Storage

Query Example
import android . provider . Contacts . People ;
import android . database . Cursor ;
// Form an array specifying which columns to return .
String [] projection = new String [] {
People . _ID ,
People . _COUNT , // fake column , generated
People . NAME ,
People . NUMBER
};
// Get the base URI for the People table in the Contacts content provider .
Uri contacts = People . CONTENT_URI ;
// Make the query .
Cursor managedCursor = managedQuery ( contacts ,
projection , // Which columns to return
null ,
// Which rows to return ( all rows )
null ,
// Selection arguments ( none )
// Put the results in ascending order by name
People . NAME + " ASC " );

Cursor Reading Example


void getColumnData ( Cursor cur ){
if ( cur . moveToFirst ()) {
String name ;
String phoneNumber ;
int nameColumn = cur . getColumnIndex ( People . NAME );
int phoneColumn = cur . getColumnIndex ( People . NUMBER );
do {
// Get the field values
name = cur . getString ( nameColumn );
phoneNumber = cur . getString ( phoneColumn );
// Do something with the values .
...
} while ( cur . moveToNext ());
}
}

Large Data
Reasonably small data (< 50 kB or so) can be stored as a blob in a database retrieve them with
Cursor.getBlob()
Large data are stored as files and the database contains a URI retrieve them with
ContentResolver.openInputStream(uri)

Adding Records
Find to which table the data must be added
Put key/value pairs in a ContentValues object
Call ContentResolver.insert(uri, content_values)
It returns a URI to the new record
Once a record is created in the main table, other records can be added to the subtables (append the subtables
name to the main records URI)

MW/2011/TIES425/Data Storage

Adding Large Data


As a blob: use ContentValues.put()
As an external file: add the metadata to the content provider
Use the returned URI as OutputStream outStream = getContentResolver().openOutputStream(uri);
Write the data to the OutputStream

Other Operations
Batch update with ContentResolver.update()
Delete records with ContentResolver.delete()
URI for a specific record delete this record
Base URI and SQL WHERE clause delete all matching records

Make Your Own Content Provider


Subclass ContentProvider
Implement query(), insert(), update(), delete(), getType() and onCreate()
Call getContext().getContentResolver().notifyChange() to notify listeners of changes in the data
Define a public static final Uri named CONTENT_URI
Define define public static String constants with the names of the columns (dont forget the _id
column and the _ID constant)

URI and MIME Types


Base URI is content://yourcompanyname
Authority is yourcompanyname
Path part of the URI can be made of whatever makes sense to the application
Use UriMatcher to scan an incoming URI and find what it refers to
MIME type for a single record: vnd.android.cursor.item/vnd.yourcompanyname.contenttype
MIME type for multiple records: vnd.android.cursor.dir/vnd.yourcompanyname.contenttype

Declaring the Content Provider


In the AndroidManifest.xml, add
< provider android:name = " com . example . autos . AutoInfoProvider "
android:authorities = " com . example . autos . autoinfoprovider "
. . . />
</ provider >

Processes and Multithreading


Multithreading and I/O
I/O can block freezes the UI
Run I/O in a separate thread (possibly a separate process)

Process lifecycle in Android


5 levels of importance for processes:
foreground
visible
service
background
empty
Processes killed by order of least importance when the system needs to free memory

MW/2011/TIES425/Data Storage

Foreground Process
Hosting a running activity
Hosting a service that is
bound to a running activity
running in the foreground
executing one of the lifecycle callbacks (onCreate(), onStart(), onDestroy())
Hosting a broadcast receiver executing onReceive()

Visible Process
No foreground component, but can still affect whats on screen
Hosting a paused activity
Hosting a service bound to a visible or foreground activity

Service Process
Doing work in the background (playing music, downloading a file)
Hosting a service started with startService()
Doesnt belong to one of the above categories

Backgound Process
Hosting a stopped activity
Process has no impact on user experience
Kept in a Last-Recently Used list, the most recently used process is the last killed
Killing it has no effect on user experience if it saves its state properly

Empty Process
Hosts no active application component
Kept for being reused for other components faster startup time

Threads
Android applications are event-driven, normally run in a single thread (called main or UI thread)
New components (e.g., services) are run in the same thread by default
Main thread blocked for more than 5 seconds application not responding dialog appears
Do not block the UI thread
Android UI toolkit is not thread-safe do not access UI components from another thread

Bad Worker Thread Example


public void onClick ( View v ) {
new Thread ( new Runnable () {
public void run () {
Bitmap b = loadImageFromNetwork ( " http :// example . com / image . png " );
mImageView . setImageBitmap ( b ); // BAD : ACCESS THE UI FROM A WORKER THREAD
}
}). start ();
}

MW/2011/TIES425/Data Storage

Good Worker Thread Example


public void onClick ( View v ) {
new Thread ( new Runnable () {
public void run () {
final Bitmap bitmap = loadImageFromNetwork (
" http :// example . com / image . png " );
mImageView . post ( new Runnable () {
public void run () {
mImageView . setImageBitmap ( bitmap );
}
});
}
}). start ();
}

Helper Methods
View.post() and View.postDelay() take a Runnable as argument and append it to the event queue of the
UI thread executing in the UI thread (with a delay for the second method)
Activity.runOnUiThread() takes a runnable and
runs it in the current thread if it is the UI thread
posts the runnable to the event queue of the UI thread otherwise

Using AsyncTask
Hides all the boilerplate code (creating a runnable, a thread and posting the result to the UI thread)
Subclass AsyncTask as an inner class
Implement its doInBackground() (run in the worker thread)
Implement its onPostExecute() (run in the UI thread)

AsyncTask Example
public void onClick ( View v ) {
new Dow nloadImageTask (). execute ( " http :// example . com / image . png " );
}
private class DownloadImageTask extends AsyncTask < String , Void , Bitmap > {
// Receives parameters from execute ()
protected Bitmap doInBackground ( String ... urls ) {
return l oadImageFromNetwork ( urls [0]);
}
// Receives the value returned by doInBackground ()
protected void onPostExecute ( Bitmap result ) {
mImageView . setImageBitmap ( result );
}
}

Conclusion
Modern mobile devices tend to hide files from users
Data storage in Android can be made in several different manners
Content providers hide the implementation of the storage to activities
Content providers can be shared across many applications
Multithreading is advised when doing I/O

MW/2011/TIES425/Data Storage

10

Вам также может понравиться