Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom interfaces in generated activities delegate to transfuse activity #56

Open
ghost opened this issue Jan 24, 2014 · 22 comments
Open
Milestone

Comments

@ghost
Copy link

ghost commented Jan 24, 2014

We use the pattern in our existing app where Fragments require an interface on the containing activity to communicate back to the activity so that in onAttach we can validate that the Activity is of a type that can support the given fragment:

if(!(activity instanceOf CustomInterface)) {
    throw new RuntimeException("Fragment must belong to an Activity that implements CustomInterface.");
}

So that we can later delegate to the activity to handle intra-fragment communication. Is there an existing way to cause transfuse to wire up the delegation from the generated activity to the transfuse activity to support this, or is there a better pattern to achieve this same mechanic?

@johncarl81
Copy link
Owner

@zump I have in the past used the EventManager and @Observes event system to pass data to and from the Activity/Fragments. You can see a working example of this here:

https://github.com/johncarl81/transfuse/tree/master/examples/integrationTest/src/main/java/org/androidtransfuse/integrationTest/fragments

Does this work for you? IMO, This is one area that Transfuse could be better, but I'm not sure what the answer is. Do you have an idea for anything that may work better?

@ghost
Copy link
Author

ghost commented Jan 24, 2014

I like the event based pattern... but it's going to be a pain to retrofit. Also, it's going to be tricky if there are return values involved, right?

How about something like this...

@Activity
@Delegate(interfaces = {CustomInterface1.class, CustomInterface2.class})
public class Main implements CustomInterface1, CustomeInterface2 {
@Override public boolean customMethod1() {
   return true;
}
@Override public void customMethod2(boolean boo) {
  // do something with bool...
}
}

which would give you something like this:

public class MainActivity extends Activity implements ContextScopeHolder, CustomInterface1, CustomInterface2 {
    private Main main$$0;
    public void onCreate(Bundle bundle$$1) { .... }

   @Override public boolean customMethod1() {
      return main$$0.customMethod1();
    }
   @Override public void customMethod2(boolean boo) {
       main$$0.customMethod2(boo);
    }
}

@johncarl81
Copy link
Owner

That's not a bad idea, and it relates to the "Call-through" events nicely.

I have a feeling that I'll be doing a lot of Transfuse tinkering this weekend 😃

@ghost
Copy link
Author

ghost commented Jan 24, 2014

Great, let me know if I can be of assistance... I've never done any work with writing annotation processing code.. but I can certainly help test it out. In the meantime, I guess I'll see how far I can get with the migration to event based stuff.

@johncarl81
Copy link
Owner

You're more than welcome to dive into the code and I'll let you know if/when I come up with something around this.

@johncarl81
Copy link
Owner

@zump I was thinking about this some more. Is what you require an object that is scoped to the Activity injected into your Fragments?

@ghost
Copy link
Author

ghost commented Feb 4, 2014

I need to be able to call methods on the delegate from an interested fragment or activity.

@johncarl81
Copy link
Owner

Ok, so I'd imagine if you could inject the delegate, your problem would be solved. Is this correct?

@ghost
Copy link
Author

ghost commented Feb 4, 2014

Yeah, that would do it.

@ghost
Copy link
Author

ghost commented Feb 5, 2014

This might get trickier than I thought... The app I'm currently converting has a very deep hierarchy of activity -> fragment(s) -> view -> view -> view... that at each level are expecting to get the context and cast it to one of several interfaces which the parent activity implements... to either trigger some behavior to propagate back down the hierarchy or grabbing the current state of data if some event bubbles up. So to retrofit this app, I would need to be able to expose that set of interfaces and have them delegate from the actual activity to the delegate.

I'm going to introduce a/some new injected object(s) to take the place of the existing mechanism.. but it seems like it might get to be a weird combination of singletons and events... I need to think it through more.

@ghost
Copy link
Author

ghost commented Feb 7, 2014

I'm slowly working my way through this massive refactor, but it is becoming really annoying to do fragment -> activity -> fragment communication... especially when there is shared state between two fragments that live in an activity... especially when only one fragment is "live" at a time.. that means the Observer pattern just wont work as the state needs to propagate once a different fragment is made live. I can't call methods on the fragments directly since they don't delegate arbitrary methods.

I am going to have to use a shared context scoped object or a singleton to handle the shared state... but it would be nice to just be able to inject the activity by an interface and share state that way... thoughts?

@ghost
Copy link
Author

ghost commented Feb 7, 2014

The shared object is problematic in that the activity or fragment may need to know when the state in that object has changed, which means registering listeners... then I get concerned about leaking context if someone (obviously not me!) is sloppy.

@johncarl81
Copy link
Owner

Could you do a combination of @Observes event listeners and context/singleton scoped objects? @Observes broadcasts in a way that will not leak. You could also use Otto instead of the EventManager if you want the @Produces feature.

... just a warning, context scoping may have issues as I've never fully tested it.

@johncarl81
Copy link
Owner

By the way, is there any way I can see the results of your refactor? Im curious to see the results.

@ghost
Copy link
Author

ghost commented Feb 7, 2014

I think that would mean that I would need both FragmentNotifyingActivityEvent and ActivityNotifyingFragmentEvent so that the activity doesn't Observe its own events?

It looks like otto would help, but it seems too error prone with all the manual registering and what not...

Unfortunately I can't share my source... but I could walk you through it on a screen share or something if you were interested.

@johncarl81
Copy link
Owner

Could the Fragment -> Activity "notification" just be a method call on a singleton, then the resulting update to the fragments be an @Observes event? Otherwise this sounds good.

I have a branch on my machine of Otto integration without manual registration, but that's with the seemingly abandoned version 2... I wonder if I could get it to work with version 1.x. (Another thing I would love to stick in a plugin library)

I'd be very interested in a screen share walk through. Contact me via email (in my profile) to coordinate.

@ghost
Copy link
Author

ghost commented Feb 7, 2014

So the fragment would call a method on the singleton, and the activity would be alerted how? I can see how to do this with bi-directional events.. but it seems like it would be tricky for anyone who didn't write it to follow....

Otto integration would be really fantastic... It's a really cool library. I'm not familiar with the 2.0 changes.. are they substantial?

This plugin framework you keep mentioning sounds glorious :)

I'll email you shortly.

@ghost
Copy link
Author

ghost commented Feb 7, 2014

Looks like this branch is still kicking... https://github.com/square/otto/tree/otto2

@johncarl81
Copy link
Owner

What I am envisioning is your sate being held in a singleton (or some scoped object) that would act as the central point of coordination. This singleton could be updated directly that would then broadcast out to subscribers that a change has occurred.... possibly the Activity could be a consumer of this event as well?

Here's the general idea:

@Singleton
class StateManager{
    @Inject EventManager eventManager;
    public void setState(State s){
        //update state
        eventMAnager.trigger(StateUpdate(s));
    }
}

@Activity
class ExampleActivity{
    @Inject StateManager
    //...
    public void onSomething(){
        stateMAnager.setState(...);
    }

    @Observers
    public void update(StateUpdate u){...}
}

@Fragment
class ExampleFragment{
    @Inject StateManager

    public void onSomethingElse(){
        stateMAnager.setState(...);
    }

    @Observers
    public void update(StateUpdate u){...}
}

Hmmm, looks like is some activity. Lets hope they release it soon. This plugin framework will be glorious! I should start work on it this weekend.

@ghost
Copy link
Author

ghost commented Feb 7, 2014

Yeah, that's kinda the path I started on... I'll let you know how it turns out.

@ghost
Copy link
Author

ghost commented Feb 8, 2014

I ended up cleaning up a bunch of these interactions.. it worked out fine. I still think we need the ability to delegate interfaces or at least inject the delegate in some situations.. but I think I'm past all the refactoring spots with very workable solutions.

@johncarl81
Copy link
Owner

Great to hear. I think there could be a handful of injections that could happen, namely @Activity -> @Fragment, and possibly @Service -> others (#28). I need to focus on fixing up context scoping and possibly fragment scoping.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant