Vous êtes sur la page 1sur 28


Persistence in Android
Jussi Pohjolainen Tampere University of Applied Sciences

Overview Preferences Using les Using databases Accessing Content Providers

Overview of Data Storing

App data is private to the applicaBon Several mechanism
Lightweight mechanism to store and retrieve key-value pairs

Open and save les on the device or removable storage

SQLite databases

Content provider is used to give the data to other apps

Shared Preferences
Very simple way of share small amount of data between acBviBes Also for saving the state of the app Preferences have names, so apps other components can found them Preferences can be private or public (world readable, writeable)

SharedPreferences provides general framework for saving and retrieving To get SharedPreferences, use
getSharedPreferences(String name) Use if you need mulBple preference les (idenBed by name) getPreferences() Use if only one preferences le is needed

To write values, call edit() to get SharedPreferences.Editor to be used when adding values to le.

Preferences Example
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); }

@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(); } }

It's possible to store les on the mobile device or on a removable storage Current applica-on folder only. ExcepBon thrown otherwise.



Standard Java streams aZer that

StaBc les
You can save staBc les into res/raw directory Accessing using openRawResource (R.raw.<lename>) Returns InputStream Cannot write to data

// Write String FILENAME = "hello_file"; String string = "hello world!"; FileOutputStream fos = openFileOutput(FILENAME, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close(); // Read FileInputStream fis = openFileInput(FILENAME); int byteChar; while((byteChar = fis.read()) != -1) { System.out.println((char) byteChar); } fis.close()

When reading public le, use FileInputStream (vs. openFileInput) Give full path: /data/data/<package>/les

Support for SQlite databases SQLiteDatabase object represents a database SQLiteOpenHelper wrapper for database acBons Android SDK has a tool called sqlite3 which enables you to browse table contens using sql commands and command line All databases are stored in /data/data/ <package_name>/databases folder on your device.
Database is private to the applicaBon that creates it

SQLiteDatabase: Open
public void openDatabase() { try { db = this.openOrCreateDatabase("MyTestDatabase.db", MODE_PRIVATE, null); db.execSQL("CREATE TABLE MYDATA(firstname TEXT, lastname TEXT)"); } catch (SQLiteException e) { e.printStackTrace(); } }

SQLiteDatabase: Insert
public void insertRows() { try { db.execSQL("INSERT INTO MYDATA VALUES ('Jack', 'Smith')"); } catch (Exception e) { e.printStackTrace(); } }

SQLiteDatabase: Read
public void readRows() { try { Cursor c = db.rawQuery("SELECT * FROM MYDATA", null); String text = ""; c.moveToFirst(); for(int i=0; i<c.getCount(); i++) { text += c.getString(0) + " " + c.getString(1); c.moveToNext(); } tv.setText(text); } catch (Exception e) { e.printStackTrace(); } }

Wraps best pracBce pacern for creaBng, opening and upgrading databases You hide the logic used to decide if a database needs to be created or upgraded before it's opened

Recommended Way: SQLiteOpenHelper

public class DictionaryOpenHelper extends SQLiteOpenHelper { private private private static final int DATABASE_VERSION = 2; static final String DICTIONARY_TABLE_NAME = "dictionary"; 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); }

// Example public class SQLiteExample extends Activity { private MyDataHelper dbhelper; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); dbhelper = new MyDataHelper(this); dbhelper.deleteAll(); dbhelper.insert("Jussi"); dbhelper.insert("Pekka"); dbhelper.insert("Tiina"); List<String> list = dbhelper.selectAll(); for(String object : list) { System.out.println(object); } } }

Remote Shell for Databases

Content Providers
The recommended way of sharing data in Android is to use content providers Generic interface for data Permission control and accessing using URI model NaBve databases available as Content Providers Publishing your own data source, other apps can incorporate your database

Content Resolver
ApplicaBon context has Content Resolver which you can use to access data
ContentResolver cr = getContentResolver();

For accessing other databases, you need a URI URI is arbitraty String, which is dened in manifest le

// Query Cursor c = getContentResolver().query(URI, ..., ... ,...); // Insert getContentResolver().insert(URI, newValues); // Delete getContentResolver().delete(URIofTheRow, ...);

Content URIs must be unique between providers. Use your package name General form

Example for querying all items


Example for querying single item (Zh)


<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="fi.tamk" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".CallMe" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name=".Data" android:authorities="fi.pohjolainen_jussi.provider.mycontentprovider"></provider> </application> <uses-sdk android:minSdkVersion="8" /> </manifest>

Accessing Content Provider

public class MyContentProvider extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ContentResolver cr = getContentResolver(); Uri uri = Uri.parse("content://fi.pohjolainen_jussi.provider.mycontentprovider"); Cursor cursor = cr.query(uri, null, null, null, null); } }

Extend Content Provider

public class MyContentProvider extends ContentProvider { @Override public int delete(Uri uri, String selection, String[] selectionArgs) {...} @Override public String getType(Uri uri) {...} @Override public Uri insert(Uri uri, ContentValues values) {...} @Override public boolean onCreate() {...} @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {...} @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {...} }

The content provider can give dierent results depending on the URI:
content:// fi.pohjolainen_jussi.provider.myapp/items content:// fi.pohjolainen_jussi.provider.myapp/ items/5

Use UBlity class URLMatcher to dierenBate the given URIs

Example of URIMatcher
public class Data extends ContentProvider { @Override public boolean onCreate() { initializeUriMatcher(); return false; } ... // Differentiate between different URI requests private static final int ALLROWS = 1; private static final int SINGLE_ROW = 2; // UriMatcher is utility class for aiding matching URIs in content providers private UriMatcher uriMatcher; private void initializeUriMatcher() { // Root node for the URI Matcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); // Add a URI to match, and the code to return when this URI is matched uriMatcher.addURI("fi.pohjolainen_jussi.provider.mycontentprovider", "items", ALLROWS); uriMatcher.addURI("fi.pohjolainen_jussi.provider.mycontentprovider", "items/#", SINGLE_ROW); } }

Example of URIMatcher
public class Data extends ContentProvider { @Override public boolean onCreate() { initializeUriMatcher(); return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch(uriMatcher.match(uri)) { case SINGLE_ROW: String rowNumber = uri.getPathSegments().get(1); // .. break; case ALLROWS: // break; }

return null; } }