Think differently (about your tools)
(Crosspost from medium.com/@kpgalligan)
A couple weeks ago we posted the first Doppl story. Since then we’ve been steadily plugging away on a few things.
The team is working on a better sample app. The droidcon app is OK, but complex and kind of weird. I wanted something that’s pretty simple to understand from the code. That should be out next week (ish).
Note: If you don’t want to read and just want to watch me ramble on and demo, see video.
The last version of the doppl gradle plugin needed to be in a separate jar module. This makes logical sense, kind of, but it can make things more difficult. There seems to be a trend of libraries that were formerly a “jar” are now an “Android Library”. That also makes sense, but then everything has to be an “android” thing. You can’t really have a jar module depend on an android library project (AFAIK). So, it feels more complex in the sense that you can’t just make a “Java” thing. Boo hoo. Anyway, just keep moving forward.
The simple solution is to integrate the doppl plugin into an Android context, so you can use it either as a jar module or inside an android module. In retrospect, this is the obvious and practical solution for almost all projects. You don’t need to move everything to a different module. You should move the stuff you’re going to want to transform into a separate package, as you do need to tell j2objc what it should transform and what it should leave alone. You’re going to need to pull your logic out of your view code anyway, so its not a huge deal (IMHO).
The doppl concept is to implement a significant portion of the Android non-UI stack on iOS. Under the hood, they are both Unix-like mobile devices with a flash drive and (sometimes) network access, so using a common code stack works (un)surprisingly well. Part of that stack is sqlite. Both Android and iOS ship with a system sqlite lib. Android has a management layer built on top that has many wonderful libraries built for it. SQLDelight is nice because it tries to provide efficiency while also staying mostly out of your way. I’m not sure if it’ll wind up being our top choice going forward, but its a strong contender, and anything we’re “officially” using needs to work with doppl, so now it does.
You can see it in action by cloning the fork and running ‘sqldelight-ios’.
There are three forks. The first just moves the Cursor into a presenter, but everything still happens in the main thread.
“RxThreading” creates a large volume of data when the app first launches. I think you wind up with (roughly) 1mil rows of data between the tables. This data load happens on background thread, as do queries, but row loads are still main thread.
“RxCaching” attempts to do some forward-looking data loads for the adapter/UITableViewDataSource. For each immediate row request, it’ll attempt to kick off data loads for a number of rows around it. If those are already in progress, it’ll skip. The data loads are done on a background thread, and “held” with a CachingObservable. If you’re trying to get data for a current row, it’ll actually block until that data load is done, but in theory, as you’re scrolling, the system will have already returned that data. Since the data is immediately available, it should just return.
So, essentially, a collection of CachingObservable<SomeData>, keyed by row index. If you ask the adapter for row ‘3’, it’ll check for rows 10 up and down, working out from ‘3’, to see if we already have an Observable for that row (which means its loading or loaded). For our row, it’ll then block till the Observable is ready. The trick is, if the data is already loaded, this should just return. As you’re scrolling, the data loads are batched out in front of the view, so in general the data should be ready by the time you get there.
Anyway, I wouldn’t call this so much “Production ready” as “Fun with Rx”, but it was fun. However, pro tips:
- Android cursors are not thread safe
- Schedulers.io uses multiple threads.
Those can seriously damage your weekend if you’re not careful.
Thanks to Jeff Namnum.