Tuesday, May 14, 2019

Who Owns State on Android

This is sort of a follow up to my last post.

The idea of state in an Android application is a confusing one. What is state? Why is it so important, and how do we attempt to get it "right"?

state in an Android application is the idea behind "who dictates what is currently being shown and being done on my device at this very moment". For example, for an app which opens a screen that changes colors between red, blue, and green, the "state" of that application could be which color is currently being displayed on screen.

Android has traditionally been very very bad at state management - I would argue it makes Android the most difficult platform to develop on simply because the job of state management is left up to the developer to manually implement in almost every single use case. Contrast this with something like web development, where HTML and scripts are cached by default unless you opt out, and strict frameworks like Angular's enforcing of DI principles or React's enforcing of one way MVI state help web developers keep their focus on the experience of using an application instead of worrying about how that tool works under the hood.

Don't get me wrong, I have many complaints with modern development and how it de-emphasizes knowing the importance of how the building blocks work - but I digress. Ultimately, at the end of the day we all want to build cool useful tools, not argue about the proper way to send a stream of bytes over the wire.

I personally believe one of the reasons why Android has traditionally been so poor at state management is because unlike these Web frameworks - or even iOS development which enforces a strict separation between Views and their ViewControllers, Android has historically never enforced any kind of recommendation or best practice - leading to many developers to learn with no idea of what even good practice may be. Android addressed the most general use case of state management, providing us with the simple onSaveInstanceState(Bundle?). Unfortunately, a general purpose tool that tries to be a little of something for everyone often ends up being nothing for anyone.

The reason why Scoop, and MvRx, Mosby, and Mobius need to exist is because of the failing of Android as a base platform to provide its developer community with a sane foundation to build on. The reason why I spent the last 2 months noodling on the idea of my own MVI framework (not saying it is any better than any of the other solutions - it was just more fun) was because these shortcomings of the platform had slowed me down for the last time. Even Google seems to acknowledge the mistake now that they are coming out with JetPack Compose which will one day be the future of Android UI.

UI is the one most difficult things in almost any application. Android applications are usually not very complex - they don't do much heavy lifting and generally just talk to servers. Their job is usually nothing more than to look pretty and feel cool when you click and tap and swipe around. Of course, not all applications are like this - pyamsoft applications are purely offline experiences using local databases or other methods of storage, so some heavy lifting is done in the applications, but nothing huge. For many cases though, the most rewarding payoff for time spent in development is to show a cool new page or animation to delight users and to provide a fun environment for working developers, or to build a cool new useful application that we use and enjoy day to day.

And Android is fun - its some of the most fun programming you'll ever have. It's so rewarding to finally get something to work and see it all come together before your very eyes. But some of that reward can be due - in part - to simply overcoming the large amount of useless ceremony that the platform puts in your way in order for you to get to your eureka moment.

Activity result callbacks, permission callbacks, fragment visibility callbacks, Activity window state changes, Window inset consumption, configuration change handling, service foreground state handling, notification channel management, receiver registration, sensor management, view related memory leaks - all of these are side effects of the platform that are left to the developer to deal with on a case by case basis. I love it - I love the freedom I get to build each application unique from the last, and I hate it when I just need to get things done.

And the crux of all these problems, the Achilles heel of UI development, is state and the ownership of state. On Android, who owns the state of a particular visual on screen?

Activities and Fragments are monstrous classes with many different potential roles. To save ourselves from going insane too quickly we always try to delegate much responsibility away from these classes as possible. There are only at max two jobs these classes should be required to do, handle system lifecycle events and handle navigation events. Everything else should be delegated away if possible. Do yourself a favor and save yourself in the future - your Android system classes like Activity, and Fragment - and to a slight degree Service - should be the ViewController, not the view. The only state these controllers would hold, is "which views are displaying on screen." This is complimented by things like the FragmentManager, which are only accessible from these classes, that handles its own internal state of "which fragment is displaying on screen." You should avoid View related state entirely if possible in your Activity and Fragment classes. UI state and the ownership of what is displayed on screen should be handled by the UiViewModel instead.

The View is a two part system - the UiViewModel and the UiView itself. The UiView should generally be a stateless class that knows how to color and shape and position itself to look pretty. The UiViewModel is a simple data class which holds a representation of the current look of the View in code. So for example, a UiViewModel like

data class MyUiViewModel(val isRed: Boolean, val displayName: String)


Would convey a model that controls the displayName rendered inside of it's UiView's TextView and whether or not the background color is red

By keeping the UiView stateless, any scenario can be rendered on demand. "Hello World" with a red background, "Hello Foo" without a red background, a blank name and a red background, and so on.

As we've seen in frameworks like React, UiViews are only updated via a single render(T) method which is given a snapshot of UiViewModel state as its only T parameter (some implementations also pass the previous state T as an additional parameter). This way, we can be guaranteed that any UI changes are all happening at the same time, and that the UI will always consistently display itself as a function of state.

By letting UiViewModels be the single source of truth and own the UiView state entirely, we can avoid Android lifecycle related bugs and achieve more consistent and predictable UIs. Of course, this can come at the cost of more typing - but the trade off of less cognitive load during debugging is its own reward.

Its not perfect - and it never will be. Modeling state in an asynchronous system like Android means bugs - and bugs are eternal. Problems still exist outside of the realm of state though, like what is the scope of each model of state? What, if anything, in Android owns the global "top down" view of application scope - and how can we synchronize the various bits of UI state between multiple views?

Perhaps another day.

For more - certainly better - reading on the architecture around Android and state, see this and this to understand why I am harping on state so much.

Stay tuned.

========================
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
Check out my code on GitHub
=========================

Friday, May 10, 2019

UI Architecture Design Patterns

At Google I/O 19, the Android Jetpack team announced Compose, which basically brings a React style UI toolkit to Android. Awesome.

For those who haven't worked in Javascript land before, I highly recommend you take a look at React and read about its philosophy behind the handling of state and data.

tldr; By enforcing a strict 1-1 relationship between a given component's state and the actual visuals drawn, you can be sure that data is always synchronized -- what ever business logic manipulates is what the screen will draw.

However, as with all nice things, Compose is not ready yet for prime time. In fact, it's not even ready for developer time - as it currently is in a pre-alpha stage. Google does recommend though that you prepare your applications for a world where a one way composable view state becomes the norm - and we don't have to wait at all to start doing that!

-----------------------------

Recently I was inspired by a talk given by Netflix engineers at a Droidcon event back in 2018 where they outlined how Netflix approached UI building via independent components which they composed into a screen. I adapted their architecture into the following couple of basic classes.

UiView.kt

@CheckResult
@IdRes
fun id(): Int

fun inflate(savedInstanceState: Bundle?)

fun saveState(outState: Bundle)

fun teardown()



UiViewModel.kt

fun bind(onRender: (state: T, oldState: T?) -> Unit)

fun unbind()



UiComponent.kt

@CheckResult
@IdRes
fun id(): Int

fun bind(
  owner: LifecycleOwner,
  savedInstanceState: Bundle?
  callback: C
)

fun saveState(outState: Bundle)





The UiComponent class would take any number of related UiViews and UiViewModels and would result in self-contained components that could be dropped into Android Views, Activities, or Fragments. Not bad - but there were many shortcomings with my implementation.

The application state was a two way flow, which would prove to be difficult to manage. Because a UiComponent was meant to be self contained, it was both a View and a Controller. My attempt at separating logic out of Activities and Fragments did not actually fix anything - it just changed the name of the problem to UiComponent. As a result, the relationship between UiView and UiViewModel was only loosely defined - sometimes the view would listen to the model and change how it displayed content on screen, sometimes the model would listen to the view and fire off navigation events using some rather hacky logic.

There also was the issue of creating the UiComponent class for each of these UI widget groupings - it was an extra file that really did not do much on its own except abstract other classes needlessly. It had to be better.

I went back to the drawing board and reasoned about how I could simplify this basic 3 class architecture I had built.

My first win was getting rid of the UiComponent class all together. It was basically a useless extra - it served as a Controller for the UiViews passed to it - but I had already established that in my Android architecture the Activity or Fragment was the controller. There was no need to have a Controller wrap another Controller, so UiComponent was removed.

UiView was a little bit trickier. Because Android's current view system is a far cry from reactive composable state, I had to make do with the current UI toolkit - which means I was required to have an inflate(Bundle?) method as that was the step where Android would inflate layout resources and find views and whatnot. Similarly, I needed to keep a teardown() method to release resources, as well as a saveState(Bundle) method for process death persistence. The id() method helped with laying out my UI widgets inside of ConstraintLayout containers, so it too stayed. Looks like I wasn't able to simplify UiView any amount - but that doesn't mean I was not able to improve it.

The main problem with UiView is that there was no established contract for when a UiView updates. Taking a page from the React toolkit, I added a render(T, T?) method which I would adopt as the only place to make dynamic UI updates in a UiView class.

Next I tackled the UiViewModel. My main issue with this class was that it required an arbitrary lifecycle of bind() and unbind() in order to make it possible to use at all. After working at it for a while, I managed to reduce UiViewModel to just one function, render() which will treat its lifecycle as the lifecycle of the RxJava stream it associates state events with. Meaning, once the RxJava stream opens, the ViewModel is "bound" and once the stream is disposed, the ViewModel is "unbound".

Instead of manually passing a callback to and from ViewModels and UiViews, I used an RxJava bus - one for the UiView as a stream of interaction events, and one for the ViewModel as a stream of presentation events. This way, a View would only emit events to its ViewModel, and the ViewModel would only emit events to the Controller. A uni-directional data flow at last.

Finally, to wrap up things and make my new spicy code play nicely with Android, I built a small stateless function which takes any number of UiViews and a ViewModel which controls them all and performs some boiler plate to set everything up. This createComponent function will inflate all Views, open the ViewModel state stream and UiView event stream, and register to a LifecycleObserver, which will dispose the stream and teardown the views when the lifecycle is destroyed. Much nicer.

I'm not the best speaker or the best writer - and can't really convey these ideas easily over a post. You may just be better off looking at the source code itself to gain a better understanding of what I'm trying to do. The buggy code for the new and hopefully improved UiViewModel can be found here. Here is the new UiView code, and my simple component wrapper.

I'll be updating all apps to follow this new architecture and eagerly await the day that Jetpack Compose releases. This new architecture should lead to fewer bugs for all the existing pyamsoft Android applications, as well as speed up the development process of my new application that is still a work in progress... but that's for another day.

========================
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

See my code on GitHub  
=========================

Tuesday, April 9, 2019

What's Up

Updates that's what.

Updates rolling out today/tomorrow for Home Button, ZapTorch, Pasterino, and WordWiz.

PYDroid has been updated to 19.2.X which is the first series to bring a new -arch module which provides an MVI design pattern for easy application building - it is powering all pyamsoft apps!

I'm working on something new at the moment - under wraps for now (unless you're able to read Git commits) since I need to make sure I have all the features rounded out as far as being feasible or not before I announce anything.

This release also marks the deprecation and unpublishing of PadLock off of the store. Unfortunately, like Power Manager before it - it is no longer welcome in a constantly changing Android environment. F for PadLock. F for Power Manager.

Long live pyamsoft.

More when I can, stay tuned...

========================
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
=========================

Wednesday, March 13, 2019

Long Time No Post

I feel like I've written this one before.

Time goes by and life catches up to us all, and I am no exception. I've been away from posting for a while, but have not been away from development. In fact, I've been working for the past couple of months re-architecting all of the pyamsoft applications to use a fancy new View-Ui-Component architecture that was introduced by Netflix at a Droidcon in 2018. It's been a wild ride and has opened up new ways for me to think and design applications.

Speaking of the applications, I've spent the last few months working on PadLock, and was hoping to release an update once I was satisfied with the codebase. However, with the recent Android Q beta announced, I find myself halted in my tracks by the big G themselves.

Android Q will be fantastic I'm sure, but it brings a change that is fundamentally incompatible with how PadLock operates. This change in how Activities can be launched from the background is the death knoll for PadLock and possibly many other applications. While I avoided the SMS fiasco, no man is safe forever.

As such, I am ceasing PadLock development. Sadly, while I am sure I could use a system level OVERLAY_WINDOW to get around the current background requirements, I am sure a future day will come where that is deprecated too in favor of PiP mode, or some official overlay API. Neither would work for PadLock's case - as its job is to be a full screen locker and directly intrude on the usage cycle of a nefarious user. With Android's continued lock down on the ability for applications to do unique and complex things across the device, PadLock is no longer a welcome application in the new world ecosystem.

I will still release updates for the other applications, but please note that once you upgrade your device to the shiny new Android Q later this year PadLock will cease to work at all. It saddens me to admit this, but it was fun while it lasted. I hope many of you found use of the tool I created.

Stay tuned.

========================
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
=========================

Tuesday, November 13, 2018

I am the night

Dark mode.

I like it, as a developer.

You like it as an insomniac who can't just seem to stop looking at Instagram and Reddit when its 2 AM and the room is already dark and you should have been asleep an hour ago because you've got an early day tomorrow.

It saves battery life.
It's cool to dynamically switch themes and see the app re-color itself correctly.


It's in - supported by all pyamsoft Android applications on the next release.


See you there!

========================
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
=========================

Tuesday, October 9, 2018

Updates

Tiny update was released which should fix crashes for folks on older KitKat devices, as well as people who have a very specific PadLock setup.

It'll trickle in in a couple of hours. Try it out.

Stay tuned for hopefully new stuff.

========================
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
=========================

Tuesday, September 11, 2018

PadLock and battery life

Battery life is probably the single most important resource on a modern mobile device. The amount of battery left can directly and indirectly determine how long a user can continue using a device, how frequently they interact with the device, and even how fast and power hungry the device operates.

PadLock is an aggressive screen locker that tries to do its best to secure sensitive information on the device while still staying out of the way when not needed. It is this hands off operating style that has given PadLock the one thousand users it has today - and I'm very grateful for that - the opportunity it has given me to reach a wide audience and develop useful tools for all of you has been second to none.

PadLock is unfortunately a little more power hungry as a result of this aggressive operating. If it is enabled - which it must be to be useful - PadLock runs 24/7 365 days a year protecting your device. Because of this, PadLock uses a lot of energy and can sometimes keep the device awake when it should be sleeping.

The next update to PadLock will introduce some changes to hopefully rectify these overstepping of device resource usage.

PadLock will not actively look for new applications when the screen is off. This will save battery while the screen is off (which for many devices is most of the time) and will instantly be back online to lock new applications once the screen turns on.

PadLock does not need to be run all the time. When you need additional security for your device, PadLock is extremely useful - but when it is just you and your phone or tablet lying down in bed relaxing, PadLock does not need to be there locking new applications. The next update will bring the ability to pause PadLock - either until you manually re-enable it or for a temporary period after which it will resume itself. This will save battery in situations where PadLock is not needed because you are interacting with your device in a safe and trusted environment.

PadLock is not the only application on your device, and its time it started acting like it. This next version of PadLock will bring many exciting new changes!

Stay tuned!

========================
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
=========================

Wednesday, August 29, 2018

Better Locking

So PadLock is powered by this thing called Usage Access which is one of the very few (possibly one of two) things in Android that is allowed to watch the phone as a whole instead of just itself as a specific application.

Using its special Usage Access powers, PadLock is able to see the last used app on the phone, compare it to its list of "I want these apps locked" and lock the app if it is needed.

Unfortunately, these special powers are very dumb and difficult to work with resulting in PadLock sometimes being overly aggressive in watching for the last used app on the phone. PadLock was so aggressive it would even watch while the screen was off - which is an oversight on my end. If the screen is off - after all - the device is not being used and therefore would not need to detect the last used application for locking.

So the next version of PadLock will come with an optimization which basically puts it to sleep while the device screen is off. This will save a large amount of battery - since most of the time the device is off in a pocket or a bag - and will let PadLock be a better Android citizen.

I'm excited to release this change along with many stability and memory improvements in the next release cycle.

Stay tuned!

========================
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
=========================

Sunday, August 5, 2018

Updatesssss

Pushed out some Android updates today.

Most notable includes PadLock 3.0.0 which gets a fresh coat of paint as well as a more reliable backend storage solution! Check it out on the store here and leave me some feedback!

I've noticed some issues with Android P which I will be addressing shortly but decided to release anyway because let's be honest who is using Android P when its still in developer preview.

Updates to everything for better or for worse. Report bugs to me via the bug report dialog in app! Thanks for sticking with me through the last couple months of radio silence, and I hope to continue making new interesting creations for all of you.

Stay tuned!

========================
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, July 28, 2018

Long Time No Nothing

Hey remember me?

I'm still here.

Haven't released anything new for a while - life catches up to us all in weird ways and I've found myself busy with other things that means I don't have endless hours to just update Android apps.

Lame I know.

I have some changes I want to get out but things need to stabilize around libraries and the new AndroidX changes with the new Android P stuff, so its all pretty up in the air at the moment.

I don't want to push out builds using Alpha libraries - but I'll settle for "production ready" beta libraries I suppose.

New stuff coming when I have the time - hopefully I have the time soon.


Stay tuned.

========================
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
=========================