Date post: | 24-Oct-2014 |
Category: |
Documents |
Upload: | nikhil-thakkar |
View: | 21 times |
Download: | 0 times |
Data Persistence in Android
Jussi Pohjolainen Tampere University of Applied Sciences
Contents
• Overview • Preferences • Using files • Using databases • Accessing Content Providers
Overview of Data Storing
• App data is private to the applicaBon • Several mechanism – Preferences
• Lightweight mechanism to store and retrieve key-‐value pairs – Files
• Open and save files on the device or removable storage – SQLite databases
• 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)
How?
• SharedPreferences provides general framework for saving and retrieving
• To get SharedPreferences, use – getSharedPreferences(String name) – Use if you need mulBple preference files (idenBfied by name)
– getPreferences() – Use if only one preferences file is needed
• To write values, call edit() to get SharedPreferences.Editor to be used when adding values to file.
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();
}
}
Files • It's possible to store files on the mobile device or on a
removable storage • Current applica-on folder only. ExcepBon thrown
otherwise. – Modes: MODE_PRIVATE, MODE_WORLD_READABLE, MODE_WORLD_WRITABLE
• Reading – openFileInput()
• WriBng – openFileOutput()
• Standard Java streams aZer that
StaBc files
• You can save staBc files into res/raw directory • Accessing using openRawResource(R.raw.<filename>)
• Returns InputStream • Cannot write to data
Files // 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()
Reading MODE_WORLD_READABLE file
• When reading public file, use FileInputStream (vs. openFileInput)
• Give full path: /data/data/<package>/files
SQLite • Support for SQlite databases – Database is private to the applicaBon that creates it
• 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.
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();
}
}
SQLiteOpenHelper
• 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 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); } }
// 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 defined in manifest file
Usage // Query
Cursor c = getContentResolver().query(URI, ..., ... ,...);
// Insert
getContentResolver().insert(URI, newValues);
// Delete
getContentResolver().delete(URIofTheRow, ...);
URIs
• Content URIs must be unique between providers.
• Use your package name • General form – content://com.<company>.provider.<app>/<data>
• Example for querying all items – content://fi.pohjolainen_jussi.provider.myapp/items
• Example for querying single item (fiZh) – content://fi.pohjolainen_jussi.provider.myapp/items/5
Manifest
<?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) {...}
}
URLMatcher
• The content provider can give different 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 differenBate 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;
}
}