android data persistence
TRANSCRIPT
![Page 1: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/1.jpg)
Android: data persistence
Romain Rochegude2016.09.30
1
![Page 2: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/2.jpg)
Introduction
2
![Page 3: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/3.jpg)
Introduction
• POO basics: modeling the domain• Modeling domain objects and their interactions• Data bound with a remote API• Need of a local database
3
![Page 4: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/4.jpg)
The native way
4
![Page 5: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/5.jpg)
The “raw” way
private static final StringSQL_CREATE_ENTRIES =
"CREATE TABLE REPO (" +"_ID INTEGER PRIMARY KEY," +"NAME TEXT)";
private static final StringSQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS REPO ";
5
![Page 6: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/6.jpg)
• Subclass SQLiteOpenHelper
public class ReposDbHelper extendsSQLiteOpenHelper {
public static final intDATABASE_VERSION = 1;
public static final StringDATABASE_NAME = "repos.db";
public ReposDbHelper(Contextcontext) {
super(context, DATABASE_NAME,null, DATABASE_VERSION);
}//...
6
![Page 7: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/7.jpg)
//...public void onCreate(SQLiteDatabase
db) {db.execSQL(SQL_CREATE_ENTRIES);
}
public void onUpgrade(SQLiteDatabasedb, int oldVersion, intnewVersion) {
db.execSQL(SQL_DELETE_ENTRIES);onCreate(db);
}}
7
![Page 8: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/8.jpg)
• Get an instance of SQLiteOpenHelper
ReposDbHelper dbHelper =new ReposDbHelper(getContext());
8
![Page 9: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/9.jpg)
• Put Information into a Database
SQLiteDatabase db =dbHelper.getWritableDatabase();
ContentValues values = newContentValues();
values.put("name", "a sample name");
long newRowId = db.insert("REPO", null,values);
9
![Page 10: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/10.jpg)
• Read Information from a Database
SQLiteDatabase db =dbHelper.getReadableDatabase();
String[] projection = { "_id", "name" };
String selection = "NAME = ?";String[] selectionArgs = { "a sample
name" };
String sortOrder = "NAME DESC";
10
![Page 11: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/11.jpg)
Cursor cursor = db.query("REPO",projection,selection,selectionArgs,null,null,sortOrder);
cursor.moveToFirst();
long itemId = cursor.getLong(cursor.getColumnIndexOrThrow("_ID")
);
• Recommendation to setup a “contract” class11
![Page 12: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/12.jpg)
The ContentProvider way
• Provide a ContentProvider subclassdedicated to the application
public class RepoProvider extendsContentProvider {
}
12
![Page 13: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/13.jpg)
• Define a specific UriMatcher and configureavailable URI
public class RepoProvider extendsContentProvider {
private static final UriMatchersUriMatcher =
newUriMatcher(UriMatcher.NO_MATCH);
}
13
![Page 14: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/14.jpg)
static {sUriMatcher.addURI("fr.test.app.provider",
"repo", 1);sUriMatcher.addURI("fr.test.app.provider",
"repo/#", 2);}
14
![Page 15: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/15.jpg)
• Override various CRUD methods
public Cursor query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder) {//...
15
![Page 16: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/16.jpg)
//...switch (sUriMatcher.match(uri)) {
case 2:selection = selection + "_ID = "
+ uri.getLastPathSegment();break;
default://...
}
16
![Page 17: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/17.jpg)
• Use it through a ContentResolver instance
mCursor = getContentResolver().query("fr.test.app.provider/repo/2",mProjection,mSelectionClause,mSelectionArgs,mSortOrder);
17
![Page 18: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/18.jpg)
• Refer to the open-sourced Google’s ioschedapplication
• Helpful libraries to simplify the deal with aContentProvider:
• ProviGen• AutoValue: Cursor Extension
18
![Page 19: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/19.jpg)
Async management
• Perform CRUD operations outside of the mainthread
19
![Page 20: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/20.jpg)
Query data using Loader
• To query the ContentProvider in anActivity, make it implementingLoaderManager.LoaderCallbacks<Cursor>
public class ReposListActivityextends FragmentActivityimplements
LoaderManager.LoaderCallbacks<Cursor>{
}
20
![Page 21: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/21.jpg)
• Start loading data with a loader identifier
getLoaderManager().initLoader(LOADER_REPOS, null,
this);
• Implement LoaderCallbacks. . .
21
![Page 22: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/22.jpg)
• . . . to create the CursorLoader
@Overridepublic Loader<Cursor> onCreateLoader(
int id, Bundle bundle) {if(id == LOADER_REPOS) {
return new CursorLoader(this,"fr.test.app.provider/repo",mProjection,null, null, null);
}//...
} 22
![Page 23: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/23.jpg)
• . . . and deal with result
@Overridepublic void
onLoadFinished(Loader<Cursor> loader,Cursor cursor) {
if(loader.getId() == LOADER_REPOS){// ...
}}
23
![Page 24: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/24.jpg)
• See also: AsyncQueryHandler
24
![Page 25: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/25.jpg)
The ORM way
25
![Page 26: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/26.jpg)
The well-known: ORMLite
• Declare your model using ORMLite annotations@DatabaseTable(tableName = "REPO")public class RepoEntity {
@DatabaseField(columnName = "_ID",generatedId = true)
public long _id;
@DatabaseFieldpublic String name;
}26
![Page 27: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/27.jpg)
• declare the corresponding DAO
public class DAORepo extendsBaseDaoImpl<RepoEntity, Long> {
public DAORepo(ConnectionSource cs)throws SQLException {this(cs, RepoEntity.class);
}
//...}
27
![Page 28: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/28.jpg)
• subclass OrmLiteSqliteOpenHelper
public class DatabaseHelperTestextends OrmLiteSqliteOpenHelper {
private static final StringDATABASE_NAME = "test.db";
private static final intDATABASE_VERSION = 1;
//...
28
![Page 29: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/29.jpg)
//...public DatabaseHelperTest(
Context context) {
super(context,DATABASE_NAME,null,DATABASE_VERSION,R.raw.ormlite_config);
}//...
29
![Page 30: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/30.jpg)
//...@Overridepublic void onCreate(SQLiteDatabase db,
ConnectionSource cs) {TableUtils.createTable(cs,
RepoEntity.class);}
@Overridepublic void onUpgrade(SQLiteDatabase db,
ConnectionSource cs, int oldVersion,int newVersion) {
//...}
30
![Page 31: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/31.jpg)
• get the requested DAODatabaseHelperTest helper = //...
ConnectionSource cs =helper.getConnectionSource();
DatabaseTableConfig<RepoEntity>tableConfig =
DatabaseTableConfigUtil.fromClass(cs,RepoEntity.class);
DAORepo dao = new DAORepo(cs,tableConfig); 31
![Page 32: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/32.jpg)
• perform CRUD operations
// create
RepoEntity repo =new RepoEntity("a sample name");
dao.create(repo);
32
![Page 33: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/33.jpg)
// read
List<RepoEntity> repos =dao.queryBuilder()
.where()
.eq("name", "a sample name")
.query();
33
![Page 34: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/34.jpg)
• Performance: orm-gap gradle pluginbuildscript {
repositories {mavenCentral()
}dependencies {
classpath'com.github.stephanenicolas.ormgap'
+ ':ormgap-plugin:1.0.0-SNAPSHOT'}
}
apply plugin: 'ormgap' 34
![Page 35: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/35.jpg)
• generate an ORMLite configuration file thatboosts DAOs creations
• to use this file
public RepoDatabaseHelper(Contextcontext) {
super(context,DATABASE_NAME,null,DATABASE_VERSION,R.raw.ormlite_config);
}
35
![Page 36: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/36.jpg)
The attractive way: requery
• Object mapping• SQL generator• RxJava and Java 8 support• No reflection, compile-time processing and
generation• Relationships support• Callback method (@PostLoad)• Custom type converters
36
![Page 37: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/37.jpg)
• Define object mapping
@Entityabstract class Repo {
@Key @Generatedint id;
String name;}
37
![Page 38: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/38.jpg)
• Easy to perform SQL queries
Result<Repo> repos = data.select(Repo.class).where(Repo.NAME.lower().like("%sample%")).orderBy(Repo.ID.desc()).get();
38
![Page 39: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/39.jpg)
Async management: RxJava
• Get a specific instance of SingleEntityStore
DatabaseSource dbSource =new DatabaseSource(context,
Models.DEFAULT, "test.db", 1);
Configuration conf =dbSource.getConfiguration();
SingleEntityStore<Persistable> data =RxSupport.toReactiveStore(new
EntityDataStore<>(conf));39
![Page 40: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/40.jpg)
• and use it the RX way
data.select(RepoEntity.class).get().subscribeOn(Schedulers.newThread()).subscribe(/*...*/)
40
![Page 41: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/41.jpg)
Conclusion
41
![Page 42: Android Data Persistence](https://reader030.vdocuments.mx/reader030/viewer/2022020213/5877a9961a28ab826e8b69a3/html5/thumbnails/42.jpg)
Conclusion
• Personal assessments of each way
ContentProvider ORMLite requery
setup - + +
performance + - +
readability - + +
maintainability - + +
42