Skip to content
This repository has been archived by the owner on Feb 26, 2023. It is now read-only.

Listeners on activity callbacks #5

Closed
pyricau opened this issue Jan 6, 2012 · 8 comments
Closed

Listeners on activity callbacks #5

pyricau opened this issue Jan 6, 2012 · 8 comments
Assignees

Comments

@pyricau
Copy link
Contributor

pyricau commented Jan 6, 2012

I'd like to implement a new Listener system for the activity callbacks, somehow inspired from RoboGuice and other sources.

Here is the idea :

We would create some listener callback interfaces, such as OnCreateListener, OnResumeListener, OnPauseListener... they may all inheriting from an AndroidCallbackListener interface (a marker interface useful from the framework point of view).

Users would write a listener class :

public class MyListener implements OnCreateListener, OnResumeListener {

  public void onCreate(Activity activity, Bundle savedInstanceState) {
    // do something
  }

  public void onResume(Activity activity) {
   // do something
  }
}

Then, they would be registered in the activity :

@EActivity
@Listeners(MyListener.class)
public class MyActivity extends Activity {

}

And that's all : onCreate and onResume in the listener will be called immediately after the callback are called in MyActivity.

The implementation would just create a new instance of MyListener and set it in a field of MyActivity_, and delegate the calls to onCreate, onResume, etc.

This whole thing is not precisely defined yet, we need feedback, so please everyone tell me what you think of this.

The listeners could also be written with a constructor given activity, instead of something method based (so it shows that there is one instance of listener per activity :

public class MyListener implements OnCreateListener, OnResumeListener {

  public MyListener(Activity activity) {
  }

  public void onCreate(Bundle savedInstanceState) {
    // do something
  }

  public void onResume() {
   // do something
  }
}

On the activity side, we could use an @EActivity field instead of a dedicated annotation :

@EActivity(listeners = {MyListener.class})
public class MyActivity extends Activity {

}

BTW, what do you guys think : should it be one listener per activity, or one per application ?

Should we provide a way to use a specific kind of activity ? For instance, having an OnCreateListener interface, we could do this :

 public class MyListener implements OnCreateListener<MyAbstractActivity> {

  public void onCreate(MyAbstractActivity activity, Bundle savedInstanceState) {
    // do something
  }

}

Or if we go with the constructor solution, then we could allow subclasses of activity, and check that the listener can be used on the activity.

Any other idea ?

This would be a great alternative for issue 110 (provide a vendor specific annotation that do some stuff in some callbacks), because we could then do something like :

public class CapptainListener implements OnPauseListener, OnResumeListener  {

    private Activity activity;

    public CapptainListener(Activity activity) {
      this.activity = activity;
    }

    @Override
    public void onResume() {
        CapptainAgent.getInstance(activity).startActivity(activity, activity.getClass().toString, null);
    }

    @Override
    public void onPause() {
        CapptainAgent.getInstance(activity).endActivity();
    }
}

@vincent.barat, Nov 1, 2011 :
Yes, I think it would be a much better way to handle Capptain integration and other Activity related listeners.
I vote for it ! :)

@pyricau
Copy link
Contributor Author

pyricau commented Jan 13, 2012

I moved this from 2.3 to 2.4 because I'd like to have a bit more experience and feedback on the @Enhanced usage before introducing listeners. They might be mixed together.

Please don't hesitate to give input and ideas !

@pyricau
Copy link
Contributor Author

pyricau commented Jan 28, 2012

We have a lot of options, and I'm not sure what's best.

First, should there be :

  • one listener instance per activity => we can inject the activity (with @RootContext) instead of having it as a param
  • one for the whole app (singleton) => then the activity needs to be passed as a param

I think the first option is the best, since it's easy then to delegate some stuff to another singleton of your own if you need to.

The other design question is : until now we talked about listeners, ie forwarding callbacks calls to a bunch of classes. In some cases and for some callbacks, we might actually need delegates. The idea here is to be able to cancel a call (or delegation to other delegates) by returning true or false.

/**
 * Returns true if the call has been handled and should not be any longer delegated to other delegates / to super.
 */
public boolean onSomeCallback() {
  // ...
  // return handled;
}

Of course, we don't want this for lifecycle callbacks (onPause(), onCreate(), ...), because the calls to super are mandatory.

Next design question : should listeners handle all callbacks, some specific callbacks through interfaces implementation, or any method using a way to designate those methods ?

All callbacks :

public interface ActivityDelegate {

  void onCreate();

  void onPause();

  // ...

}

We could have an AbstractActivityDelegate with no ops for each method to allow implementing only the needed ones. This would be safer from the annotation processor point of view, especially with Eclipse compiler, because the activity code wouldn't depend on the listener implementation.

I'll try to describe other options later.

@funk78
Copy link

funk78 commented Apr 4, 2012

I dont understand everything you've written here but i love the idea of listeners! Because then i could really finally get rid of roboguice.

At the moment i use Listeners for 3 things: Language, Themes and Tracking.

As for the Theme it was important that the Listener callback would happen before the setContentView(). So it might be useful to define if a listener should be called at the beginning or at the end of a method?

I vote for:

 @EActivity(listeners = {MyListener.class})

instead of an annotation.

@naixx
Copy link
Contributor

naixx commented Aug 1, 2012

I agree with one listener per activity or fragment and it seems that defining specific callbacks would be more convenient instead of ActivityDelegate.

@EBean
public class Test implements OnResumeListener{

    @Override
    void onResume(){

    }
}

So that we can inject the bean in an activity or fragment. We can check at compile time, if the bean implements any listeners' interface and is injected in the right place(activity or fragment). There is a question if one bean injects another.

Or we can make it via annotations:

@EBean
public class Test{

    @OnResume
    void myResumeCallback(){

    }
}

The code would be even cleaner. We could have something similar to RoboGuice events.

@pyricau
Copy link
Contributor Author

pyricau commented Aug 3, 2012

I think this is an interesting direction. This means, however, that we cannot have custom events. At least not this way.

Since we'd want to cascade lifecycle calls, we'd need all EBeans to implement "onResume / onPause" etc methods, and cascade to all their dependencies, although it will mostly be empty method calls.

Then, "@onResume" would just add a call to from annotated method from the "onResume()" method implemented in the generated code.

@naixx naixx mentioned this issue Aug 20, 2012
@pyricau
Copy link
Contributor Author

pyricau commented Oct 18, 2012

Regarding the lifecycle callbacks, Android 14 provides the needed bits ActivityLifecycleCallbacks can be used with Application.registerActivityLifecycleCallbacks()

Also worth looking at Otto, which provides an Event Bus for Android.

@pyricau
Copy link
Contributor Author

pyricau commented Oct 18, 2012

Maybe we should implement our compile time version of Otto, or maybe users that need this kind of feature should be redirected to Otto.

There's really two things here : responding to lifecycle events, and being able to use an event bus.

@pyricau
Copy link
Contributor Author

pyricau commented Mar 3, 2013

I gave it a few more thoughts and I think in fact an event bus is the way to go. If one wants lifecycle callbacks for all components, one can create a Base class for that type of component.

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

No branches or pull requests

3 participants