Building a digital wallet Android app in Kotlin - Session 2

This post is part of a series where I build a digital wallet app for Android in Kotlin. This is a learning exercise suggested to me by my Android mentor. Over the coming months I’ll share my progress and my learnings, in addition to any feedback I received in order to iterate and improve on my code. I hope others can learn from what I learned as part of this experiment.

Previous post in this series: Building a digital wallet Android app in Kotlin: Part 1.

Session 2: Sunday, 16 Feb 2020

The goal today is to get the RecyclerView up and running in the UI. I’ll be using my BNR book as guidance on how to set up code for the adapter and view holder. As agreed with JC, I’ll be doing this with a single activity and multiple fragments (in order to understand the impacts of fragment lifecycle too)

My Dev Results

It took me a little while to get started on this, mainly due to being stuck on where to start. So I decided to draw out a class diagram that separates my Model, Controller & View layers. In my Model layer I just have a number of Account objects. In my Controller layer I have the MainActivity, the Fragments and ViewModels. In the View layer I have my various types of layouts:

Since I haven’t built anything with multiple fragments on the screen at once yet, I feel that my activity_main_layout will have to reference some ‘total of all balances’ TextView up the top, in its own fragment, and then below that I’ll have a RecyclerView in its own fragment. I feel a bit too much that I’m copying boilerplate code in a lot of places from text book guidance; the challenge for me next time will be to create all components of the RecyclerView without a template to follow. For example - in the examples I follow, they create an inner class for the ViewHolder and the Adapter class, all within the fragment itself. I wanted to split this out to separate responsibilities so I split out the ViewHolder into its own class and that worked fine. However, the challenge came when trying to do this for the ViewHolder.Adapter becaause you need to refer to the fragment’s layoutInflater. So should I pass the context of the fragment to the adapter? It doesn’t feel like it makes sense because why does the adapter need to know its caller. But then again, the adapter is specifically for that fragment and only that fragment, so maybe it makes sense to couple these two classes? For now I’ve kept the ViewHolder.Adapter as an inner class of the fragment that uses it.

The end result of today’s session is that I have a dummy list of Account objects being displayed in my RecyclerView. Next, I’ll be wiring up my parsed JSON to the screen. I had more trouble with this now than I expected - my understanding of where to assemble each layer of the app’s tasks is mediocre.

I tried to have my JSON load in the background but then I couldn’t refer to it nested further down into my ViewHolder inside my Adapter inside my fragment, that also requires a context. Obviously it’s bad to pass context to a ViewHolder so that was out. Then I got stuck on where I should pre-load in the background before the UI is updated, not having a good enough understanding of the lifecycle events of fragments it seems that I can only render a blank screen because my JSON wasn’t loaded yet. I need to figure out the best way of the UI updating after the JSON is loaded in the background. I would have thought, like in React, that the UI should update automatically when data is updated, since it’s wrapped in a ViewModel. There is more to learn here even though this is frustrating I feel that it’s a good learning.

Feedback from JC: Tuesday, 18 Feb 2020: Time to refocus

My Android mentor, JC, reviewed my approach and my code completed to date. He gave me the following advice:

Questions asked of me:

  • “What is your core business logic?”
  • “Does your core business logic care about how you “get” the data? Whether it’s Rx, Coroutines, Retrofit, In memory storage…”
  • “Does your core business logic care whether it’s an activity or fragment or a webview you’re presenting the data to?”
  • “It will be overwhelming at first, but try to apply what Matt has talked about about the functional core and imperative shell”

Notes directed at me:

  • Doing the data story first and using WorkManager is very Android-specific. You haven’t nailed down the business logic yet; you could have designed the data objects first without intertwining Android-specific stuff. Focus on one part first - getting the core business logic better. Then you have a proper interface ready. Then it doesn’t matter if it’s WorkManager, Co-routines, etc, the most important thing is how you get the data. Interface in its place.
  • Don’t get caught up with WorkManager etc, that’s just one implementation, could be many. Without implementation details, you can then test that functionality. You don’t even need to know how to run an Android test. It’s functional.
  • If this becomes your study tool, next implementation could implement the data story with many interfaces, one with WorkManager, one with RetroFit, etc. Can try all.
  • Don’t get caught up parsing with JSON, you just need a data object - hard coding for now is fine.
  • The view - the view should display whatever from the core, can just be a TextView for now. Hitting the core should matter more. RecyclerView, adapter, etc comes later as long as core business logic is done first.
  • We aren’t worrying about fragment vs activity, because later, if we want to switch out single-activity-multi-fragment to multi-activity, then we should be able to - the core business logic doesn’t change
  • I’m being overwhelmed now because I’m mashing everything Android (UI) + Non-Android (business logic) code together. Focus on one thing first - if you want to display it first, mock core business logic. Or if you want to focus on business logic, don’t worry about UI for now.
  • Build a good momentum on the core first - Then we can unit test the business logic side without mocking Android stuff.
  • When we’re ready to do UI stuff, can use the BNR book on how to display in different ways, ConstraintLayout, transferring data, etc
  • What is the core business logic? Displaying data based on some kind of trigger (can be anything: button click, lifecycle event, etc). Secondly, if I get an ID, it should give me a list based on that ID. Forget about what needs to be displayed, we’re talking about the data itself.
  • Is it worth starting from scratch again? Maybe

Code Review: Feedback on code completed to date

  • I don’t need Accounts model (which is a list of Account), just make a list of Account
  • Explanation of ‘store’ concept - in memory singleton, gives you accounts = ok, usable
  • Use the Worker later on, don’t need it for now
  • For this to work, suggest making an AccountsStore to make it an interface, and one of the implementations could be an in-memory one
  • inside the ViewModel, good - no use of Android components (eg. context)
  • Testing ViewModel is hard: what does ViewModel actually give you? Need to review this.
  • Do ViewModel’s have state? Because if you store a list of accounts in the ViewModel, then you also store a list of accounts in the Store - it gets tricky, then you have 2 different data sources for the same thing. Which one is the source of truth? How do I determine which one has the freshest data? Need to determine sync mechanism etc. What if store updates but ViewModel doesn’t? Mismatch.
  • ViewModel’s have value but why do we need it as an inherited object? Unless you know how to control its persistence, may be hard.
  • Make a ‘MockAccountStore’ for what my ViewModel’s dummy data is currently doing - that is good as a hardcoded starting point. Maybe the ViewModel just gets the interface.
  • ViewModel then prepares the data for the view, by interacting with the sources.
  • Let’s say the ViewModel gets a request to get all accounts. It’s very specific to this view - make a few methods to expose (not everything on the list) just ‘getAccounts()’ for example, under getAccounts() it could be hitting the inMemoryStore, database, whatever, depends on the implementation.

Advice on handling asyncronous behaviour:

  • Think about delivery - the timing of events. When I make this request, is it instantaneous? If it’s not, how do I communicate with the view to tell it that it’s waiting? Eg. async task to get accounts from the store.
  • In the old days of Android there were async tasks everywhere was painful; makes a return to the service, returns a token, passes a callback to be triggered when the async process is done, removes the spinner or whatever else
  • The hard part: what to do during that waiting period? In the early days, it was done by callbacks. Nowadays, it is done commonly with RX Java (good for iOS too, transferable knowledge, observables etc)
  • Start with callbacks: unlock your knowledge. Have a callback that is triggered by the service; feed that into the ViewModel. That way regardless of whether the service is doing it async or not, ViewModel doesn’t care. Pass in an event, you get an event in return.
  • your getAccounts() shouldn’t expect a list. Otherwise your getAccounts() will be async inside your activity, and then it will be waiting when called. Delivery of the actual list should be in another way. Decouple that. This is a good first step: work on business logic, separate request from response, pass to activity. Once in activity, display any way, eg. TextView, or maybe as a log message.