State saving in Android MVP with Kotlin delegates

Roman Savin
Blacklane Engineering
3 min readJan 25, 2018

Now you’re probably thinking, “Oh no, not another blog post about MVWhatever. I hoped we left it in 2k17”. Yes, but with Kotlin everything is getting better, so let’s discuss an old topic again.

TL;DR

So, you have a state which has to be stored somewhere. There are different approaches and opinions. It’s up to you to choose the best solution for your specific problem.

In our project we have in memory cache for loaded data and presenters use it via repositories. So usually we don’t need to store anything inside a presenter which is very convenient and easily testable.

But there are cases when we need to keep a state which is used only for presentation logic. It could be too dumb and small to store it in the domain layer or somewhere else in the application scope.

We’d like to have it in the presenter because it’s presenter’s responsibility to deal with presentation logic. But we live in Android and we have to use Bundle to store the state during activity recreation which is not good because we don’t want to have framework specific classes or callbacks inside our presenters. Therefore for such cases we used stateful presenters in our project.

With this approach presenters have a method like getState() which is invoked from onSaveInstanceState() of an Activity or a Fragment.

It has some disadvantages:

  1. If we use Icepick, the View has magic ugly properties which are used only to initialize presenter.
  2. A stateful presenter has to be initialized with a state by a View. The state is often null during the first launch so we have to implement additional if-else branch in the presenter’s init() method.
  3. The View knows about the state. It becomes smarter and has more than just rendering logic. It’s difficult to cover with unit tests.

Kotlin Delegates

The following approach was inspired by this blog post (in Russian)

Let’s use the delegated properties feature to solve our problem.

We created the basic delegate class which implements ReadWriteProperty interface and stores the value of the delegated property in the stateBundle. It’ll be explained later why we use lazy initialisation.

We need to create two different implementations of the ReadWriteProperty.getValue() method for nullable and not null types.

The only difference is the base type of the generic.

Let’s create a class which provides the delegates in a convenient way and can be injected to our presenters.

Now we need to apply it for our MVP architecture. First of all we need an interface for a stateful View.

Then add some logic to the BaseActivity. It’s not much more than we usually add using Icepick.

Note that the BaseActivity has stateBundle but doesn’t implement ViewWithState interface. It allows us to extend BaseActivity either with stateful or stateless views.

Now let’s implement MVP components.

  1. In our example the view property is lateinit in the BasePresenter. It means that the delegated property count is initialized before we get the view (during the CounterPresenter creation). That’s the reason why we used lazy initialisation of the stateBundle in the InstanceStateProvider class and why the methods of the InstanceStateDelegates get the viewProvider block and not the view itself.
  2. Inject instanceState with Dagger.
  3. BOOM! Now we have a state inside the presenter. It’s stored in a bundle inside the delegate but the presenter knows nothing about framework specific implementation details.

And that’s how our View looks:

CounterActivity has no state saving specific code and that’s great.

So what do we have?

  1. We got rid of state in the View layer.
  2. We keep the presentation state in the presenter and we don’t have Android specific classes there.
  3. The state specific code in the presenter is clean and null safe.

Bonus: Testing

We inject InstanceStateDelegates object into presenters so it’s easy to mock and write unit tests:

--

--