Thursday , 27 June 2019
AndroidAnnotations: Understanding the bean lifecycle

AndroidAnnotations: Understanding the bean lifecycle

android_thumb_090613.jpg

AndroidAnnotations
eliminates the boilerplate code that is common in Android projects by replacing
it with annotations. Since there is less code to write and maintain, this helps
to speed development and improve maintainability.

In my previous TechRepublic article, I covered the basics of how to set up an AndroidAnnotations project and use general annotations. In this installment, I will cover the core
concepts of the bean lifecycle and explain how to use those concepts to develop
more complex applications.

Lifecycle management

An important concept within any dependency injection framework is
lifecycle management. Understanding the scope of components, as well as how to
hook into any events within that scope, is key to developing more complex
applications. AndroidAnnotations provides two scoping options for @EBean
components: default scope and singleton scope.

The default scope creates a new bean each time the bean is
injected. This is the most common case, as it ensures state-sharing is limited
to the dependent component. The singleton scope is reminiscent of the
well-known Singleton pattern. There is only one @EBean
instance for the scope of the entire App in this case. This scope is useful
when a component is either expensive to create (e.g., requires parsing a
complex configuration file) or requires extensive memory allocation (e.g., a
caching component).

To define the scope of an @EBean as singleton, annotate the
component as follows:

@EBean(scope = Scope.Singleton)
public class CalculatorBean {
  // ... ...
}

Be aware that there are certain injection limitations with a
singleton Bean. For example, injection of views or the application context is
disallowed, as these components have a scope that is more limited than the
singleton scope.

Lifecycle hooks

The @AfterInject annotation allows you to execute code directly
after dependencies are injected into an @EBean. In general, class
initialization logic is performed in the constructor. In cases where
dependencies are necessary to perform that initialization logic, the
@AfterInject annotation is useful.

@EBean
public class CalculatorBean {
  @Bean
  private Calculator calculator;

  @AfterInject
  public void initMemory() {
    // retrieve and set the last memory record into the 
    // calculator bean
  }
	
  // ... ...
}

As you can see in the example above, we initialize the calculator
memory once the calculator bean has been injected.

Similarly, the @AfterViews annotation allows code to be executed
directly after the injection of view components. This is useful when views need
some type of initial setup prior to user interaction.

@EActivity(R.layout.activity_calculator)
public class CalculatorActivity extends Activity {
  // ... ...

  @ViewById Button memoryRecall;

  @Bean CalculatorService calculatorService;
  @Bean Calculator calculator;
	
  @AfterViews
  public void initMemoryRecall() {
    if(calculator.hasMemory()) {
      memoryRecall.getBackground()
        .setColorFilter(Color.BLUE, Mode.MULTIPLY);
    }
  }
	
  // ... ...
}

Notice that we modify the color of the memory recall Button based
on whether any memory data has been retrieved by the CalculatorService.

To deepen your understanding of the @AfterInject and @AfterViews
method, you can review the generated underscore class (e.g.,
CalculatorActivity_.java) to see exactly where the annotated methods are being
invoked.

Method tracing

To see when our lifecycle methods are executed, we can use the
@Trace annotation.  This is also
useful when debugging or tracking down performance bottlenecks by tracing
method execution to determine the location of the root cause.

@EActivity(R.layout.activity_calculator)
public class CalculatorActivity extends Activity {
  private final static String TAG = "Calculator";

  // ... ...

  @Click
  @AfterViews
  @Trace(level = Log.INFO, tag = TAG)
  public void initMemoryRecall() {
    if(calculator.hasMemory()) {
      memoryRecall.getBackground()
        .setColorFilter(Color.BLUE, Mode.MULTIPLY);
    }
  }
	
  // ... ...
}

By adding this annotation at the method level, you will see log
results like the following:

I/CalculatorActivity(  302): Entering [void initMemory() ]
I/CalculatorActivity(  302): Exiting [void initMemory() ], duration in ms: 42

As you can see, a Log level is specified along with a Tag for identifying
the log statement. The duration of the method execution is output by the exit
statement; this provides visibility into which methods are suspects for
performance tuning.

Experiment on your own

Now that you understand the basics of the bean lifecycle, try
your own use cases to experiment on your own. This will help drive a deeper understanding
of the inner workings of the AndroidAnnotations framework.

Leave a Reply

Your email address will not be published. Required fields are marked *

*