0

Oddly enough, I don't seem to have found an answer regarding this.

I've been trying for a while to use the Android Room Persistence Library to store persistent data across app terminations, and one of the big sticking points for me is its asynchronous (results aren't ready the moment the database call returns) nature, requiring work to access the databases be done on separate threads instead, which is done in order to maximize performance.

And this doesn't seem to be a problem now except for one thing: it seems to me that in at least one big, important area, this is difficult to see how to do, at least when other concerns are taken into account, and that is the one of initializing a UI element, like an Activity or Fragment where the information displayed in it to the user and/or its state have to be loaded from the database. The "additional concerns" are related to those touched upon here:

Is it good practice implementing Parcelable on a Room database entity?

in particular, I am using a design setup that generally follows the principles mentioned in the answer there: namely, that I have a set of "model" objects which are designed to be agnostic as to the underlying database system we are using.

The problem, however, of course, is trying to figure out what to do on first initialization, when we first bring up the app and/or UI feature. The model object has to be created here, and ideally, it should be primed with data from the existing database. And that means we have to do a load from the database, which is also asynchronous. Yet, we cannot make the UI feature until we have that data that is supposed to fill it, and thus we have to wait.

And this runs into issues of good design practices: which are my primary question. In particular, I want to ensure the UI code knows - in line with what is mentioned in the linked answer - as little as possible about the underlying database implementation and that means it cannot be "informed", so to speak, of quirks like that some databases demand asynchronous accesses while others may not. And the current implementation has "synchronous" semantics - a "LOAD" call to the abstract database (called a "repository" in the question above) is expected to furnish data immediately, and likewise, after a "SAVE" call, an immediately-subsequent "LOAD" call should be expected to return the data just "SAVE"d. If we use a callback, then that means we have to do that in the UI code, which means we break the abstraction barrier, which is clearly poor design/coding practice and a basic OOP "no-no".

So this makes me wonder: is it "OK" practice for this particular instance to "cheat" by starting an AsyncTask or similar and then waiting with its "get()" method underneath the model object to fake synchronous access, or is this, as my smell test seems to be telling me (on the logic that, generally, if you're trying to break or work around a well-reasoned design feature, you're probably doing something wrong), a no-no, and if so, what is the "correct" way to handle this?

Moreover, none of the reference material I've found seems to address this even though it seems like such a basic and important use case, which is extremely aggravating and headpoppy.

The_Sympathizer
  • 1,191
  • 9
  • 17
  • 1
    I would never block the UI thread- show a placeholder value of some sort whilst data is being loaded from your database SQLite operations are usually very fast anyway. – PPartisan May 13 '19 at 07:44
  • @PPartisan : The problem with that is code design. How do you implement that placeholder stuff without "spilling" to the UI code that you're using an asynchronous library "under the hood" (and thus breaching an abstraction barrier) so that say if, in the future, you decide to switch to something other than Room, you won't need to mess with your UI code? – The_Sympathizer May 13 '19 at 07:46
  • Ordinarily I use Room with `RxJava`, so querying the db will return a `Flowable` with a `startWith()`, and into that I'll pass some default value. That said, you don't need Rx to do this - if you use a design pattern like MVP, you can set your default value immediately when your `Presenter` is attached, and update the `View` when your data is ready – PPartisan May 13 '19 at 07:50
  • @PPartisan : So basically, you're suggesting it should have the UI element start up with a default or "uninitialized" state, and then it would suddenly "change" once the stuff gets loaded in? And thus, basically, there _has_ to be at least a _little_ "leakage" of information about the kind of library we're using under the hood, right? – The_Sympathizer May 13 '19 at 07:53
  • Not at all - if you want consistent behaviour regardless of the underlying mechanism (Room, SQLiteOpenHelper, file system, web server, etc.), there's nothing to stop you putting your own abstraction layer on top of Room, and ensuring that whatever implements that layer meets the criteria that (i) data is loaded asynchronously and (ii) it provides a default value. This method of using placeholder values whilst data is loaded asynchronously is very common - you'll see it in the Facebook and Quora apps at a minimum, if you have them, but they're ubiquitous. – PPartisan May 13 '19 at 07:57
  • @PPartisan : So then basically it is more a matter of expanding the design to use another standard, general practice (use of a default value) than necessarily "leaking" information about the underlying libraries, right? – The_Sympathizer May 13 '19 at 09:16
  • Yeah, it's certainly one way to do it - define your own contract and ensure your implementation(s) meet it. – PPartisan May 13 '19 at 09:19
  • @PPartisan : What might be another way, though? – The_Sympathizer May 13 '19 at 09:20
  • I think we're drifting into "too broad" territory if we discuss all possible alternatives. Defining your own contract/abstraction that includes a default value, and providing a default value in your Presenter when using MVP, are two solutions that are perfectly reasonable. – PPartisan May 13 '19 at 11:08

0 Answers0