Skip to content
Ognyan Bankov edited this page Oct 25, 2016 · 19 revisions

The main building block of a regular Android application is the 'Activity'. The main building block of a Forge application is the Forge unit or just unit for short. You can have plain Activities in your Forge application but in order to use the forge goodies you will have to create an unit.

A unit is consisted of two components:

  • Activity component - that is an Activity that implements 'UnitActivity'. It will be responsible for the presentation
  • Resident component - class that implements 'ResidentComponent'. Forge-android framework ensures that a resident component is created by it's activity, it is paired to it and stays resident even during configuration changes like rotation and it is re-paired to the recreated activity. Resident components contain the business logic.

Forge unit provides several features/benefits:

  • separation between business logic and presentation
  • convenient handling of rotation (and other configuration changes)
  • you no longer need to use AsyncTask because you can execute background tasks in the resident component via Java's standard Executors
  • you no longer need to use onSaveInstanceState() and restore the state in onCreate() because the resident component stays alive all the time and you can use it to store state in it directly, i.e. without using Bundle but just simple POJO.

For a tutorial about how to create a unit please visit Creating your first unit.

Lifecycle of the unit components

Forge applications extend UnitApplication class and use ActivityLifecycleCallbacks to intercept the activity lifecycle transitions like onCreate() and onResume() which are then fed into the UnitManager which takes care to create, manage (pair to an activity) and destroy the resident components.

The activity's lifecycle is the usual.

Resident components have very simple lifecycle: they are created when activity is created for the first time and destroyed when activity is destroyed AND it is finishing (isFinishing() returns true). A resident component stays alive during activity's configuration changes and it is re-paired to the newly created activity by the UnitManager (this happens automatically).

public class ActSimple extends UnitBaseActivity<ResSimple> {
    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        // if you call getResident() here it will return null
        
        super.onCreate(savedInstanceState, persistentState);
        
        // here you can call getResident() and you will receive reference to the resident
    }

As you can see in the above code, after the call to super.onCreate() you may call getResident() and be sure that you receive non-null result.

Resident component's activity lifecycle callbacks

Resident component interface defines 3 callbacks that are related to the activity's lifecycle:

  • onActivityResumed() called when activity is resumed
  • onActivityPaused() called when activity is paused
  • onActivityFinishing() called when activity is destroyed AND it is finishing.

Developers can use these callbacks to start/pause/stop long running tasks that are run in the resident.

Usually you will use just onActivityFinishing() to clean up/close any resources that the resident component had created.

onActivityResumed() and onActivityPaused() are used in rare cases when you have to abandon an operation when activity is paused and restart it after the activity is eventually resumed.

Testability

Testability of the resident components

Resident components should be able to be tested via normal JUnit4 unit tests, i.e. not via androidTest. For that they should not use any android classes (android.* package). If you have android.* imports in your resident component usually that means that there is some problem with your design.

Testability of the unit activities

In order to be able to test the activities more easily it is good practice to split the resident components in two: one interface describing the methods and one class that implements the interface. That way it is easier to mock the resident component using Mockito or other similar methods.

Unit activities should be tested using androidTests. Please note that currently (2016-10) there is a still huge problem with the android unit tests - if you have multiple test (i.e. more than one) if you run them in a batch they will all share one and the same instance of the Application class which will cause exceptions. Another problem is that lifecycle of the activities is ovelaping, i.e. activity from one test is still in onResume() while activity from another test is in onCreate(). That combined with the single Application instance leads to a lot of problems which renders the whole androidTest infrastructure unusable when test are run in a batch. In order androidTests to work you will have to run them manually one by one.

There are also other problems with the androidTest infrastructure which generally seems immature. Let's hope that Google will soon take care for it and make it more usable.