Slide 1

Slide 1 text

Android Data Michael Pardo

Slide 2

Slide 2 text

Android Storage Options • SharedPreferences • Filesystem • SQLite

Slide 3

Slide 3 text

Android Storage Options • SharedPreferences • Filesystem • SQLite • Network • NoSQL

Slide 4

Slide 4 text

SharedPreferences

Slide 5

Slide 5 text

SharedPreferences • Stores key-value pairs as XML • Good for a relatively small collection • Primitive data types only

Slide 6

Slide 6 text

Real-world uses • Game item inventory • Application state • User preferences

Slide 7

Slide 7 text

8176c32554b6c4a1937cf14580d4afbb

Slide 8

Slide 8 text

// Provide a name for the preferences file name SharedPreferences prefs = getSharedPreferences( PREFS_NAME, Context.MODE_PRIVATE);

Slide 9

Slide 9 text

// Uses the activity's class name as the preferences file name SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);

Slide 10

Slide 10 text

// Uses the activity's class name as the preferences file name SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); ! // Same as above SharedPreferences prefs = getSharedPreferences( activity.getLocalClassName(), Context.MODE_PRIVATE);

Slide 11

Slide 11 text

// Use package name as the preferences file name SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( context);

Slide 12

Slide 12 text

// Use package name as the preferences file name SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( context); ! // Same as above SharedPreferences prefs = getSharedPreferences( getPackageName() + "_preferences", Context.MODE_PRIVATE);

Slide 13

Slide 13 text

// Get instance of SharedPreferences SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);

Slide 14

Slide 14 text

// Get instance of SharedPreferences SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); ! // Retrieve stored value boolean firstRun = prefs.getBoolean("firstRun", true);

Slide 15

Slide 15 text

// Get instance of SharedPreferences SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); ! // Retrieve stored value boolean firstRun = prefs.getBoolean("firstRun", true); ! if (firstRun) { // Get instance of editor and persist value SharedPreferences.Editor editor = prefs.edit(); editor.putInt("firstRun", false); editor.commit(); }

Slide 16

Slide 16 text

Libraries • Complex Preferences • SimpleSharedPreferences • Typed Preferences

Slide 17

Slide 17 text

Complex Preferences // Get instance of ComplexPreferences ComplexPreferences prefs = ComplexPreferences.getComplexPreferences( this, PREFS_NAME, Context.MODE_PRIVATE);

Slide 18

Slide 18 text

Complex Preferences // Get instance of ComplexPreferences ComplexPreferences prefs = ComplexPreferences.getComplexPreferences( this, PREFS_NAME, Context.MODE_PRIVATE); ! // Persist Inventory prefs.putObject("inventory", inventory); prefs.commit();

Slide 19

Slide 19 text

Complex Preferences // Get instance of ComplexPreferences ComplexPreferences prefs = ComplexPreferences.getComplexPreferences( this, PREFS_NAME, Context.MODE_PRIVATE); ! // Persist Inventory prefs.putObject("inventory", inventory); prefs.commit(); ! // Retrieve Inventory Inventory inventory = prefs.getObject("inventory", Inventory.class);

Slide 20

Slide 20 text

SimpleSharedPreferences // Create instance of SimpleSharedPrefernces SimpleSharedPreferences prefs = new SimpleSharedPreferences(context);

Slide 21

Slide 21 text

SimpleSharedPreferences // Create instance of SimpleSharedPrefernces SimpleSharedPreferences prefs = new SimpleSharedPreferences(context); ! // Retrieve stored value boolean firstRun = prefs.getBoolean("firstRun", true);

Slide 22

Slide 22 text

SimpleSharedPreferences // Create instance of SimpleSharedPrefernces SimpleSharedPreferences prefs = new SimpleSharedPreferences(context); ! // Retrieve stored value boolean firstRun = prefs.getBoolean("firstRun", true); ! if (firstRun) { // Persist value prefs.putBoolean("firstRun", false); }

Slide 23

Slide 23 text

Typed Preferences // Get instance of SharedPreferences SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);

Slide 24

Slide 24 text

Typed Preferences // Get instance of SharedPreferences SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); ! // Create instance of BooleanPreference BooleanPreference firstRun = new BooleanPreference(prefs, "firstRun", true);

Slide 25

Slide 25 text

Typed Preferences // Get instance of SharedPreferences SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE); ! // Create instance of BooleanPreference BooleanPreference firstRun = new BooleanPreference(prefs, "firstRun", true); ! // Retrieve stored value if (firstRun.get()) { // Persist value firstRun.set(true); }

Slide 26

Slide 26 text

Ease of Use Complexity of Data Amount of Data SharedPreferences Filesystem SQLite

Slide 27

Slide 27 text

Filesystem

Slide 28

Slide 28 text

Filesystem • Same as Java I/O API • Good for large amounts of data • Internal and external storage

Slide 29

Slide 29 text

Internal Storage • Always available • Privately accessible by default • All files removed from internal storage upon uninstallation

Slide 30

Slide 30 text

External Storage • Not always available • Publicly accessible • All files removed from getExternalFilesDir() upon uninstallation

Slide 31

Slide 31 text

Real-world uses • API request cache • Game state object • Edited images

Slide 32

Slide 32 text

JSONObject gameState = game.getState(); ! String fileName = "game_state.json"; String fileContents = gameState.toString(); ! // Use context.openFileOutput() to open an output stream FileOutputStream fos = openFileOutput(fileName, Context.MODE_PRIVATE); ! fos.write(fileContents.getBytes()); fos.close();

Slide 33

Slide 33 text

JSONObject gameState = game.getState(); ! String fileName = "game_state.json"; String fileContents = gameState.toString(); ! // Use context.getFilesDir() to open an output stream FileOutputStream fos = new FileOutputStream(new File(getFilesDir(), fileName)); ! fos.write(fileContents.getBytes()); fos.close();

Slide 34

Slide 34 text

...

Slide 35

Slide 35 text

... ! ! ...

Slide 36

Slide 36 text

public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED); }

Slide 37

Slide 37 text

public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED); } ! public boolean isExternalStorageReadable() { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_MOUNTED_READ_ONLY); }

Slide 38

Slide 38 text

context.openFileOutput(String name, int mode); ! context.openFileInput(String name); ! context.getFileStreamPath(String name); ! context.getFilesDir(); ! context.getCacheDir(); ! Environment.getDownloadCacheDirectory(); ! Environment.getExternalStorageDirectory(); ! Environment.getExternalStoragePublicDirectory(String type); ! Environment.getExternalStorageState();

Slide 39

Slide 39 text

Libraries • Apache Commons IO • Guava

Slide 40

Slide 40 text

Apache Commons IO // Get URL content InputStream in = new URL( "http://developer.android.com" ).openStream(); try { String html = IOUtils.toString(in); } finally { IOUtils.closeQuietly(in); }

Slide 41

Slide 41 text

Apache Commons IO // Get file lines File file = new File(getFilesDir() , "todo.txt"); List lines = FileUtils.readLines(file, "UTF-8");

Slide 42

Slide 42 text

Apache Commons IO String filename = "/data/data/com.michaelpardo.app/cache/../todo.txt"; String normalized = FilenameUtils.normalize(filename); // result is "/data/data/com.michaelpardo.app/todo.txt"

Slide 43

Slide 43 text

Guava // Read the lines of a UTF-8 text file ImmutableList lines = Files.asCharSource(file, Charsets.UTF_8) .readLines();

Slide 44

Slide 44 text

Guava // Count distinct word occurrences in a file Multiset wordOccurrences = HashMultiset.create( Splitter.on(CharMatcher.WHITESPACE) .trimResults() .omitEmptyStrings() .split(Files.asCharSource(file, Charsets.UTF_8).read()));

Slide 45

Slide 45 text

Guava // SHA-1 a file HashCode hash = Files.asByteSource(file).hash(Hashing.sha1());

Slide 46

Slide 46 text

Guava // Copy the data from a URL to a file Resources.asByteSource(url).copyTo(Files.asByteSink(file));

Slide 47

Slide 47 text

Ease of Use Complexity of Data Amount of Data SharedPreferences Filesystem SQLite

Slide 48

Slide 48 text

SQLite

Slide 49

Slide 49 text

SQLite • SQL database engine • Good for structured, relational data • A lot of boilerplate

Slide 50

Slide 50 text

Real-world uses • Address book • Restaurant ordering system • Product inventory

Slide 51

Slide 51 text

public final class PostContract { public PostContract() {} ! public static abstract class Post implements BaseColumns { public static final String TABLE_NAME = "posts"; public static final String COLUMN_NAME_TITLE = "title"; public static final String COLUMN_NAME_AUTHOR_ID = “author_id"; ... } }

Slide 52

Slide 52 text

public class PostsDbHelper extends SQLiteOpenHelper { public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = "Posts.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) { db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); } ! public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { onUpgrade(db, oldVersion, newVersion); } }

Slide 53

Slide 53 text

// Create instance of database helper PostsDbHelper mDbHelper = new PostsDbHelper(getContext());

Slide 54

Slide 54 text

// Create instance of database helper PostsDbHelper mDbHelper = new PostsDbHelper(getContext()); ! // Get writable database SQLiteDatabase db = mDbHelper.getWritableDatabase();

Slide 55

Slide 55 text

// Create array of columns to retrieve String[] projection = { Post._ID, Post.COLUMN_NAME_TITLE, ... };

Slide 56

Slide 56 text

// Create array of columns to retrieve String[] projection = { Post._ID, Post.COLUMN_NAME_TITLE, ... }; ! // Retrieve cursor Cursor c = db.query( Post.TABLE_NAME, projection, selection, selectionArgs, null, // groupBy null, // having null, // orderBy null // limit );

Slide 57

Slide 57 text

// Move to first row cursor.moveToFirst();

Slide 58

Slide 58 text

// Move to first row cursor.moveToFirst(); ! // Get column index int index = cursor.getColumnIndexOrThrow(Post._ID);

Slide 59

Slide 59 text

// Move to first row cursor.moveToFirst(); ! // Get column index int index = cursor.getColumnIndexOrThrow(Post._ID); ! // Get column value long id = cursor.getLong(index);

Slide 60

Slide 60 text

// Move to first row cursor.moveToFirst(); ! // Get column index int index = cursor.getColumnIndexOrThrow(Post._ID); ! // Get column value long id = cursor.getLong(index); ! // Close cursor when finished cursor.close();

Slide 61

Slide 61 text

// Create instance of ContentValues and set values ContentValues values = new ContentValues(); values.put(Post.COLUMN_NAME_TITLE, title); values.put(Post.COLUMN_NAME_AUTHOR_ID, authorId);

Slide 62

Slide 62 text

// Create instance of ContentValues and set values ContentValues values = new ContentValues(); values.put(Post.COLUMN_NAME_TITLE, title); values.put(Post.COLUMN_NAME_AUTHOR_ID, authorId); ! // Insert row using ContentValues long newRowId = db.insert( Post.TABLE_NAME, null, values );

Slide 63

Slide 63 text

// Create instance of ContentValues and set values ContentValues values = new ContentValues(); values.put(Post.COLUMN_NAME_AUTHOR_ID, authorId);

Slide 64

Slide 64 text

// Create instance of ContentValues and set values ContentValues values = new ContentValues(); values.put(Post.COLUMN_NAME_AUTHOR_ID, authorId); ! // Create selection definition String selection = Post.COLUMN_NAME_AUTHOR_ID + " LIKE ?"; String[] selectionArgs = { String.valueOf(oldAuthorId) };

Slide 65

Slide 65 text

// Create instance of ContentValues and set values ContentValues values = new ContentValues(); values.put(Post.COLUMN_NAME_AUTHOR_ID, authorId); ! // Create selection definition String selection = Post.COLUMN_NAME_AUTHOR_ID + " LIKE ?"; String[] selectionArgs = { String.valueOf(oldAuthorId) }; ! // Update rows using ContentValues and selection int count = db.update( Post.TABLE_NAME, values, selection, selectionArgs );

Slide 66

Slide 66 text

// Create selection definition String selection = Post.COLUMN_NAME_AUTHOR_ID + " LIKE ?"; String[] selectionArgs = { String.valueOf(authorId) };

Slide 67

Slide 67 text

// Create selection definition String selection = Post.COLUMN_NAME_AUTHOR_ID + " LIKE ?"; String[] selectionArgs = { String.valueOf(authorId) }; ! // Delete rows using selection db.delete( Post.TABLE_NAME, selection, selectionArgs );

Slide 68

Slide 68 text

Libraries • ActiveAndroid • greenDAO • OrmLite • Sugar ORM

Slide 69

Slide 69 text

ActiveAndroid @Table("posts") public class Post extends Model { @Column("title") String mTitle; @Column("author") User mAuthor; ... }

Slide 70

Slide 70 text

ActiveAndroid // Query List posts = new Select().from(Post.class).execute();

Slide 71

Slide 71 text

ActiveAndroid // Query List posts = new Select().from(Post.class).execute(); ! // Save or update Post post = new Post(); post.setTitle("ActiveAndroid"); post.setAuthor(user); post.save();

Slide 72

Slide 72 text

ActiveAndroid // Query List posts = new Select().from(Post.class).execute(); ! // Save or update Post post = new Post(); post.setTitle("ActiveAndroid"); post.setAuthor(user); post.save(); ! // Delete post.delete();

Slide 73

Slide 73 text

greenDAO Schema schema = new Schema(1, "de.greenrobot.posts"); ! Entity post = schema.addEntity("post"); post.addIdProperty(); post.addStringProperty("title"); ! Property userIdProperty = user.addLongProperty("author").getProperty(); post.addToOne(user, userIdProperty); ! DaoGenerator daoGenerator = new DaoGenerator(); daoGenerator.generateAll(schema, "../MyProject/src-gen");

Slide 74

Slide 74 text

greenDAO // Query List posts = userDao.queryBuilder().list();

Slide 75

Slide 75 text

greenDAO // Query List posts = userDao.queryBuilder().list(); ! // Save Post post = new Post(); post.setTitle("greenDAO"); post.setAuthor(user); daoSession.insert(post);

Slide 76

Slide 76 text

greenDAO // Query List posts = userDao.queryBuilder().list(); ! // Save Post post = new Post(); post.setTitle("greenDAO"); post.setAuthor(user); daoSession.insert(post); ! // Update post.setModifiedDate(new Date()); daoSession.update(post);

Slide 77

Slide 77 text

greenDAO // Query List posts = userDao.queryBuilder().list(); ! // Save Post post = new Post(); post.setTitle("greenDAO"); post.setAuthor(user); daoSession.insert(post); ! // Update post.setModifiedDate(new Date()); daoSession.update(post); ! // Delete daoSession.delete(post);

Slide 78

Slide 78 text

OrmLite @DatabaseTable(tableName = "posts") public class Post { @DatabaseField private String title; @DatabaseField private User author; ... }

Slide 79

Slide 79 text

OrmLite // Query List posts = postDao.queryForAll();

Slide 80

Slide 80 text

OrmLite // Query List posts = postDao.queryForAll(); ! // Save Post post = new Post(); post.setTitle("ORMLite"); post.setAuthor(user); postDao.create(post);

Slide 81

Slide 81 text

OrmLite // Query List posts = postDao.queryForAll(); ! // Save Post post = new Post(); post.setTitle("ORMLite"); post.setAuthor(user); postDao.create(post); ! // Update post.setModifiedDate(new Date()); postDao.update(post);

Slide 82

Slide 82 text

OrmLite // Query List posts = postDao.queryForAll(); ! // Save Post post = new Post(); post.setTitle("ORMLite"); post.setAuthor(user); postDao.create(post); ! // Update post.setModifiedDate(new Date()); postDao.update(post); ! // Delete postDao.delete(post);

Slide 83

Slide 83 text

SugarORM public class Post extends SugarRecord { String title; User author; ... }

Slide 84

Slide 84 text

SugarORM // Query List posts = Post.find(Post.class);

Slide 85

Slide 85 text

SugarORM // Query List posts = Post.find(Post.class); ! // Save or update Post post = new Post(); post.setTitle("SugarORM"); post.setAuthor(user); SugarRecord.save(post);

Slide 86

Slide 86 text

SugarORM // Query List posts = Post.find(Post.class); ! // Save or update Post post = new Post(); post.setTitle("SugarORM"); post.setAuthor(user); SugarRecord.save(post); ! // Delete SugarRecord.delete(post);

Slide 87

Slide 87 text

A note about ORMs

Slide 88

Slide 88 text

Ease of Use Complexity of Data Amount of Data SharedPreferences Filesystem SQLite

Slide 89

Slide 89 text

Network

Slide 90

Slide 90 text

Network • File sync or mobile backend • Cross-device storage • Many storage options • Not always available

Slide 91

Slide 91 text

Sync Libraries • Box • Dropbox Sync • Google Drive • OneDrive

Slide 92

Slide 92 text

MBaaS Libraries • BaasBox • Helios • DropBox Datastore • Kinvey • Parse

Slide 93

Slide 93 text

BaasBox • Administration console • Users management • Content management • File management • “Friendship” • DB management • Push notifications • API access management

Slide 94

Slide 94 text

Helios • Data synchronization • Push notifications • In-App purchases • Logging and Analytics • Passbook & Newsstand (iOS)

Slide 95

Slide 95 text

Dropbox Datastore • Cloud datastore • Local datastores • Shared datastores • Push notifications

Slide 96

Slide 96 text

Kinvey • Cloud datastore • Local datastore • User management • File management • Push notifications • Location content • Backend services • Encryption • External data sources

Slide 97

Slide 97 text

Parse • Cloud datastore • Local datastore • Social integration • Push notifications • Analytics

Slide 98

Slide 98 text

NoSQL

Slide 99

Slide 99 text

NoSQL • Key value pairs or documents • Simple design • Non-relational • Implementations vary

Slide 100

Slide 100 text

Libraries • Couchbase Lite • JasDB • SimpleNoSQL • SnappyDB • WaspDB

Slide 101

Slide 101 text

Couchbase Lite • JSON document store • Full query API • Server and peer-to-peer replication

Slide 102

Slide 102 text

JasDB • JSON document store • Full query API • REST and native APIs

Slide 103

Slide 103 text

SimpleNoSQL • JSON document store • Lightweight functional queries • Backed by SQLite

Slide 104

Slide 104 text

SnappyDB • Key value store • Use primitive type and array values • Based on Google’s LevelDB • Uses Snappy compression and Kryo serialization

Slide 105

Slide 105 text

WaspDB • Key value store • Use complex key and value types • Uses Kryo serialization

Slide 106

Slide 106 text

Easy to use Complex data A lot of data Distributed SharedPreferences Filesystem SQLite Network NoSQL

Slide 107

Slide 107 text

Questions? @pardom