Sunday, February 19, 2017

Patch Sunday

Android stuff has finally hit production and should be trickling out over the next couple of hours.

Update and have fun!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my FaceBook Page
Follow my Google+ Page
=========================

Saturday, February 18, 2017

Testing Where

Testing testing testing.
Everybody always wants to have tests.
Would it surprise you to know that I have almost no tests? Of course it wouldn't.
But lets talk a bit about where we should seek to test and why.

In a basic Android MVP project, you can split your code up into mainly three larger layers:

The View

The Presenter

The Model

The View is dumb. While some people do like to write unit or integration tests for it, generally you should hope you don't have to. What's more important for the View layer is that it feels right, and that interaction with the application through various screens and flows doesn't do anything unexpected. It's probably the most dog food layer for testing. Generally you would just be dealing with direct implementations, Activities and Fragments and Views.

The Presenter is dumb as well. The presenter should serve a single, easy purpose. It should parse information from your Model layer and package it into easy bite sized pieces for the View layer to react to. It shouldn't need extensive testing because, well, it shouldn't do any heavy lifting. While we don't want to call this layer dumb, it should have so little logic in it that there is really nothing important to test. To make my point, many of my applications have a presenter which simply subscribes to an Observable and puts the RX operations on the correct scheduler. While some people like to make this layer accessible via Interfaces and then hide an implementation (as I used to do) it adds a large amount of extra files for no real benefits. You should be safe working with direct implementations here too.

A sublayer of the Presenter layer is the optional Interactor that many programmers use to define an interface which interacts with raw sources of data and parses it into something that can be consumed by the Presenter object. I use Interactors in my projects as well and enjoy the idea of a middle man object like a DAO. While the data they consume is ever changing, I like to fall back on the Observable object as the resulting something that I pass to my Presenters. The Interactor does some heavier lifting, it's job would be to take various sources of data and turn them into objects that are consumable by the Presenter, so there may actually be a lot of logic here. As such, you'd probably prefer to work with direct implementations instead of interfaces, because you want to know that your logic for parsing these raw types into consumable Objects is correct. You don't want to have a test double for your underlying logic.

Where you want to actually be able to drop in test doubles is in your Model. If you are able to double the data source and data stores, then you shouldn't care about testing the other layers. You would simply need to validate that your Interactor, Presenter, and View all perform the way you would expect given a set model. By making sure that various fake double models are handled correctly, you have validated that your logic is sound between the other layers of your application.

The real model is anything that you would consider a data source or data store. Databases, shared preferences, files, Android specific device components (like the Camera) should all be abstracted away behind a POJO interface where possible. Even things like your Retrofit interface which defines your endpoints, and your Autovalue classes would fall in this category. By being able to provide test doubles for a database operation, or shared preference storage, or even the response to a Camera request, you can validate that your logic in the application is sound. Because it is out of your control to handle things like the underlying Camera API or how Retrofit hit endpoints, you can rest assured that if your tests pass, its as good as things will get.

You can reduce the number of tests and interface doubles in your code to strictly the classes that are in the Model layer. This should help clean up your code and allow you to remove many redundant tests which may be duplicating or even replacing your production logic for Interactors, Presenters, and Views.

Or you can be like me and have no tests at all.

:)

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my FaceBook Page
Follow my Google+ Page
=========================

MVP is weird

So the more I read about MVP architectures as they apply to Android, the more I see things like this:

Presenter.java

class Presenter {



    DataSource ds = DataSource.get();
    View v;

    void bindView(View v) {
        this.v = v;
    }

    void unbindView() {
        this.v = null;
    }

    void doSomething() {
        v.onDoSomething();
    }

    void doSomethingElse() {
        ds.dataToObservable()
            .subscribe(data -> {
                v.onDoSomethingElse(data.stuff);
            });
    }

    interface View {
        void onDoSomething();

        void onDoSomethingElse(Stuff stuff);
    }
}


View.java


class View implements Presenter.View {

    Presenter presenter = Presenter.create();

    void onStart() {
        presenter.bindView(this); 
        button.setOnClickListener(v -> {
            if (condition) {
                presenter.doSomething();
            } else {
                presenter.doSomethingElse();
            }
        });
    } 

    void onStop() {
        presenter.unbindView(); 
        button.setOnClickListener(null);
    } 

    @Override public void onDoSomething() {
      doStuff();
    }

    @Override public void onDoSomethingElse() {
      doOtherStuff();
    }
}

That was a difficult process to write all of that psuedocode in Blogger. Not fun.
I'm curious to know where the Android practice of making the Activity or Fragment or whatever implement the entire View contract began. It seems like it clutters up the Activity with all of these callback methods that you can't really figure out where they are coming from or where they go to.

It seems to me like it would be much more efficient to implement these callbacks in separate contracts for the presenter instead of one large one, and have each presenter method accept a different contract. In doing so, you can keep the callback in a visually easy to read place - passed in with the method call to the presenter.

This way, while you are jumping around the various views and dealing with the already difficult to follow lifecycle methods, you can rest assured that your presenter layer logic won't just come out of the blue.

You can easily then trace the presenter logic by simply using Android studio to open the source file for the presenter, and you can see just where your callback is used.

Presenter.java

class Presenter {



    DataSource ds = DataSource.get();

    void doSomething(Something something) {
        something.onDoSomething();
    }

    void doSomethingElse(SomethingElse somethingElse) {
        ds.dataToObservable()
            .subscribe(data -> {
                somethingElse.onDoSomethingElse(data.stuff);
            });
    }

    interface Something {
        void onDoSomething();
    }
    
    interface SomethingElse {

        void onDoSomethingElse(Stuff stuff);
    }
}


View.java


class View {

    Presenter presenter = Presenter.create();

    void onStart() {
        button.setOnClickListener(v -> {
            if (condition) {
                presenter.doSomething(() -> doStuff());
            } else {
                presenter.doSomethingElse(() -> doOtherStuff());
            }
        });
    } 

    void onStop() {
        button.setOnClickListener(null);
    }
}

Sure it nests callbacks, which can visually not be as nice, but the code that responds to the presenter's action stays very local to where the presenter is invoked. And if you have lambdas, you can clean up the code to the point that you would barely notice the callback chain.

You'll have to make sure you release memory and subscriptions for your presenter onStop so that you don't accidentally hang on to these callbacks, but then again, you should be doing that already.

Visually your code will be easier to interpret and easier to read. It saves you from having to search through files just to find where you override a callback, and saves you from having to dig through files to find out which interface provides which contract for which presenter.

Brain dump off.

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my FaceBook Page
Follow my Google+ Page
=========================

A work in progress

Lots of fixes under the hood, but unfortunately no real new features.

Smaller APK sizes across the board. Faster performance, less memory usage, all that fun stuff.

Code is hopefully easier to read and go through and maintain.
I've been working particularly on stability in Power Manager. Older methods would prevent a lot of frequent automatic changes, I'm hoping with the new Android Job library backing the automatic queuing, the application will be more effective and more efficient.

DontSuck media player is actually nearing release, though its taken longer than I initially imagined.

PadLock now caches all of its large lists, meaning you only need to wait through the long loading time once, unless you wish to refresh the lists, which is still done in the same way.

Hoping to make a release over the weekend, but we'll see as time permits.

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my FaceBook Page
Follow my Google+ Page
=========================