Android Data

B6ea8bb06748e123565b6a997febbac8?s=47 Michael Pardo
September 21, 2014

Android Data

Android gives you several options to store your data, and the Android community gives you even more. This talk will cover the many different data storage options available to you and the reasons you might choose one over the other. We'll look at implementation details, common pitfalls, and examples of how you might use each in the real world.

B6ea8bb06748e123565b6a997febbac8?s=128

Michael Pardo

September 21, 2014
Tweet

Transcript

  1. Android Data Michael Pardo

  2. Android Storage Options • SharedPreferences • Filesystem • SQLite

  3. Android Storage Options • SharedPreferences • Filesystem • SQLite •

    Network • NoSQL
  4. SharedPreferences

  5. SharedPreferences • Stores key-value pairs as XML • Good for

    a relatively small collection • Primitive data types only
  6. Real-world uses • Game item inventory • Application state •

    User preferences
  7. <?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="token">8176c32554b6c4a1937cf14580d4afbb</string> <boolean name="first_launch"

    value="false" /> </map>
  8. // Provide a name for the preferences file name SharedPreferences

    prefs = getSharedPreferences( PREFS_NAME, Context.MODE_PRIVATE);
  9. // Uses the activity's class name as the preferences file

    name SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
  10. // 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);
  11. // Use package name as the preferences file name SharedPreferences

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

    prefs = PreferenceManager.getDefaultSharedPreferences( context); ! // Same as above SharedPreferences prefs = getSharedPreferences( getPackageName() + "_preferences", Context.MODE_PRIVATE);
  13. // Get instance of SharedPreferences SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);

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

    // Retrieve stored value boolean firstRun = prefs.getBoolean("firstRun", true);
  15. // 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(); }
  16. Libraries • Complex Preferences • SimpleSharedPreferences • Typed Preferences

  17. Complex Preferences // Get instance of ComplexPreferences ComplexPreferences prefs =

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

    ComplexPreferences.getComplexPreferences( this, PREFS_NAME, Context.MODE_PRIVATE); ! // Persist Inventory prefs.putObject("inventory", inventory); prefs.commit();
  19. 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);
  20. SimpleSharedPreferences // Create instance of SimpleSharedPrefernces SimpleSharedPreferences prefs = new

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

    SimpleSharedPreferences(context); ! // Retrieve stored value boolean firstRun = prefs.getBoolean("firstRun", true);
  22. 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); }
  23. Typed Preferences // Get instance of SharedPreferences SharedPreferences prefs =

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

    getPreferences(Context.MODE_PRIVATE); ! // Create instance of BooleanPreference BooleanPreference firstRun = new BooleanPreference(prefs, "firstRun", true);
  25. 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); }
  26. Ease of Use Complexity of Data Amount of Data SharedPreferences

    Filesystem SQLite
  27. Filesystem

  28. Filesystem • Same as Java I/O API • Good for

    large amounts of data • Internal and external storage
  29. Internal Storage • Always available • Privately accessible by default

    • All files removed from internal storage upon uninstallation
  30. External Storage • Not always available • Publicly accessible •

    All files removed from getExternalFilesDir() upon uninstallation
  31. Real-world uses • API request cache • Game state object

    • Edited images
  32. 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();
  33. 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();
  34. <!-- Read permission is implicit. --> <manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"

    /> ... </manifest>
  35. <!-- Read permission is implicit. --> <manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"

    /> ... </manifest> ! ! <!-- Will be required in a future release, but not at the moment. --> <manifest ...> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ... </manifest>
  36. public boolean isExternalStorageWritable() { String state = Environment.getExternalStorageState(); return state.equals(Environment.MEDIA_MOUNTED);

    }
  37. 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); }
  38. 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();
  39. Libraries • Apache Commons IO • Guava

  40. 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); }
  41. Apache Commons IO // Get file lines File file =

    new File(getFilesDir() , "todo.txt"); List<String> lines = FileUtils.readLines(file, "UTF-8");
  42. 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"
  43. Guava // Read the lines of a UTF-8 text file

    ImmutableList<String> lines = Files.asCharSource(file, Charsets.UTF_8) .readLines();
  44. Guava // Count distinct word occurrences in a file Multiset<String>

    wordOccurrences = HashMultiset.create( Splitter.on(CharMatcher.WHITESPACE) .trimResults() .omitEmptyStrings() .split(Files.asCharSource(file, Charsets.UTF_8).read()));
  45. Guava // SHA-1 a file HashCode hash = Files.asByteSource(file).hash(Hashing.sha1());

  46. Guava // Copy the data from a URL to a

    file Resources.asByteSource(url).copyTo(Files.asByteSink(file));
  47. Ease of Use Complexity of Data Amount of Data SharedPreferences

    Filesystem SQLite
  48. SQLite

  49. SQLite • SQL database engine • Good for structured, relational

    data • A lot of boilerplate
  50. Real-world uses • Address book • Restaurant ordering system •

    Product inventory
  51. 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"; ... } }
  52. 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); } }
  53. // Create instance of database helper PostsDbHelper mDbHelper = new

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

    PostsDbHelper(getContext()); ! // Get writable database SQLiteDatabase db = mDbHelper.getWritableDatabase();
  55. // Create array of columns to retrieve String[] projection =

    { Post._ID, Post.COLUMN_NAME_TITLE, ... };
  56. // 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 );
  57. // Move to first row cursor.moveToFirst();

  58. // Move to first row cursor.moveToFirst(); ! // Get column

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

    index int index = cursor.getColumnIndexOrThrow(Post._ID); ! // Get column value long id = cursor.getLong(index);
  60. // 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();
  61. // 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);
  62. // 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 );
  63. // Create instance of ContentValues and set values ContentValues values

    = new ContentValues(); values.put(Post.COLUMN_NAME_AUTHOR_ID, authorId);
  64. // 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) };
  65. // 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 );
  66. // Create selection definition String selection = Post.COLUMN_NAME_AUTHOR_ID + "

    LIKE ?"; String[] selectionArgs = { String.valueOf(authorId) };
  67. // 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 );
  68. Libraries • ActiveAndroid • greenDAO • OrmLite • Sugar ORM

  69. ActiveAndroid @Table("posts") public class Post extends Model { @Column("title") String

    mTitle; @Column("author") User mAuthor; ... }
  70. ActiveAndroid // Query List<Post> posts = new Select().from(Post.class).execute();

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

    Save or update Post post = new Post(); post.setTitle("ActiveAndroid"); post.setAuthor(user); post.save();
  72. ActiveAndroid // Query List<Post> 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();
  73. 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");
  74. greenDAO // Query List<Post> posts = userDao.queryBuilder().list();

  75. greenDAO // Query List<Post> posts = userDao.queryBuilder().list(); ! // Save

    Post post = new Post(); post.setTitle("greenDAO"); post.setAuthor(user); daoSession.insert(post);
  76. greenDAO // Query List<Post> 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);
  77. greenDAO // Query List<Post> 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);
  78. OrmLite @DatabaseTable(tableName = "posts") public class Post { @DatabaseField private

    String title; @DatabaseField private User author; ... }
  79. OrmLite // Query List<Post> posts = postDao.queryForAll();

  80. OrmLite // Query List<Post> posts = postDao.queryForAll(); ! // Save

    Post post = new Post(); post.setTitle("ORMLite"); post.setAuthor(user); postDao.create(post);
  81. OrmLite // Query List<Post> 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);
  82. OrmLite // Query List<Post> 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);
  83. SugarORM public class Post extends SugarRecord<Post> { String title; User

    author; ... }
  84. SugarORM // Query List<Post> posts = Post.find(Post.class);

  85. SugarORM // Query List<Post> posts = Post.find(Post.class); ! // Save

    or update Post post = new Post(); post.setTitle("SugarORM"); post.setAuthor(user); SugarRecord.save(post);
  86. SugarORM // Query List<Post> 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);
  87. A note about ORMs

  88. Ease of Use Complexity of Data Amount of Data SharedPreferences

    Filesystem SQLite
  89. Network

  90. Network • File sync or mobile backend • Cross-device storage

    • Many storage options • Not always available
  91. Sync Libraries • Box • Dropbox Sync • Google Drive

    • OneDrive
  92. MBaaS Libraries • BaasBox • Helios • DropBox Datastore •

    Kinvey • Parse
  93. BaasBox • Administration console • Users management • Content management

    • File management • “Friendship” • DB management • Push notifications • API access management
  94. Helios • Data synchronization • Push notifications • In-App purchases

    • Logging and Analytics • Passbook & Newsstand (iOS)
  95. Dropbox Datastore • Cloud datastore • Local datastores • Shared

    datastores • Push notifications
  96. Kinvey • Cloud datastore • Local datastore • User management

    • File management • Push notifications • Location content • Backend services • Encryption • External data sources
  97. Parse • Cloud datastore • Local datastore • Social integration

    • Push notifications • Analytics
  98. NoSQL

  99. NoSQL • Key value pairs or documents • Simple design

    • Non-relational • Implementations vary
  100. Libraries • Couchbase Lite • JasDB • SimpleNoSQL • SnappyDB

    • WaspDB
  101. Couchbase Lite • JSON document store • Full query API

    • Server and peer-to-peer replication
  102. JasDB • JSON document store • Full query API •

    REST and native APIs
  103. SimpleNoSQL • JSON document store • Lightweight functional queries •

    Backed by SQLite
  104. SnappyDB • Key value store • Use primitive type and

    array values • Based on Google’s LevelDB • Uses Snappy compression and Kryo serialization
  105. WaspDB • Key value store • Use complex key and

    value types • Uses Kryo serialization
  106. Easy to use Complex data A lot of data Distributed

    SharedPreferences Filesystem SQLite Network NoSQL
  107. Questions? @pardom