v3.0.0 (Dec 21, 2017)
Aug 31, 2014
Dec 31, 2018 (Retired)
Pushtorefresh (pushtorefresh)
Artem Zinnatullin :slowpoke: (artem-zinnatullin)
Shintaro Katafuchi (hotchemi)
Jens Driller (jenzz)
Yasuhiro SHIMIZU (yshrsmz)
Dmitrii Nikitin (nikitin-da)
Anton Pogonets (anton-pogonets)
Ilya Zorin (geralt-encore)
Mohd Farid (mfarid)
Vlad Shvaydetskiy (omihaz)
Iiro Krankka (roughike)
Krzysztof Skrzynecki (skrzyneckik)
Ahmad El-Melegy (mlegy)
Rustam (rsinukov)
Valery Kulikov (ValeriusGC)
Yuri K (mohaxspb)
Source code
APK file


StorIO — modern API for SQLiteDatabase and ContentResolver

  • Powerful & Simple set of Operations: Put, Get, Delete
  • API for Humans: Type Safety, Immutability & Thread-Safety
  • Convenient builders with compile-time guarantees for required params. Forget about 6-7 null in queries
  • Optional Type-Safe Object Mapping, if you don't want to work with Cursor and ContentValues you don't have to
  • No reflection in Operations and no annotations in the core, also StorIO is not ORM
  • Full control over queries, transaction and object mapping
  • Every Operation over StorIO can be executed as blocking call or as io.reactivex.Flowable/io.reactivex.Single/io.reactivex.Completable/io.reactivex.Maybe
  • RxJava as first class citizen, but it's not required dependency!
  • Reactive: io.reactivex.Flowable from Get Operation will observe changes in StorIO (SQLite or ContentProvider) and receive updates automatically
  • StorIO is replacements for SQLiteDatabase and ContentResolver APIs
  • StorIO + RxJava is replacement for Loaders API
  • We are working on MockStorIO (similar to MockWebServer) for easy unit testing

You can find all releases on Maven Central.

Some examples

Get list of objects from SQLiteDatabase
List<Tweet> tweets = storIOSQLite
  .listOfObjects(Tweet.class) // Type safety
  .withQuery(Query.builder() // Query builder
    .where("author = ?")
    .whereArgs("artem_zin") // Varargs Object..., no more new String[] {"I", "am", "tired", "of", "this", "shit"}
    .build()) // Query is immutable — you can save it and share without worries
  .prepare() // Operation builder
  .executeAsBlocking(); // Control flow is readable from top to bottom, just like with RxJava
Put something to SQLiteDatabase
  .put() // Insert or Update
  .objects(someTweets) // Type mapping!
Delete something from SQLiteDatabase
    .where("timestamp <= ?")
    .whereArgs(System.currentTimeMillis() - 86400) // No need to write String.valueOf()

Reactive? Single.just(true)!

Get something as io.reactivex.Flowable and receive updates!
  .asRxFlowable(BackpressureStrategy.LATEST) // Get Result as io.reactivex.Flowable and subscribe to further updates of tables from Query!
  .observeOn(mainThread()) // All Rx operations work on
  .subscribe(tweets -> { // Please don't forget to dispose
     // Will be called with first result and then after each change of tables from Query
     // Several changes in transaction -> one notification
Want to work with plain Cursor, no problems
Cursor cursor = storIOSQLite
  .withQuery(Query.builder() // Or RawQuery
    .where("who_cares = ?")

How object mapping works?

You can set default type mappings when you build instance of StorIOSQLite or StorIOContentResolver
StorIOSQLite storIOSQLite = DefaultStorIOSQLite.builder()
  .addTypeMapping(Tweet.class, SQLiteTypeMapping.<Tweet>builder()
    .putResolver(new TweetPutResolver()) // object that knows how to perform Put Operation (insert or update)
    .getResolver(new TweetGetResolver()) // object that knows how to perform Get Operation
    .deleteResolver(new TweetDeleteResolver())  // object that knows how to perform Delete Operation
  // other options
  .build(); // This instance of StorIOSQLite will know how to work with Tweet objects

You can override Operation Resolver per each individual Operation, it can be useful for working with SQL JOIN.

To save you from coding boilerplate classes we created Annotation Processor which will generate PutResolver, GetResolver and DeleteResolver at compile time, you just need to use generated classes

Notice that annotation processors are not part of the library core, you can work with StorIO without them, we just made them to save you from boilerplate.

StorIOSQLite:dependencies { implementation 'com.pushtorefresh.storio3:sqlite-annotations:insert-latest-version-here' annotationProcessor 'com.pushtorefresh.storio3:sqlite-annotations-processor:insert-latest-version-here' }

StorIOContentResolver:dependencies { implementation 'com.pushtorefresh.storio3:content-resolver-annotations:insert-latest-version-here' annotationProcessor 'com.pushtorefresh.storio3:content-resolver-annotations-processor:insert-latest-version-here' }
@StorIOSQLiteType(table="tweets") publicclassTweet { // Annotated fields should have package-level visibility.@StorIOSQLiteColumn(name="author") String author; @StorIOSQLiteColumn(name="content") String content; // Please leave default constructor with package-level visibility.Tweet() {} }


In order to make annotation processors work with Kotlin you need to add the following to your

build.gradle:apply plugin: 'kotlin-kapt'

Then use

kapt configuration instead of annotationProcessor.@StorIOSQLiteType(table ="tweets") dataclassTweet @StorIOSQLiteCreator constructor( StorIOSQLiteColumn(name = "author") valauthor:String, StorIOSQLiteColumn(name = "content") valcontent:String)

AutoValue:@AutoValue@StorIOSQLiteType(table="tweets") publicabstractclassTweet { // Annotated methods should have package-level or public visibility.@StorIOSQLiteColumn(name="author") abstractStringauthor(); @StorIOSQLiteColumn(name="content") abstractStringcontent(); // Parameters order depends on declaration order.@StorIOSQLiteCreatorstaticTweetcreate(Stringauthor, Stringcontent) { returnnewAutoValue_Tweet(author, content); } }

Annotation Processor will generate three classes in same package as annotated class during compilation: