Flow (or my fork of it, Flowless) is a backstack. Well, technically it is a thing that holds the backstack, which is called History.
History
is — you can guess it — the history of the application at a given time. This History
is actually just a list of objects — albeit its indexing is inverted, where get(0)
returns the last inserted element, and get(size-1)
returns the first inserted element. A stack, if you will.
A ← top element, .get(0)-B-C-D
As list:
[D,C,B,A]
In this list, the object D
, C
, B
, A
is just a simple instance of a class with some data fields and hashCode/equals methods; also called a “value object”. It’s just a class that looks like this though
public class A {private final String data;
**public** A(String data) {
**this**.**data** \= data;
}
@Override
**public boolean** equals(Object object) { _// auto-generated_ **if**(object == **null**) {
**return false**;
}
**if**(!(object **instanceof** A)) {
**return false**;
}
**return** StringUtils.equals(**this**, **data**, ((A) object).**data**);
}
@Override
**public int** hashCode() { _// auto-generated_ **return** getClass().hashCode() + 31 \* (**null** \== **data** ? 0 : **data**.hashCode());
}
}
So yeah, this A
totally simple object that just contains a String and overrides equals/hashCode is called a Key
. This is what represents where you are in your application.
In the case of Activities, this would mean that you have the following activities:
DActivity, CActivity, BActivity, AActivity
and DActivity
, CActivity
, BActivity
would be in the background in onStop
(but not destroyed), while AActivity
is in the front (with onStart()
and onResume()
called).
When you start AActivity
in BActivity
, what happens is that BActivity
is put to onPause()
, AActivity
boots up and is placed in… actually, let me grab the documentation
Activity A’s onPause() method executes.
Activity B’s onCreate(), onStart(), and onResume() methods execute in sequence. (Activity B now has user focus.)
Then, if Activity A is no longer visible on screen, its onStop() method executes.
But in this case, I had to:
1.) check the documentation to see what’s going on
2.) the backstack is implicit, so getting from AActivity
to CActivity
immediately is very very difficult. You need to tinker with CLEAR_TOP
intent flag and hope it actually works. If it doesn’t, you might even set singleTop
launchMode on your Activity to finally make it work, somehow.
— — — — -
Now in the case of Flow, this isn’t so complicated. When you call
Flow.get(this).set(A.create());
Then what you receive is called a “traversal” that is “dispatched”. Simply put, it tells you that something happened, and shows where you previously were in your application: [D,C,B]
, and where you are heading: [D,C,B,A]
.
If you were in [D,C,B,A]
and you call Flow.get(this).set(C.create());
, then you will get a traversal that says you were in [D,C,B,A]
, and you’re going to [D,C]
.
What happens (the stuff I grabbed from the documentation) is completely up to you to write, so you are in direct control of what happens.
This generally means that what you end up doing is:
- check if you’re still in the exact same state, if yes, then don’t do anything (tell Flow that you’ve handled the traversal)- persist the state of the current layout- remove current layout- inflate a new layout- restore the state of the new layout (if state exists)- add new layout- tell Flow that you’ve handled the traversal
(here’s an example for a simple dispatcher)
— — — — — —
This is actually great for multiple reasons:
1.) you don’t end up with views in the background in onPause()
that are not destroyed, so you don’t have to ensure that you “reload the data and refresh” when you navigate back
2.) the views are destroyed, thus freeing up memory, which is more performant
3.) activity transitions have additional overhead, which makes them slower than view transitions (also, TransitionEverywhere
works on Views, but not on Activities/Fragments)
4.) now that you have direct access to “where you are in the app”, but also “what happens when I go from here to there”, it’s actually pretty easy to set up a traversal like so:
[D,C,B,A]
=> [C,E,B]
Because it’s literally just
Flow.get(this).setHistory(History.newBuilder().push(C.create()).push(E.create()).push(B.create()).build());
How do you intend to do that with the Activity stack and intent flags? No damn clue!
— — — — — — — — — — — — — — — — — — — — — — —
— — — — — — — — — — — — — — — — — — — — — — —
TL;DR: Flow is a backstack library that allows you to easily persist/restore the view’s state, and allows you to handle changes between where you are at a given moment, and where you are heading. That’s pretty much all it does.
(Reddit discussion thread: https://www.reddit.com/r/androiddev/comments/5h0er6/eli5_what_does_squares_flow_library_do/ )