square / flow

Name UI states, navigate between them, remember where you've been.
Apache License 2.0
2.79k stars 241 forks source link

Can't understand how properly manage FlowServices #185

Open zoopolitic opened 8 years ago

zoopolitic commented 8 years ago

I'm newbie in flow/mortar concepts, I've got how to user mortar to provide custom services via getSystemContext, and understood basic concept of how flow works, but only with simple examples. I'm using Dagger 2 in all my applications and in current one I have one activity which holds it's Component. With flow I'm replacing screens with KeyChanger like in examples but how can I manage subcomponents (read MortarScopes) within one activity, in particular create and destroy MortarScopes with FlowServices.

I have Application root component and activity component, theirs lifecycles are simple, App component lives as long as App lives and activity's component lives only with activity, but I want to have some "subscope" which lives and dies within activity, let's say I have a couple of screens (flow's keys), and when I open one of them - I create component, then I open next screen and it receives dependencies from this component, then I do some work close one screen, second screen and I need to destroy MortarScope. I hope you understand what I mean.

I thought that i could achieve this behavior with bindServices and tearDownServices, but I can't figure out how to properly do this. In bindServices I can check instance of Key and if it is first screen key that needs component - I can create it, but when should I destroy mortartScope? If I do something like this:

    @Override
    public void tearDownServices(Services services) {
        super.tearDownServices(services);
        if (!(services.getKey() instanceof FirstKey)) {
            MortarScope partScope = rootScope
                    .findChild(partComponent.class.getName());
            if (partScope != null) {
                partScope.destroy();
            }
        }
    }

then scope will be destroyed even if dialog is opened, because I' receive FirstKey in tearDownServices; I think I should use TreeKey maybe, but I'm not quite understand what TreeKey is for. Thanks.

leonardo2204 commented 8 years ago

@zoopolitic I have this same doubt, I started developing a sample project trying to figure this out and providing a sample combining this two excellent libs. Contributions are very welcome !

Anyway, this issue is related to #177

zoopolitic commented 8 years ago

I've dug a little into Flow and how it works and achieved desired scoping logic with TreeKey. Apparently KeyManager won't teardown key until it has child nodes. In order to keep ServicesFactory "lean and simple" I've added interface ScopeKey, so all keys that implement this interface should createService and tearDown it after scope is no longer needed. It works well, but I still have question about flow+mortar (both latest versions). ServicesFactory not work with context now, but the whole idea of mortar is to use Context.getSystemService to manage user services. So mortar and flow are completely decoupled now if I understand right. Every Screen object in flow now can work without mortar, only by using ServicesFactory to provide all needed services and components. What advantages of using mortar with flow?

almozavr commented 8 years ago

Working on the same thing for https://github.com/techery/presenta Have a feeling that mortat is odd now with flow's Services

zoopolitic commented 8 years ago

Want to hear library developers, because maybe I misunderstood something. Also I can't figure out how to "shadow" parent services with mortar. Couldnt find any examples but library's README says that it's possible. Added issue: https://github.com/square/mortar/issues/189

leonardo2204 commented 8 years ago

Yeah, the hard time I'm having is figuring out how to reach the parent scope(or dagger parent) so in case of subcomponents, I can fulfil my child graph.

e.g: EditDialogComponent

@zoopolitic Would you mind to provide some sample of what you did to provide subgraph(scopes), please ?

And the difference between Flow and Mortar (even with ServiceFactory) is that Flow does not provide (yet ?) a way to saveInstance as easy as Mortar's onSave (with Bundler), but I guess Flow has reached a major step providing a ServiceFactories.

Zhuinden commented 8 years ago

Well, Mortar gives you MortarScopes that exist by a String TAG until they are explicitly destroyed and otherwise can contain objects, and also gives you ViewPresenter<V extends View> for your custom viewgroups which have the fairly handy methods onEnterScope() and onExitScope() which aren't called again and again on configuration change, only when the mortar scope is actually destroyed.

I believe Flow Services should be able to retain that for you, and ViewPresenters were a hassle to persist state properly with anyways. I guess the only thing missing is scope inheritance from the application's scope... but is that really necessary?

Personally, I think I'll abandon Flow Services as well altogether, and use the traversal Key chain like I did with my modified Flow-Path's PathContainer from 0.12 retaining whether I should keep a view group's subscoped component alive in the Application scope, and delegate all state persistence to Flow 1.0.

P.S. I'm most likely just rambling. ServiceFactory doesn't have scope inheritance, essentially.

leonardo2204 commented 8 years ago

@Zhuinden I thought about just the same, leaving ServiceFactory alone and create the MortarScope on the Dispatcher. The only downside I think I'll have is to destroy the scope once it's not used anymore.

Zhuinden commented 8 years ago

@leonardo2204 really not that difficult, you have a chain of keys in order if you use Traversal.reverseIterator() for the outgoing/incoming key. If the keys don't match, destroy the scope. You just have to do the same thing as here except use Keys in place of Paths, and do this mitchmatch in the Dispatcher.

zoopolitic commented 8 years ago

I'm using Flow ServicesFactory only to manage scopes (dagger components currently). It's useful because bindServices and tearDown triggers every time some key changes, so KeyChanger and stays clean. As I said before I have ScopeKey:

@public interface ScopeKey<T> {

    T getComponent(MortarScope rootScope);

    void tearDown(MortarScope rootScope);
}

Which is parent key for TreeKeys for several screens and it manages creating component and tearDown scope if needed.

leonardo2204 commented 8 years ago

@zoopolitic Yeah, that is good. But did you manage to make subcomponents (subscopes) to work ?

zoopolitic commented 8 years ago

I'm using subcomponents everywhere, with root scope I'm searching for scope that i need and create subcomponent with it

leonardo2204 commented 8 years ago

With Dagger2 ? I mean, how do you search for the parent scope with a given key and get it's dagger service to pass it as a parent parameter ?

zoopolitic commented 8 years ago

RootScope is providing in the FlowServices constructor as @donaldlittlepie suggested in the issue opened by you. And when we have root MortarScope - we can find any child MortarScope. Unfortunately there is no way to recursively search some service from the root to the child

Zhuinden commented 7 years ago

Because of https://github.com/square/flow/issues/231 , Flow Services don't work as intended