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

Enhance custom classes

Csaba Kozák edited this page Mar 30, 2020 · 40 revisions

Since AndroidAnnotations 2.4

Enhancing custom classes

You can use annotations in a class that is not a standard Android component (such as an Activity, a Service).

You just need to annotate it with @EBean:

@EBean
public class MyClass {

}

An @EBean annotated class must have only one constructor. This constructor must either have no parameters, or, as of AndroidAnnotations 2.7, it may have one parameter of type Context.

Injecting enhanced classes

To use this enhanced class in another enhanced class or in an enhanced Android component, use @Bean:

@EBean
public class MyOtherClass {

  @Bean
  MyClass myClass;

}

Notice how you can chain dependencies:

@EActivity
public class MyActivity extends Activity {

  @Bean
  MyOtherClass myOtherClass;

}

You always get a new instance when you use @Bean on a field, unless that bean is a Singleton (see Scopes below).

Method based injection

Since AndroidAnnotations 4.0.0

@EActivity
public class MyActivity extends Activity {

  @Bean
  void setOneBean(MyClass myClass){
    // do something with myClass
  }
  
   void setMultipleBeans(@Bean MyClass myClass, @Bean MyOtherClass myOtherClass){
    // do something with myClass and myOtherClass
  }

}

It is worth noting that the generated subclasses are final, which means that you can't extend generated classes. However, you can extends original classes and generated code will be also generated for the child class. This works for every annotations.

For example the following class will also have myOtherClass injected :

@EActivity
public class MyChildActivity extends MyActivity {

}

Injecting implementations in assignable fields

Since AndroidAnnotations 2.5

If you want to use a dependency in your code through its API (either a superclass or an interface), you can specify the implementation class as the value parameter of the @Bean annotation. In latter case you must annotate the implementation class and not the interface.

@EActivity
public class MyActivity extends Activity {

    /* A MyImplementation instance will be injected. 
     * MyImplementation must be annotated with @EBean and implement MyInterface.
     */
    @Bean(MyImplementation.class)
    MyInterface myInterface;

}

Does that provide decoupling advantages ? Well, the class still knows about its dependencies implementation, but at least the code using those dependencies has to rely on the API, which is an interesting goal.

Supported annotations

You can use most AA annotations in an @EBean class:

@EBean
public class MyClass {

  @SystemService
  NotificationManager notificationManager;

  @UiThread
  void updateUI() {

  }

}

View related annotations

You can use view related annotations ([@View](Injecting Views), @Click...) in your @EBean class:

@EBean
public class MyClass {

  @ViewById
  TextView myTextView;

  @Click(R.id.myButton)
  void handleButtonClick() {

  }

}

Notice that this will only work if the root Android component that depends on MyClass is an activity with a layout that contains the needed views. Otherwise, myTextView will be null, and handleButtonClick() will never be called.

Injecting the root context

You can inject the root Android component that depends on your @EBean class, using the @RootContext annotation. Please notice that it only gets injected if the context has the right type.

@EBean
public class MyClass {

  @RootContext
  Context context;

  // Only injected if the root context is an activity
  @RootContext
  Activity activity;

  // Only injected if the root context is a service
  @RootContext
  Service service;

  // Only injected if the root context is an instance of MyActivity
  @RootContext
  MyActivity myActivity;

}

Method based injection

Since AndroidAnnotations 4.0.0

@EBean
public class MyBean {

  @RootContext
  void setContext(Context context){
    // do something with context
  }
  
  @RootContext
  void setService(Service service){
    // do something with service
  }

}

In the MyClass instance referenced by the following activity, the service field of MyClass (see above) will be null.

@EActivity
public class MyActivity extends Activity {

  @Bean
  MyClass myClass;

}

Injecting the root Fragment

Since AndroidAnnotations 4.7.0

Use it on Fragment fields in an @EBean annotated classes to inject the Fragment which originally injected the bean instance.

@EBean
public class MyClass {

  @RootFragment
  Fragment fragment;

  // Only injected if the root fragment implements a given interface
  @RootFragment
  MyInterface fragment;
}

Executing code after dependency injection

When the constructor of your @EBean annotated class is called, it's fields have not been injected yet. If you need to execute code at build time, after dependency injection, you should use the @AfterInject annotation on some methods.

@EBean
public class MyClass {

  @SystemService
  NotificationManager notificationManager;

  @Bean
  MyOtherClass dependency;

  public MyClass() {
    // notificationManager and dependency are null
  }

  @AfterInject
  public void doSomethingAfterInjection() {
    // notificationManager and dependency are set
  }

}

Warning

If the parent and child classes have @AfterViews, @AfterInject or @AfterExtras annotated methods with the same name, the generated code will be buggy. See issue #591 for more details.

Also, while there is a guaranteed order about when we call @AfterViews, -Inject or -Extras annotated methods, there is no guaranteed order for calling each of the methods with the same @AfterXXX annotation (see issue #810).

Details about when the methods with one of those annotations are called you can find here.

Scopes

Since AndroidAnnotations 2.5

  • the default scope: a new bean instance is created each time a bean must be injected
  • the singleton scope: a new instance of the bean is created the first time it is needed. It is then retained and the same instance is always injected.
@EBean(scope = Scope.Singleton)
public class MySingleton {

}

Caution

  • If your bean has a Singleton scope, it will only keep a reference to the application context (using context.getApplicationContext(). That's because it lives longer than any activity / service, so it shouldn't keep a reference to any such Android component, to avoid memory leaks.
  • We also disable view injection and view event binding in @EBean components with Singleton scope, for the exact same reason (views have a reference to the activity, which may therefore create a leak).

Since AndroidAnnotations 4.7

  • the Activity scope: a new instance of the bean is created the first time it is needed in an Activity scope, it is then retained and the same instance is always returned from within the same Activity. Different Activities hold different copies of the bean.
  • the Fragment scope: a new instance of the bean is created the first time it is needed in a Fragment scope, it is then retained and the same instance is always returned from within the same Fragment. Different Fragments hold different copies of the bean.

Using AndroidAnnotations

Questions?

Enjoying AndroidAnnotations

Improving AndroidAnnotations

Extending AndroidAnnotations

Clone this wiki locally