Slide 1

Slide 1 text

 Jerzy Chalupski chalup, futuresimple  [email protected] Data model on Android Base CRM http://porcupineprogrammer.blogspot.com/ 

Slide 2

Slide 2 text

Data model on Android 61 releases 200+ migrations 50 models ~90 relations ~2 years on Google Play ~100k LOC

Slide 3

Slide 3 text

futuresimple chalup

Slide 4

Slide 4 text

UI + Services ContentProvider Database

Slide 5

Slide 5 text

UI + Services ContentProvider Database BOILERPLATE UNFRIENDLY APIS

Slide 6

Slide 6 text

UI + Services ContentProvider Database BOILERPLATE BOILERPLATE UNFRIENDLY APIS

Slide 7

Slide 7 text

UI + Services ContentProvider Database BOILERPLATE BOILERPLATE UNFRIENDLY APIS MIGRATIONS BOILERPLATE

Slide 8

Slide 8 text

UI + Services ContentProvider Database BOILERPLATE UNFRIENDLY APIS

Slide 9

Slide 9 text

public class Address { public Long id; public String region; public String zip; public String country; public String city; public String street; } @Override public ContentValues getContentValues() { ContentValues values = new ContentValues(); values.put(Addresses.ID, id); values.put(Addresses.REGION, region); values.put(Addresses.ZIP, zip); values.put(Addresses.COUNTRY, country); values.put(Addresses.CITY, city); values.put(Addresses.STREET, street); return values; } POJOs with android.content.*

Slide 10

Slide 10 text

public class Address { public Long id; public String region; public String zip; public String country; public String city; public String street; } public static Address getObjectFromCursor(Cursor c) { Address object = new Address(); int cId = c.getColumnIndex(Addresses.ID); object.id = c.isNull(cId) ? null : c.getLong(cId); object.region = c.getString(c.getColumnIndex(Addresses.REGION)); object.zip = c.getString(c.getColumnIndex(Addresses.ZIP)); object.country = c.getString(c.getColumnIndex(Addresses.COUNTRY)); object.city = c.getString(c.getColumnIndex(Addresses.CITY)); object.street = c.getString(c.getColumnIndex(Addresses.STREET)); return object; } POJOs with android.content.*

Slide 11

Slide 11 text

MINI QUIZ TIME! What’s wrong with this code? Cursor c = /* some valid cursor */; boolean isDirty = c.getBoolean(c.getColumnIndex("is_dirty"));

Slide 12

Slide 12 text

MINI QUIZ TIME! What’s wrong with this code? “Cannot resolve method Cursor.getBoolean(int)” Cursor c = /* some valid cursor */; boolean isDirty = c.getBoolean(c.getColumnIndex("is_dirty"));

Slide 13

Slide 13 text

MINI QUIZ TIME! ContentValues values = new ContentValues(); values.put("is_dirty", true); What’s wrong with this code? “Cannot resolve method Cursor.getBoolean(int)” Cursor c = /* some valid cursor */; boolean isDirty = c.getBoolean(c.getColumnIndex("is_dirty"));

Slide 14

Slide 14 text

MINI QUIZ TIME! ContentValues values = new ContentValues(); values.put("is_dirty", true); boolean isDirty = c.getInt(c.getColumnIndex("is_dirty")) == 1; What’s wrong with this code? “Cannot resolve method Cursor.getBoolean(int)” Cursor c = /* some valid cursor */; boolean isDirty = c.getBoolean(c.getColumnIndex("is_dirty"));

Slide 15

Slide 15 text

public class Address { @Column(Addresses.ID) public Long id; @Column(Addresses.REGION) public String region; @Column(Addresses.ZIP) public String zip; @Column(Addresses.COUNTRY) public String country; @Column(Addresses.CITY) public String city; @Column(Addresses.STREET) public String street; } MicroOrm microOrm = new MicroOrm(); Address address = microOrm.fromCursor(cursor, Address.class); ContentValues values = microOrm.toContentValues(address); POJOs with MicroOrm

Slide 16

Slide 16 text

 chalup/microorm POJOs with MicroOrm List
addresses = Lists.newArrayList(); if (c != null && c.moveToFirst()) { do { addresses.add(Address.getObjectFromCursor(c)); } while (c.moveToNext()); } BEFORE: AFTER: List
addresses = microOrm.listFromCursor(c, Address.class);

Slide 17

Slide 17 text

MINI QUIZ TIME! How many parameters there is in ContentResolver’s query method? resolver.query(uri, ?);

Slide 18

Slide 18 text

MINI QUIZ TIME! How many parameters there is in ContentResolver’s query method? resolver.query(uri, null, null, null, null);

Slide 19

Slide 19 text

MINI QUIZ TIME! resolver.query(uri, null, null, null, null); resolver.query(uri, null, null, null, null, null); // API 16 How many parameters there is in ContentResolver’s query method?

Slide 20

Slide 20 text

MINI QUIZ TIME! resolver.query(uri, null, null, null, null); resolver.query(uri, null, null, null, null, null); // API 16 How many parameters there is in ContentResolver’s query method? What about SQLiteDatabase? db.query(table, ?);

Slide 21

Slide 21 text

MINI QUIZ TIME! resolver.query(uri, null, null, null, null); resolver.query(uri, null, null, null, null, null); // API 16 How many parameters there is in ContentResolver’s query method? What about SQLiteDatabase? db.query(table, null, null, null, null, null, null); db.query(table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null, null);

Slide 22

Slide 22 text

MINI QUIZ TIME! resolver.query(uri, null, null, null, null); resolver.query(uri, null, null, null, null, null); // API 16 How many parameters there is in ContentResolver’s query method? What about SQLiteDatabase? db.query(table, null, null, null, null, null, null); db.query(table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null, null);

Slide 23

Slide 23 text

MINI QUIZ TIME! resolver.query(uri, null, null, null, null); resolver.query(uri, null, null, null, null, null); // API 16 How many parameters there is in ContentResolver’s query method? What about SQLiteDatabase? db.query(table, null, null, null, null, null, null); db.query(table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null, null);

Slide 24

Slide 24 text

MINI QUIZ TIME! resolver.query(uri, null, null, null, null); resolver.query(uri, null, null, null, null, null); // API 16 How many parameters there is in ContentResolver’s query method? What about SQLiteDatabase? db.query(table, null, null, null, null, null, null); db.query(table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null); db.query(true, table, null, null, null, null, null, null, null, null);

Slide 25

Slide 25 text

ContentResolver API resolver.query(uri, null, null, null, null); resolver.query(uri, null, "is_dirty = ?", new String[] { String.valueOf(1) }, null ); ContentValues values = new ContentValues(); values.put("is_dirty", true); resolver.update(uri, values, null, null);

Slide 26

Slide 26 text

 futuresimple/android-db-commons ProviderAction.query(uri).perform(resolver); FluentCursor fluentCursor = ProviderAction .query(uri) .where("is_dirty = ?", 1) .perform(resolver); fluentCursor.toFluentIterable(new Function() { /* ... */ }); ProviderAction .update(uri) .value("is_dirty", true) .perform(resolver); ProviderAction API

Slide 27

Slide 27 text

@Override public Loader onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(getActivity()); loader.setUri(Contacts.CONTENT_URI); loader.setSelection("is_dirty = ?"); loader.setSelectionArgs(new String[] { String.valueOf(1) }); return loader; } @Override public void onLoadFinished(Loader loader, Cursor c) { if (c != null && c.moveToFirst()) { do { // interesting code } while (c.moveToNext()); } } CursorLoader API

Slide 28

Slide 28 text

@Override public Loader onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(getActivity()); loader.setUri(Contacts.CONTENT_URI); loader.setSelection("is_dirty = ?"); loader.setSelectionArgs(new String[] { String.valueOf(1) }); return loader; } @Override public void onLoadFinished(Loader loader, Cursor c) { if (c != null && c.moveToFirst()) { do { // interesting code } while (c.moveToNext()); } } CursorLoader API

Slide 29

Slide 29 text

CursorLoaderBuilder API @Override public Loader onCreateLoader(int id, Bundle args) { return CursorLoaderBuilder .forUri(Contacts.CONTENT_URI) .where("is_dirty = ?", 1) .build(getActivity()); }  futuresimple/android-db-commons

Slide 30

Slide 30 text

 futuresimple/android-db-commons @Override public Loader> onCreateLoader(int id, Bundle args) { return CursorLoaderBuilder .forUri(Contacts.CONTENT_URI) .transform(new Function() { /* ... */ }) .build(getActivity()); } @Override public Loader onCreateLoader(int id, Bundle args) { return CursorLoaderBuilder .forUri(Contacts.CONTENT_URI) .wrap(new Function() { /* ... */ }) .build(getActivity()); } CursorLoaderBuilder API

Slide 31

Slide 31 text

UI + Services ContentProvider Database MIGRATIONS BOILERPLATE

Slide 32

Slide 32 text

UI + Services ContentProvider Database MIGRATIONS BOILERPLATE

Slide 33

Slide 33 text

BACKEND MOBILE

Slide 34

Slide 34 text

BACKEND MOBILE

Slide 35

Slide 35 text

BACKEND MOBILE

Slide 36

Slide 36 text

BACKEND MOBILE

Slide 37

Slide 37 text

BACKEND MOBILE

Slide 38

Slide 38 text

BACKEND MOBILE

Slide 39

Slide 39 text

BACKEND MOBILE

Slide 40

Slide 40 text

BACKEND MOBILE

Slide 41

Slide 41 text

BACKEND MOBILE

Slide 42

Slide 42 text

BACKEND MOBILE

Slide 43

Slide 43 text

BACKEND MOBILE 250k items?

Slide 44

Slide 44 text

BACKEND MOBILE ?

Slide 45

Slide 45 text

BACKEND MOBILE

Slide 46

Slide 46 text

BACKEND MOBILE

Slide 47

Slide 47 text

BACKEND MOBILE OFFLINE MODE = DATA MIGRATIONS

Slide 48

Slide 48 text

SQLite’s ALTER TABLE no ALTER COLUMN no DROP COLUMN no touching of PRIMARY KEY no ADD/DROP/ALTER CONSTRAINT

Slide 49

Slide 49 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, first_name TEXT, last_name TEXT ); CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, - first_name TEXT, - last_name TEXT + name TEXT ); SQLite’s ALTER TABLE ?

Slide 50

Slide 50 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, first_name TEXT, last_name TEXT ); CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, - first_name TEXT, - last_name TEXT + name TEXT ); SQLite’s ALTER TABLE ALTER TABLE users RENAME TO tmp;

Slide 51

Slide 51 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, first_name TEXT, last_name TEXT ); CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, - first_name TEXT, - last_name TEXT + name TEXT ); SQLite’s ALTER TABLE ALTER TABLE users RENAME TO tmp; CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, name TEXT );

Slide 52

Slide 52 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, first_name TEXT, last_name TEXT ); CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, - first_name TEXT, - last_name TEXT + name TEXT ); SQLite’s ALTER TABLE ALTER TABLE users RENAME TO tmp; CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, name TEXT ); INSERT INTO users (_id, email) SELECT _id, email FROM tmp;

Slide 53

Slide 53 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, first_name TEXT, last_name TEXT ); CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, - first_name TEXT, - last_name TEXT + name TEXT ); SQLite’s ALTER TABLE ALTER TABLE users RENAME TO tmp; CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, name TEXT ); INSERT INTO users (_id, email) SELECT _id, email FROM tmp; DROP TABLE tmp;

Slide 54

Slide 54 text

 futuresimple/android-schema-utils Sane migrations API CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, first_name TEXT, last_name TEXT ); CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT, - first_name TEXT, - last_name TEXT + name TEXT ); TableMigration migration = TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .withMapping(/* ... */) .build(); mMigrationsHelper.performMigrations(db, migration);

Slide 55

Slide 55 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); Migrations gotcha v1

Slide 56

Slide 56 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); + first_name TEXT, + last_name TEXT Migrations gotcha v1 v5

Slide 57

Slide 57 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5

Slide 58

Slide 58 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); - email TEXT + email TEXT NOT NULL + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5 v9

Slide 59

Slide 59 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); - email TEXT + email TEXT NOT NULL + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); db.execSQL( ”DELETE FROM users” + ”WHERE email IS NULL” ); TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5 v9

Slide 60

Slide 60 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); - email TEXT + email TEXT NOT NULL + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); db.execSQL( ”DELETE FROM users” + ”WHERE email IS NULL” ); TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5 v9

Slide 61

Slide 61 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); - email TEXT + email TEXT NOT NULL + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); db.execSQL( ”DELETE FROM users” + ”WHERE email IS NULL” ); TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5 v9

Slide 62

Slide 62 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); - email TEXT + email TEXT NOT NULL + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); db.execSQL( ”DELETE FROM users” + ”WHERE email IS NULL” ); TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5 v9

Slide 63

Slide 63 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); - email TEXT + email TEXT NOT NULL + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); db.execSQL( ”DELETE FROM users” + ”WHERE email IS NULL” ); TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5 v9

Slide 64

Slide 64 text

CREATE TABLE users ( _id INTEGER PRIMARY KEY, email TEXT ); - email TEXT + email TEXT NOT NULL + first_name TEXT, + last_name TEXT TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); db.execSQL( ”DELETE FROM users” + ”WHERE email IS NULL” ); TableMigration .of(Tables.USERS) .to(CREATE_TABLE_USERS) .build(); Migrations gotcha v1 v5 v9 HAVE TO USE v5 SCHEMA

Slide 65

Slide 65 text

MIGRATIONS == OLD SCHEMAS

Slide 66

Slide 66 text

MINI QUIZ TIME! How would you keep old db schemas?

Slide 67

Slide 67 text

MINI QUIZ TIME! How would you keep old db schemas? CREATE_TABLE_X_r1500

Slide 68

Slide 68 text

MINI QUIZ TIME! How would you keep old db schemas? CREATE_TABLE_X_r1500 NO DIFFS

Slide 69

Slide 69 text

MINI QUIZ TIME! How would you keep old db schemas? CREATE_TABLE_X_r1500 List of upgrades NO DIFFS

Slide 70

Slide 70 text

MINI QUIZ TIME! How would you keep old db schemas? CREATE_TABLE_X_r1500 List of upgrades NO CURRENT SCHEMA NO DIFFS

Slide 71

Slide 71 text

MINI QUIZ TIME! How would you keep old db schemas? CREATE_TABLE_X_r1500 List of upgrades Current schema with list of downgrades NO CURRENT SCHEMA NO DIFFS

Slide 72

Slide 72 text

MINI QUIZ TIME! How would you keep old db schemas? CREATE_TABLE_X_r1500 List of upgrades Current schema with list of downgrades NO DIFFS NO CURRENT SCHEMA UNINTUITIVE

Slide 73

Slide 73 text

Sane(ish?) schema API private static final Schemas SCHEMA = Schemas.Builder .currentSchema(10, new TableDefinition(Tables.ADDRESSES, new AddColumn(Addresses.REGION, "TEXT"), new AddColumn(Addresses.ZIP, "TEXT"), new AddColumn(Addresses.COUNTRY, "TEXT"), new AddColumn(Addresses.CITY, "TEXT"), new AddColumn(Addresses.STREET, "TEXT ") ) ) .upgradeTo(7, clear(Tables.ADDRESSES)) .downgradeTo(4, new TableDowngrade(Tables.ADDRESSES, new DropColumn(Addresses.REGION) ) ) .downgradeTo(2, dropTable(Tables.ADDRESSES)) .build();  futuresimple/android-schema-utils

Slide 74

Slide 74 text

Look ma, no boilerplate! @Override public void onCreate(SQLiteDatabase db) { Schema currentSchema = SCHEMAS.getCurrentSchema(); for (String table : currentSchema.getTables()) { db.execSQL(currentSchema.getCreateTableStatement(table)); } } public void onUpgrade(final SQLiteDatabase db, int oldVersion, int newVersion) { SCHEMAS.upgrade(oldVersion, mContext, db); }  futuresimple/android-schema-utils

Slide 75

Slide 75 text

UI + Services ContentProvider Database MIGRATIONS BOILERPLATE

Slide 76

Slide 76 text

Obvious index is obvious db.execSQL("CREATE TABLE " + Tables.RAW_CONTACTS + " (" + // ... RawContacts.CONTACT_ID + " INTEGER REFERENCES contacts(_id)," + // ... ");"); db.execSQL("CREATE INDEX raw_contacts_contact_id_index ON " + Tables.RAW_CONTACTS + " (" + RawContacts.CONTACT_ID + ");");

Slide 77

Slide 77 text

“This would be easy if we knew the relations between our models...”

Slide 78

Slide 78 text

Thneed public static final ModelGraph MODEL_GRAPH = ModelGraph .of(ModelInterface.class) .identifiedByDefault().by(ModelColumns.ID) .where() .the(CONTACT).references(USER).by(Contacts.USER_ID) .the(CONTACT).groupsOther().by(Contacts.CONTACT_ID) .the(DEAL).references(CONTACT).by(Deals.CONTACT_ID) .the(DEAL).references(USER).by(Deals.USER_ID)  chalup/thneed

Slide 79

Slide 79 text

Thneed public static final ModelGraph MODEL_GRAPH = ModelGraph .of(ModelInterface.class) .identifiedByDefault().by(ModelColumns.ID) .where() .the(CONTACT).references(USER).by(Contacts.USER_ID) .the(CONTACT).groupsOther().by(Contacts.CONTACT_ID) .the(DEAL).references(CONTACT).by(Deals.CONTACT_ID) .the(DEAL).references(USER).by(Deals.USER_ID) public class ModelGraph { public void accept(ModelVisitor super TModel> visitor); public void accept(RelationshipVisitor super TModel> visitor); }  chalup/thneed

Slide 80

Slide 80 text

 futuresimple/android-autoindexer public class AutoIndexer { public static Collection generateIndexes(ModelGraph<~> graph); public static String getCreateStatement(SqliteIndex index); } AutoIndexer

Slide 81

Slide 81 text

 futuresimple/android-autoindexer public class AutoIndexer { public static Collection generateIndexes(ModelGraph<~> graph); public static String getCreateStatement(SqliteIndex index); } private static void createIndexes(SQLiteDatabase db) { FluentIterable indexes = FluentIterable .from(AutoIndexer.generateIndexes(MODEL_GRAPH)) .filter(Predicates.not(AutoIndexer.isIndexOnColumn(ModelColumns.ID))) .filter(Predicates.not(AutoIndexer.isIndexOnColumn(BaseColumns._ID))); for (SqliteIndex index : indexes) { db.execSQL(AutoIndexer.getCreateStatement(index)); } } AutoIndexer

Slide 82

Slide 82 text

MINI QUIZ TIME! What are the issues related to schema generation?

Slide 83

Slide 83 text

Autogenerate? Autodestruct. sqlite> select * from sqlite_master limit 10; type name tbl_name rootpage sql ---------- ---------------- ---------------- ---------- ------------------------------------------- table android_metadata android_metadata 3 CREATE TABLE android_metadata (locale TEXT) table stages stages 4 CREATE TABLE stages (_id INTEGER PRIMARY KE index sqlite_autoindex stages 5 table sqlite_sequence sqlite_sequence 6 CREATE TABLE sqlite_sequence(name,seq) table sources sources 7 CREATE TABLE sources (id INTEGER PRIMARY KE table contacts contacts 8 CREATE TABLE contacts (_id INTEGER PRIMARY index sqlite_autoindex contacts 9 table deals deals 10 CREATE TABLE deals (_id INTEGER PRIMARY KEY index sqlite_autoindex deals 11 table notes notes 12 CREATE TABLE notes (_id INTEGER PRIMARY KEY

Slide 84

Slide 84 text

Autogenerate? Autodestruct. SQLiteMaster.dropIndexes(db); SQLiteMaster.dropTriggers(db); SQLiteMaster.dropViews(db); List parts = SQLiteMaster.getSQLiteSchemaParts(db); sqlite> select * from sqlite_master limit 10; type name tbl_name rootpage sql ---------- ---------------- ---------------- ---------- ------------------------------------------- table android_metadata android_metadata 3 CREATE TABLE android_metadata (locale TEXT) table stages stages 4 CREATE TABLE stages (_id INTEGER PRIMARY KE index sqlite_autoindex stages 5 table sqlite_sequence sqlite_sequence 6 CREATE TABLE sqlite_sequence(name,seq) table sources sources 7 CREATE TABLE sources (id INTEGER PRIMARY KE table contacts contacts 8 CREATE TABLE contacts (_id INTEGER PRIMARY index sqlite_autoindex contacts 9 table deals deals 10 CREATE TABLE deals (_id INTEGER PRIMARY KEY index sqlite_autoindex deals 11 table notes notes 12 CREATE TABLE notes (_id INTEGER PRIMARY KEY  futuresimple/sqlitemaster

Slide 85

Slide 85 text

Autogenerate? Autodestruct. SQLiteMaster.dropIndexes(db); SQLiteMaster.dropTriggers(db); SQLiteMaster.dropViews(db); List parts = SQLiteMaster.getSQLiteSchemaParts(db); sqlite> select * from sqlite_master limit 10; type name tbl_name rootpage sql ---------- ---------------- ---------------- ---------- ------------------------------------------- table android_metadata android_metadata 3 CREATE TABLE android_metadata (locale TEXT) table stages stages 4 CREATE TABLE stages (_id INTEGER PRIMARY KE index sqlite_autoindex stages 5 table sqlite_sequence sqlite_sequence 6 CREATE TABLE sqlite_sequence(name,seq) table sources sources 7 CREATE TABLE sources (id INTEGER PRIMARY KE table contacts contacts 8 CREATE TABLE contacts (_id INTEGER PRIMARY index sqlite_autoindex contacts 9 table deals deals 10 CREATE TABLE deals (_id INTEGER PRIMARY KEY index sqlite_autoindex deals 11 table notes notes 12 CREATE TABLE notes (_id INTEGER PRIMARY KEY  futuresimple/sqlitemaster

Slide 86

Slide 86 text

 futuresimple/sqlitemaster Autogenerate? Autodestruct. SQLiteMaster.dropIndexes(db); SQLiteMaster.dropTriggers(db); SQLiteMaster.dropViews(db); List parts = SQLiteMaster.getSQLiteSchemaParts(db); sqlite> select * from sqlite_master limit 10; type name tbl_name rootpage sql ---------- ---------------- ---------------- ---------- ------------------------------------------- table android_metadata android_metadata 3 CREATE TABLE android_metadata (locale TEXT) table stages stages 4 CREATE TABLE stages (_id INTEGER PRIMARY KE index sqlite_autoindex stages 5 table sqlite_sequence sqlite_sequence 6 CREATE TABLE sqlite_sequence(name,seq) table sources sources 7 CREATE TABLE sources (id INTEGER PRIMARY KE table contacts contacts 8 CREATE TABLE contacts (_id INTEGER PRIMARY index sqlite_autoindex contacts 9 table deals deals 10 CREATE TABLE deals (_id INTEGER PRIMARY KEY index sqlite_autoindex deals 11 table notes notes 12 CREATE TABLE notes (_id INTEGER PRIMARY KEY

Slide 87

Slide 87 text

Forger forger; Upload upload = forger .iNeed(Upload.class) .with(Uploads.IS_CACHED, true) .in(mContentResolver); Byproduct: Forger  futuresimple/forger

Slide 88

Slide 88 text

Forger forger; Upload upload = forger .iNeed(Upload.class) .with(Uploads.IS_CACHED, true) .in(mContentResolver); Attachment attachment = forger .iNeed(Attachment.class) .relatedTo(upload) .in(mContentResolver); Byproduct: Forger  futuresimple/forger

Slide 89

Slide 89 text

Forger forger; Attachment attachment = forger .iNeed(Attachment.class) // .relatedTo(upload) .in(mContentResolver); Byproduct: Forger  futuresimple/forger

Slide 90

Slide 90 text

Forger forger; Upload upload = forger .iNeed(Upload.class) .with(Uploads.IS_CACHED, true) .in(mContentResolver); Attachment attachment = forger .iNeed(Attachment.class) .relatedTo(upload) .in(mContentResolver); Byproduct: Forger  futuresimple/forger

Slide 91

Slide 91 text

Forger forger = FORGER .inContextOf(User.class).in(mContentResolver); Upload upload = forger .iNeed(Upload.class) .with(Uploads.IS_CACHED, true) .in(mContentResolver); Attachment attachment = forger .iNeed(Attachment.class) .relatedTo(upload) .in(mContentResolver); Byproduct: Forger  futuresimple/forger

Slide 92

Slide 92 text

 futuresimple/forger @Test public void shouldNotifyAboutReadyShareRequest() throws Exception { FORGER .inContextOf(User.class).in(mContentResolver) .inContextOf(Contact.class).in(mContentResolver) .inContextOf(Upload.class).in(mContentResolver) .inContextOf(Attachment.class).in(mContentResolver) .inContextOf(ShareDocumentRequest.class).in(mContentResolver) .iNeed(ShareDocumentList.class).in(mContentResolver); testSubject.notifyReadyShareRequests(); verify(mNotificationManager).notify(anyInt(), any(Notification.class)); } Byproduct: Forger

Slide 93

Slide 93 text

UI + Services ContentProvider Database BOILERPLATE

Slide 94

Slide 94 text

Work in progress! AutoProvider Uri matching/building, CRUD basic implementation... ...or something completely replacing ContentProvider

Slide 95

Slide 95 text

Work in progress! AutoProvider Uri matching/building, CRUD basic implementation... ...or something completely replacing ContentProvider

Slide 96

Slide 96 text

Work in progress! AutoProvider TableJoiner Uri matching/building, CRUD basic implementation... ...or something completely replacing ContentProvider The SQLite helper to end all SQLite helpers!

Slide 97

Slide 97 text

Work in progress! AutoProvider TableJoiner Cerberus Uri matching/building, CRUD basic implementation... ...or something completely replacing ContentProvider The SQLite helper to end all SQLite helpers! Attaches itself to database and executes EXPLAIN QUERY PLAN for each query.

Slide 98

Slide 98 text

?

Slide 99

Slide 99 text

No content

Slide 100

Slide 100 text

Thanks.