Contents


Presentations

There are a couple of presentations hosted on surge that I occasionally use, they might be useful for you too. Ironically they don’t work at all on mobile.

They were written using spectacle which is a pretty good ReactJS presentation library.

Android architecture basics

android basics presentation

This spells out the main problem with the default android architecture and the motivation for using libraries like fore in the first place.

Presenter perspective including notes is here

Regular slides without notes is here

(if you open those two links on separate tabs of the same browser, the slides will automatically keep themselves in sync)

fore basics

**fore** basics presentation

This one takes you though all the main points of fore together with a lot of examples. I don’t think it really adds anything that isn’t already in these docs - it’s like a quick summary version.

Presenter perspective including notes is here

Regular slides without notes is here

(Again, if you open those two links on separate tabs of the same browser, the slides will automatically keep themselves in sync)

Coding Exercise

In my day job I sometimes take new developers through the basics of MVO style architectures and I’ve found the most effective way of doing this (aside from pointing them in the direction of these docs) is to get them to do a basic two page android app. I give them as much time as they need as the focus should be on learning and understanding rather than producing code. They typically take between a few days and a week, although once they know what they are doing they could easily complete the whole thing in an hour or so.

What I find truly fascinating is that the feedback I give them once they have finished, tends to be around 75% identical. I could almost copy and paste my feedback from the last developer and just change the lines of code that I am referring to.

As the mistakes made are all so similar, I plan to add an equivalent exercise here together with the standard feedback, so that the reader can use it themselves, or with their team.

Stay tuned….

State versus Events

This is quite subtle but the issue presents itself in many different architectures, so I think it’s worth saying a few things about it. You can choose to treat any of your applications data as state or an event. The choice you make will effect how straight forward it is to handle that data, and how clear your resulting code is.

Let’s take the example of a network error.

If you choose to treat the network error as state, then in MVO style, somewhere you will have a getter in a model that exposes this error state, maybe it returns ERROR_NETWORK. It will return this ERROR_NETWORK object via the getter until the model changes (perhaps when you make another network call: that error state will be cleared, the observers notified, and the model’s getter will now return a ERROR_NONE object when syncView() is next run). Similarly in MVI style, the ViewState will have an error field that will be ERROR_NETWORK and then after the error state has been cleared, the field will be ERROR_NONE in the next render() pass.

Now let’s think about the UI that might represent that error. Maybe when you are in the error state, you want a warning icon to display. Now let’s say we rotate the screen (it’s often helpful to think about what would happen during a screen rotation because it can be representative of a lot of other situations). After a rotation you still want to see the warning icon, because that’s the current state, and you never want your view to lie. Other things can cause the view to re-sync itself and likewise you don’t want that warning icon to disappear just because of a syncView() / render() call. The only time you want that warning icon to not be visible, is when the error state has actually been reset to ERROR_NONE by some logic processing away from the view layer.

Looks like choosing to store our error as state was the right move here.

Now let’s consider another UI style, one where we display a temporary toast message or a snackbar when we encounter an error. That’s a pretty common way of handling network errors. When the syncView() or render() method is called we notice the presence of ERROR_NETWORK and we show a toast message accordingly. How about when we rotate the screen? Well when the view is re-synced with the state of the app we will show that toast again, in fact anything that causes the view to be re drawn will cause that toast to appear again - multiple toasts for the same error is definitely not what we want. It’s not the end of the world, there are a number of ways to handle this, in fore you would use a syncTrigger that bridges the two worlds of state and events, letting you fire one off events only as a result of a state change. But anyway, for this style of UI maybe we chose the wrong way of representing our error here. By treating our error as an event rather than a state of our view, we can just use a callback to fire a toast message and our code will likely end up a lot simpler.

After all, a network error relates to a single point in time, if we loose it on rotation does it really matter? maybe it does, maybe it doesn’t - maybe you want to treat everything as state just for consistency. That’s where you need to make a decision about state versus event.

This comes up a lot with displaying menus, popups, errors and running animations. There is a little more on that here: When should I use an Observer, when should I use a callback listener?

Basic model tutorial

Writing model code gets easier with practice but as a starting point you could do a lot worse than to start by modelling real world concepts. That should also make it easier for other developers to understand what you are aiming for.

For example if you have a printer attached to your android app that you need to use, you probably want a Printer class for a model.

(In fore, most of the models end up having global scope, if you have a model that you want to restrict the scope of, you can use a Factory class to get a local instance, or use a library like Dagger. Remember if you call “new” in a View layer class, you won’t be able to mock that object out for tests later, see here for more info)

In this case it makes sense to give our Printer model global application scope because a) the real printer is right there by your application ready for printing no matter what part of the app you are in and b) it’s easy and clear to do.

There will also only be one instance of the Printer model, because: there is only one real printer. (This model is written in pseudo Java, writing one in Kotlin makes no difference of course.)


public class Printer {
}
 

class Printer {
}
 

The Printer model might have a boolean that says if it’s busy printing or not. It might have a boolean that says that is has run out of paper. It might have a number that tells you how many items it has in the queue at the moment. All of this state should be available via getters for any View or other Observer code to access.


public class Printer {

    private boolean isBusy = false;
    private boolean hasPaper = true;
    private int thingsLeftToPrint;

    public boolean isBusy() {
        return isBusy;
    }

    public boolean isHasPaper() {
        return hasPaper;
    }

    public int getThingsLeftToPrint() {
        return thingsLeftToPrint;
    }
}
 

class Printer {

  var isBusy = false
    private set
  var hasPaper = true
    private set
  var thingsLeftToPrint = 0
    private set

}
 

The Printer class will have some public methods like print(Page pageToPrint) for example, and as this will take a while, that call will need to be asynchronous and that means you should probably have a listener callback that will get called when it is finished:


public class Printer {

    private boolean isBusy = false;
    private boolean hasPaper = true;
    private int thingsLeftToPrint;

    public void printThis(Page pageToPrint, CompleteCallBack completeCallBack){

        isBusy = true;
        numPagesLeftToPrint++;

        //...do the printing asynchronously, then once back on the UI thread:

        isBusy = false;
        numPagesLeftToPrint--;

        completionCallBack.complete();
    }

    public boolean isBusy() {
        return isBusy;
    }

    public boolean isHasPaper() {
        return hasPaper;
    }

    public int getThingsLeftToPrint() {
        return thingsLeftToPrint;
    }
}
 

class Printer {

  var isBusy = false
    private set
  var hasPaper = true
    private set
  var thingsLeftToPrint = 0
    private set

  fun printThis(pageToPrint: Page, completeCallBack: CompleteCallBack) {

      isBusy = true
      numPagesLeftToPrint++

      //...do the printing asynchronously, then once back on the UI thread:

      isBusy = false
      numPagesLeftToPrint--

      completionCallBack.complete()
  }

}
 

The Printer model will need USB connection stuff and maybe a Formatter that will let you format your page appropriately for the type of printer you have (or something). We’ll add these dependencies as constructor arguments, and we are going to deliberately crash if someone mistakenly tries to send us null values here (nulls will never work here so we may as well crash immediately and obviously). Annotating parameters to not be null is not really enough because it’s only a compile time check and can still let things slip through.


private final USBStuff usbStuff;
private final Formatter formatter;

public Printer(USBStuff usbStuff, Formatter formatter) {
    this.usbStuff = Affirm.notNull(usbStuff);
    this.formatter = Affirm.notNull(formatter);
}
 

class Printer constructor(val usbStuff: UsbStuff, val formatter: Formatter) {
  ...
}
 

We’re nearly there. If we want to involve this Printer model in the view layer we will probably want it to be Observable so that any observing view will be notified whenever it changes (and therefore the view needs to be refreshed).

The quickest way to do that is to extend ObservableImp:


public class Printer extends ObservableImp {

    public Printer(WorkMode workMode) {
        super(workMode);
    }

    ...
}
 

class Printer(notificationMode: WorkMode) : ObservableImp(notificationMode) {
  ...
}
 

Next we need to make sure that the observers are notified each time the Printer model’s state changes, and we do that by calling notifyObservers() whenever that happens:


isBusy = true;
numPagesLeftToPrint++;
notifyObservers();  //fore Observable will take care of the rest
 

isBusy = true
numPagesLeftToPrint++
notifyObservers()  //fore Observable will take care of the rest
 

The asynchronous printing that we’ve glossed over so far could be implemented with an AsyncBuilder like so:


new AsyncBuilder<Void, Void>(workMode)
    .doInBackground(input -> {

        //...do the printing

        return null;
    })
    .onPostExecute(result -> {

        //back on the UI thread

        isBusy = false;
        numPagesLeftToPrint--;
        notifyObservers();

        completeCallBack.complete();
    })
    .execute((Void) null);
 

AsyncBuilder<Unit, Unit>(workMode)
    .doInBackground {

        //...do the printing

    }
    .onPostExecute {

        //back on the UI thread

        isBusy = false
        numPagesLeftToPrint--
        notifyObservers()

        completeCallBack.complete()
    }
    .execute()
 

Here’s what we might end up with for a rough Printer model:


public class Printer extends ObservableImp {

    private final USBStuff usbStuff;
    private final Formatter formatter;
    private final WorkMode workMode;

    private boolean isBusy = false;
    private boolean hasPaper = true;
    private int numPagesLeftToPrint;

    public Printer(USBStuff usbStuff, Formatter formatter, WorkMode workMode) {
        super(workMode);
        this.usbStuff = Affirm.notNull(usbStuff);
        this.formatter = Affirm.notNull(formatter);
        this.workMode = Affirm.notNull(workMode);
    }

    public void printThis(Page pageToPrint, final CompleteCallBack completeCallBack) {

        if (isBusy){
            completeCallBack.fail();
            return;
        }

        isBusy = true;
        numPagesLeftToPrint++;
        notifyObservers();

        new AsyncBuilder<Void, Void>(workMode)
                .doInBackground(input -> {

                    //...do the printing

                    return null;
                })
                .onPostExecute(result -> {

                    //back on the UI thread

                    isBusy = false;
                    numPagesLeftToPrint--;
                    notifyObservers();

                    completeCallBack.complete();
                })
                .execute((Void) null);

    }

    public boolean isBusy() {
        return isBusy;
    }

    public boolean isHasPaper() {
        return hasPaper;
    }

    public int getNumPagesLeftToPrint() {
        return numPagesLeftToPrint;
    }
}

class Printer(
  val usbStuff: USBStuff,
  val formatter: Formatter,
  val workMode: WorkMode) : ObservableImp(workMode) {

    var isBusy = false
        private set
    val isHasPaper = true
    var numPagesLeftToPrint: Int = 0
        private set

    fun printThis(pageToPrint: Page, completeCallBack: CompleteCallBack) {

        if (isBusy) {
            completeCallBack.fail()
            return
        }

        isBusy = true
        numPagesLeftToPrint++
        notifyObservers()

        AsyncBuilder<Unit, Unit>(workMode)
            .doInBackground {
              //do the printing
            }
            .onPostExecute { result ->

                //back on the UI thread

                isBusy = false
                numPagesLeftToPrint--
                notifyObservers()

                completeCallBack.complete()
            }
            .execute()
    }
}
 

Obviously that doesn’t work yet, we’ve ignored numPagesLeftToPrint and the printing details, but you get the idea.

There is something important that snuck in to that version though: The WorkMode parameter tells the Observable implementation ObservableImp how you want your notifications to be sent, it’s also being used by the AsyncBuilder. Usually you will pass WorkMode.ASYNCHRONOUS here.

When you construct this Printer model for a test though, along with mocking the USBStuff, you will pass in WorkMode.SYNCHRONOUS as the constructor argument. SYNCHRONOUS will have the effect of making all the asynchronous code run in sequence so that testing is super easy.

Take a look at how the CounterWithLambdas model in sample app 2 is tested for example.

Take a look at the Model Check List to make sure you have everything down, and then maybe head over to the Data Binding section where we tie it all together with the view layer.

Android’s Original Mistake

Separating view code from everything else is widely considered a good thing, but despite that agreement, it’s rare to come across an android app that actually does that.

Unfortunately, right from its inception the Android platform was developed with almost no consideration for data binding or for a separation between view code and testable business logic, and that legacy remains to this day.

Instead of separating things horizontally in layers with views in one layer and data in another layer, the Android designers separated things vertically. Each self contained Activity (incorporating UI, data and logic) wrapped up in its own little reusable component. That’s also probably why testing was such an afterthought for years with Android - if you architect your apps like this, testing them becomes extremely difficult.

Android seems to have been envisioned a little like this:

vertical separation

A more standard way of looking at UI frameworks would have been to do something like this:

horizontal separation

I know, crap diagrams, but anyway a lot of the complication of Android development comes from treating the Activity class as some kind of reusable modular component and not as a thin view layer (which is what it really is). Hacks like onSaveInstanceState() etc, are the result of fundamentally missing the basic requirement of (all?) UI platforms: the need to separate the view layer from everything else.

Despite the obvious problems of writing networking code or asynchronous code inside an ephemeral view layer, think about how many Android apps you’ve encountered that fill their Activity and Fragment classes with exactly that. And think about how much additional code is then required to deal with a simple screen rotation (or worse, how many apps simply disable screen rotation because of the extra headache). Sometimes even smart developers can fail to see the wood for all the trees.

Fortunately it’s almost all completely unecessary. The sample apps should clearly demonstrate just how clean android code can become once you start properly separating view code from everything else.

Troubleshooting / How to Smash Code Reviews

Android apps that are written using fore have a certain look to them code-wise, the code in these docs and the sample apps looks very similar. This really helps when performing code reviews because structural errors tend to jump out at you a little more. The first part of this post explains the concept better than I could, and I’d recommend you give it a quick read.

Typical characteristics of an app built with fore

Given any app that is attempting to implement fore: first check the package structure, then investigate one of the activities and/or fragments to check it’s as small as it can be. Next take a look at a View class to see if you recognise the flow mentioned above. Check the databinding especially i.e. is an observer being added and removed, how does the syncView method look. Look out for any ui state being set outside of the syncView method. It should take seconds to establish if the project is approximately correct and has a chance of the UI remaining consistent, handling rotations and not having memory leaks. Further to that here is a list of specific warning signs that will highlight potentially incorrect code (this list is especially helpful for code reviews - these are all things I have seen professional developers do).

1) Any code which is setting or updating view states that is not inside the syncView() method. Example: “clickListener -> setDisabled”. That’s a very common pattern in MVP (and it makes handling rotation hard), in MVO it’s usually an indication that the developer might not understand why syncView() is designed like it is, and will almost certainly result in hard to identify UI consistency bugs when screens are rotated etc. Point them to the reactive ui section where it talks about syncView().

2) Activities and Fragments that have more than a few lines of code in them. Sometimes there are good reasons for putting code in Activities and Fragments, setting up bundles and intents for example, but you should be immediately suspicious of any errant code that gets into these classes. Often this code can be moved to a model class, safely away from tricky lifecycle issues and where it can also be more easily tested. (If you value your sanity and that of your team, you should make sure that there are absolutely no AsyncTask instances or networking code in any Activity or Fragment classes at all. Ever.)

3) Code in a Fragment that casts it’s parent activity and then calls that activity for further processing. Again sometimes that is appropriate, but unfortunately it’s a very common pattern that is often misused. The idea of course is to let a Fragment communicate with an Activity in a safe way. Generally if you have two ephemeral view layer components talking to each other, it’s going to cause problems. When this technique is used as a way to access functionality written in the parent activity which should really have been written in a model class in the first place, it just acts as a sticking plaster for a problem that should never have existed anyway. The answer of course is to put that code in a model, inject that model into the fragment and let the fragment access it directly, that totally removes the dependence on the host Activity and removes a lot of boiler plate in the process.

4) Adding or removing observers outside of android lifecycle methods. I’m not saying there is never a good reason to do that (particularly if you want to set up one long living model to observe the state of another long living model). But it is a bit unusual and might warrant a rethink. It’s usually a mistake (and a cause of memory leaks).

5) Wherever you see an addObserver() it’s always worth checking that you can see the associated removeObserver() call in the mirrored lifecycle method (eg. onStart()/onStop() or onResume()/onPause()) to make sure references are being cleaned up and memory isn’t being leaked.

6) Any change of state in an observable model that doesn’t end with a call to notifyObservers(). Even if it’s not necessary for the current implementation, by not notifying the observers here, we now have a model that only works in certain (undocumented) circumstances. If someone else comes along and wants to observe your model and does not get a notification as expected when some state changes, something will break.

7) Any getter method that does more than pass back an in-memory copy of the data asked for. In order for the databinding to be performant, we want any getter methods to return fairly quickly. Try to front load any processing rather than doing it in the getters.

8) Any observers that do anything other than sync their entire view are usually (but not always) incorrect. Generally the observer just does one thing (sync the view), and this means you can use the same instance of that observer to register with several different models in the same view.

9) Code or tests that makes assumptions about the number of times syncView() or somethingChanged() will be called This is pretty important, you can’t simply fire one off events based on syncView() being called (like starting an activity for example) - because you are depending on being notified by the model an exact number of times. Code like that can easily break in two ways:

The deal is that whenever something (anything) changes in a model, you will be notified. But you maybe be notified more than you expect. In order to be robust, your syncView() must make no assumptions about the number of times it may or may not be called. Sometimes you will of course need to bridge the world of syncing views and triggering one off events, and the way you do that in fore is to use SyncTrigger. Also take a look at this in case what you really want is a callback anyway.

10) A syncView() that is more than 5-10 lines long or so and/or doesn’t have one line to set an affirmative value for each property of each UI element you are concerned with. Take a look at how to write a good syncView() method under the data binding section.

11) Any click listeners or text change listeners should generally be talking directly to model classes, or asking for navigation operations for example: MyActivity.startMe(getContext()). Occasionally it’s useful for listeners to directly call syncView() to refresh the view (when an edit text field has changed for example). What they generally shouldn’t be doing is accessing other view components like fragments or activities and checking their state in some way. Sometimes if you follow this code it ends up calling a model class somewhere down the line anyway, in which case the model class should just be called directly (you get your model references to any view class using dependency injection)

12) Public methods on models that return their state directly through a callback, and therefore shortcut the MVO pattern. Depending on what data is returned here, this may make it difficult to cleanly support rotation. It might be worth re-reading the MVO architecture page and the reactive UIs page.

13) Any state kept in view layer classes is at risk of being lost. How does this view survive rotation, would loosing that state matter? if yes, then it might be better kept inside a model, away from the view layer. Take a glance at the state versus events discussion.

14) Any logic kept in view layer classes is usually harder to test. It can be hard to totally remove all the logic from the view layer (especially navigational logic once you factor in the back button) but be aware that the logic here is usually a lot harder to test and if you can move it away from the view layer reasonably easily, then you probably should. If there is some particularly complicated logic for a view state in the syncView() method for example, that logic is a prime candidate to be moved out of the view layer into a model or utility class where it can be tested more easily.

15) Having a syncView() method, but not calling it syncView(). This specific method is talked about a lot and it’s very handy to call it the same thing so that everyone knows what everyone else is talking about. Making your View implement SyncableView is probably a good idea anyway, it’s also a requirement to use the optional lifecycle classes.

Dependency Injection Basics

Dependency Injection is pretty important, without it I’m not sure how you could write a properly tested Android app. But it’s not actually that complicated.

All it really means is instead of instantiating the things that you need to use (dependencies) inside of the class that you’re currently in, pass them to your class instead (either via the constructor or some other method if the constructor is not available such as with the Android UI classes).

Don’t do this:


public MessageSender() {
    networkAccess = new NetworkAccess();
}
 

fun MessageSender() {
    networkAccess = NetworkAccess()
}
 

Do this instead


public MessageSender(NetworkAccess networkAccess) {
    this.networkAccess = networkAccess;
}
 

fun MessageSender(networkAccess: NetworkAccess) {
    this.networkAccess = networkAccess
}
 

If you don’t have access to the constructor, you can do this (like we do in a lot of the fore sample apps):


MessageSender messageSender;

protected void onFinishInflate() {
    super.onFinishInflate();

    messageSender = CustomApp.get(MessageSender.class);
}
 

private lateinit var messageSender: MessageSender

override fun onFinishInflate() {
    super.onFinishInflate()

    messageSender = CustomApp.get(MessageSender::class.java)
}
 

In a commercial app, the number of dependencies you need to keep track of can sometimes get pretty large, so some people use a library like Dagger2 to manage this:


@Inject MessageSender messageSender;

protected void onFinishInflate() {
    super.onFinishInflate();

    DaggerComponent.inject(this);
}
 

private lateinit var messageSender: MessageSender

override fun onFinishInflate() {
    super.onFinishInflate()

    DaggerComponent.inject(this)
}
 

The main reason for all of this is that dependency injection enables you to swap out that NetworkAccess dependency (or swap out MessageSender) in different situations.

Maybe you want to swap in a mock NetworkAccess for a test so that it doesn’t actually connect to the network when you run the test. If NetworkAccess is an interface, dependency injection would let you replace the entire implementation with another one without having to alter the rest of your code.

A quick way to check how your Java code is doing on this front is to look for the keyword new (it’s slightly less obvious in Kotlin as there is no new keyword). If you are instantiating an object, then that is a dependency that won’t be able to be swapped or mocked out at a later date (which may be fine, as long as you are aware of it).

Incidentally don’t let anyone tell you that you must use a dependency injection framework in your android app. In the fore sample apps, all the dependencies are managed in the ObjectGraph class and managing even 100 dependencies in there is no big deal (if you have an app with more than 100 global scope dependencies then you’re probably doing something wrong anyway). Keeping them all in the same place also lets you see what your dependency graph actually looks like at a glance. So if you and your team dig dagger, then use it. But if you spent a few days stabbing yourself in the eye with it instead - feel free to manage those dependencies yourself. See here for more on this

Inversion of Control

This term really confused me when I first heard it years ago, so here’s my take in case it’s helpful for you.

Imagine if we have a company. It has a CEO at the top. Underneath the CEO are departments like Marketing, HR, Finance. Those departments all print documents using a printer.

The CEO is in control of the whole lot, whatever she says goes. But if you took that to extremes it would be ridiculous. The CEO would tell the departments exactly what documents to print, but also with what paper, and what printer ink. When paper tray 3 ran out, the CEO would be the one to decide to switch to using paper tray 2 instead, or to display an error on the printer display. After 5 minutes of no printing, the CEO would decide to put the printer into power saving mode. You get the idea. Don’t write software like that.

Inversion of control means turning that control on its head and giving it to the lower parts of the system. Who decides when to enter power saving mode on the printer? the printer does, it has control. And the printer wasn’t manufactured in the office, it was made elsewhere and “injected” into the office by being delivered. Sometimes it gets swapped out for a newer model that prints more reliably. Write software like that.

Global vs Local scope dependencies and rotation

Most of the models used in fore (the dependencies in this case) tend to be global in scope i.e. they exist throughout the lifetime of the app. A good example would be an AccountModel with methods such as login(), logout(), isLoggedIn(), isCurrentlyPerformingNetworkAccess(), getUserName() etc. This means they will maintain any state they have (such as whether they are currently performing network access or not) independent of anything that is happening in the view layer.

This of course means that when a view is rotated, it makes no difference to the model.

[We’re not talking about data pojos here btw, if your view is just being driven by some data that has a fixed state, you can use techniques like fragments.setArguments(bundle), and rotation will work just fine.]

For locally scoped models however (regardless if you are using Pure DI or Dagger) the actual scope of the model is usually tied to a class in the UI layer (Activity class, or your custom View class). On rotation, these View layer classes disappear and the locally scoped model reference (and the state it holds) disappears with it.

The solution is to extend the lifecycle of these models so that they survive beyond rotation. (If you are using Dagger2, it is your locally scoped Component class that you need to keep a reference to). You can do that by putting your models in something like Google’s ViewModel, or use a home grown solution for example: storing a reference to them statically, or storing them in something attached to the application class. Deciding when exactly to null out the references to those locally scoped models is highly app specific.

Frequently Occasionally Asked Questions

1) Why not put a parameter in the Observer.somethingChanged() method?

If I had a dollar for everyone who asked me this question! (I would have, about $4)

There is a very good reason why we don’t have a parameter here, but it is complicated, so stay with me. We could use a generic maybe and let a model send data or a message directly to the observers. Sending data like that might at first seem like an easy and convenient thing to do, but in my experience it basically always ends up in a world of maintenance pain.

Adding a parameter here would let client code use the observer like some kind of messenger thing or an event bus. While that could be a perfectly valid thing to do for the specific situation you find yourself in, when it comes to binding data to an android view layer it almost always ends up destroying the long term maintainability of the code base.

(Adding a parameter here has been tried by yours truly in many different projects over the years by the way, it always ends up being removed resulting in a considerably cleaner code base, so this has been the fore approach now for a number of years and it seems to work very well).

One reason (but not the only one) is that often, different views or other Observers will want different things from the same model and as the code evolves, that model slowly ends up having to support many different flavoured observables all with different parameter requirements.

Similarly there will often be views or other Observables that are interested in more than one model, and if those models all have different observable interfaces, all those interfaces will need to be implemented and managed by the view, rather than just using a single Observer implementation.

It balloons the amount of code that needs to be written. It also leads developers down the wrong path regarding data binding and ensuring consistency when your application is rotated etc (see more on that in the Reactive UIs section).

[Quick example, this view is driven by these two models. If each model had a different parameter in its somethingChanged() method, the view would need to implement two different observer callbacks - now what if the view was interested in 5 different models? (which would not be a problem at all for MVO by the way): The view would need to implement and manage a whole bunch of different observers, or you would need a super model that wrapped all that and presented one observable interface to the view, i.e. extra code, extra tests, no benefit]

Passing a parameter here is also the “obvious” thing to do - which means, if it’s an option, it will always be chosen by the less experienced developers in the team. Apart from giving you code review headaches, letting a new developer do that would prevent that developer from learning the more powerful way to use MVO - which, although extremely simple, can take a while to get your head around.

This is one case where fore is stopping you from making an easy but horrible architectural mistake. The library is as valuable for what you can’t do with it, as it is for what you can do with it.

“This library is as valuable for what you can’t do with it, as it is for what you can do with it.”

Try to get comfortable using these observers to just notify observing client code of any (unspecified) changes to the model’s state (once the observing client code has been told there are changes, it can use fast returning getters on the model to find out what actually happened, redraw it’s state, or whatever - if this isn’t straight forward then the models you have implemented probably need to be refactored slightly, check the observer vs callback discussion first). For some, this is a strange way to develop, but once you’ve done it a few times and you understand it, the resulting code is rock solid and very compact.

2) When should I use an Observer, when should I use a callback listener?

The observer / notify pattern is not always going to be suitable for what you want to do. In particular, if you are looking to receive a one off success/fail result from a model as a direct result of the model performing some operation (like a network request) then a regular callback may serve you better. In this case the success or failure of the network call does not alter any fundamental state of the model, so a callback / listener is ideal. More about how to treat state here.

for example:


model.doStuffOnAThread(new ResultListener{
    @Override
    public void success(){
        //do next thing
    }
    @Override
    public void fail(UserMessage reason){
        showMessage(reason);
    }
});
 

model.doStuffOnAThread(object : ResultListener {
    override fun success() {
      //do next thing
    }
    override fun fail(reason: UserMessage) {
      showMessage(reason)
    }
})
 

You can use both patterns in the same model with no problem of course, in the example above the model could have a busy state that changes from false to true and back to false again during a network request so that view code can redraw itself as showing a busy swirly if appropriate. That would be better managed using an observer pattern as follows:


public void doStuffOnAThread(final ResultListener resultListener){

    busy = true;
    notifyObservers();

    startAsyncOperation(new FinishedListener(){
        @Override
        public void finished(){
            busy = false;
            resultListener.success();
            notifyObservers();
        }
    });
}
 

fun doStuffOnAThread(resultListener: ResultListener) {

    busy = true
    notifyObservers()

    startAsyncOperation(object : FinishedListener {
        override fun finished() {
            busy = false
            resultListener.success()
            notifyObservers()
        }
    })
}
 

For a real example of both techniques, take a look at the FruitFetcher.fetchFruits() method in the retrofit example app. Notice how it fetches some fruit definitions, which does change the state of the model and therefore results in a call to the notifyObservers(). But the success or failure of the result is temporary and does not form part of the state of the FruitFetcher model, so that is just reported via a call back and the model forgets about it.

For consistency, and for the same reasons outlined above, try to strongly resist the urge to respond directly with the data that was fetched via this listener. i.e. callback.success(latestFruit). It’s tempting, and it will even work, but it breaks the whole point of MVO and it will lead other more inexperienced developers down the wrong path when they are trying to use your model - why would they bother to implement syncView() properly in their view, if they can just take a short cut here (hint: they won’t). And then without anyone noticing, they will loose all the benefits of reactive UIs, see syncView() for a refresher.

3) Syncing the whole view feels wasteful, I’m just going to update the UI components that have changed for efficiency reasons.

This seems to be the reaction of about 20% of the developers that come across this pattern for the first time. I think it might depend on what style of development experience they have have had in the past (it obviously won’t be a problem for anyone coming from MVI for example).

The first thing to bare in mind is of course: “premature optimisation is the route of all evil” or however that quote goes.

The second thing is to make absolutely sure there is a complete understanding of the section on syncView(), particularly the deliberate bug that shows how doing ad-hoc updates can go wrong.

Everything in computing is a trade off, and when considering a trade off you need to understand two things: the upsides (in this case: the lure of only updating the parts of the view that need updating) and the downsides (in this case: loosing the ability to support rotations by default, and increasing the risk of UI consistency issues as discussed in the syncView() link above).

Making a tradeoff when you don’t fully appreciate one of those sides (up or down) is obviously not a great place to be.

If after appreciating the downsides of this tradeoff, there is still an interest in sacrificing that robustness in the name of “performance” or “battery life”, then read on.

This is not usually a problem for any developer that has written game loops, or implemented their own animations using easing equations or similar, but if you’ve never done that type of development, you might be in danger of seriously underestimating how fast even the most basic android phone runs.

In to the matrix

Before we go any further, if you haven’t already, go to developer settings on android and check out the debug tools that let you see the screen updates as they happen.

WARNING if you’re epileptic, maybe skip this part, you will see some incredibly annoying rapid screen flashing as the screen is updated multiple times a second. I’m not epileptic but a few minutes of that makes me feel seriously car sick.

The first one is “Show surface updates” it flashes when part of the screen is being redrawn. You might be surprised just how often the screen is being updated as you use your android device.

The second option you have is “Show GPU view updates” this only shows GPU updates and depending on your device you may see this working a lot, or not at all.

Now that you’ve peeked a little under the hood, you’ll be able to appreciate that if you’re looking at a single waiting animation (like a standard indeterminate progress bar on Android), the screen (or at least that part of it) will be updating the UI around 30 times a second in response to a ui widget that is continually recalculating its state, also 30 times a second. Any scrolling of a list view; any background blurring animation; even a blinking cursor will sometimes cause the screen to be redrawn 30 times a second or so. That’s how fast it needs to be to trick human eyes into thinking something is moving when it isn’t - you’re just seeing a sequence of still images.

If you put some logs in the syncView() method you’ll also see that it is in fact hardly called at all most of the time, unless you are using the Observables to run an animation loop (which is something that you absolutely can do given the performance of the observer implementation in this library by the way).

The syncView() also completes pretty quickly as all of your getters should be returning fast anyway, as recommended here.

In addition, if you are setting a value on a UI element that is the same as the value it already has, it would be a bug in the android framework if it caused a complete re-layout in response anyway (I’m not guaranteeing such bugs don’t exist, in fact EditText does something slightly annoying: it calls afterTextChanged() even when the text is identical to what it had before, but it’s easy to work around. In any case, if you ever did get any kind of performance issues here, that’s the time to measure and see what is happening, not before). If you follow the guidelines here correctly you will almost certainly encounter no problems at all, and that includes running on very cheap, low end devices. What you do get however is unparalleled robustness and clarity of code - which, because logic mistakes become fewer under those circumstances, sometimes results in even more performant code.

If you have a model that is changing in some way that an observer just so happens NOT be interested in, you will end up making a pass through syncView() unnecessarily (but still not actually redrawing the screen): chillax and be happy with the knowledge that your UI is definitely consistent ;)