Best Practice - Application

Quote

Base class for those who need to maintain global application state.

  • Application is the start point of your program
  • It's called when the application is starting, before any activity, service, or receiver objects have been created
  • Only Content Provider is started before it

All logic are based in one Application class and few interfaces :

Application

Application is a Singleton that you can get from any Activity or Service with getApplication() method. Also you can cast your getApplicationContext() to Application.

// get inside of Activity or Service
Application app = getApplication();

// get from Contex
Application app = (Application)view.getContext().getApplicationContext();

You can create your own custom application. To do this you need :

  • Create class that extends Application
public class App extends Application {
    // your logic goes here
}
  • Initialize your custom Application in manifest (just add name tag that should matches your Application path)
<!--AndroidManifest.xml-->
<application
    android:name="yourpackage.App"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">

And what it can give to us ?

// ...
Application app = getApplication();

// as Application extends ContextWrapper we can do everything the it can
// (get Assets as well)
AssetManager assets = app.getAssets();

And thats all, all we could do before API 14 without overriding. So if you are supporting old OS versions and need to maintain global application state you will need to create Custom Application

public class App extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // your application starts from here
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        // This is called when the overall system is running low on memory
        // and actively running processes should trim their memory usage
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Called by the system when the device configuration changes while your
        // component is running. Unlike activities Application doesn't restart when
        // a configuration changes
    }


    @Override
    public void onTerminate() {
        super.onTerminate();
        // This method is for use in emulated process environments only.
        // You can simply forget about it because it will never be called on real device
    }
}

From API 14 android guys added few simple observers, so now you don't need to create Custom Application every time

  • Added possibility to set ComponentCallbacks : onConfigurationChanged and onLowMemory methods
  • Added new ComponentCallbacks2 interface: implements ComponentCallbacks and has new onTrimMemory method. It provide us possibility to handle different memory levels change.
  • Note that onLowMemory is not called from API 14. You should only use it as a fallback for older versions, which can be treated the same as onTrimMemory with the ComponentCallbacks2.TRIM_MEMORY_COMPLETE level.
  • Added registerActivityLifecycleCallbacks which allows you to handle state change of each activity in your program
// set ComponentCallbacks with out overriding
app.registerComponentCallbacks(new ComponentCallbacks2() {
    @Override
    public void onConfigurationChanged(Configuration configuration) {
    // determinate Configuration Change
    }

    @Override
    public void onLowMemory() {
    // use it only for older API version
    }

    @Override
    public void onTrimMemory(int level) {
    // Called when the operating system has determined that it is a good
    // time for a process to trim unneeded memory from its process
    }
});

// set ActivityLifecycleCallbacks
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks(){
    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {}

    @Override
    public void onActivityStarted(Activity activity) {}

    @Override
    public void onActivityResumed(Activity activity) {}

    @Override
    public void onActivityPaused(Activity activity) {}

    @Override
    public void onActivityStopped(Activity activity) {}

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {}

    @Override
    public void onActivityDestroyed(Activity activity) {}
});

From API 18 we have one more observer Application.OnProvideAssistDataListener that allows to place into the bundle anything you would like to appear in the Intent.EXTRA_ASSIST_CONTEXT part of the assist Intent

app.registerOnProvideAssistDataListener(new Application.OnProvideAssistDataListener() {
    @Override
    public void onProvideAssistData(Activity activity, Bundle data) {
    // put your changes here
    }
});

Performance & Tips

  • Applications starts before any activity, service, or receiver objects have been created. Use this to initialize your model (http client, database, libraries etc.)

  • There is normally no need to use Application as your static model(hold your objects as class fields). In most situation, static singletons can provide the same functionality in a more modular way. If your singleton needs a global context (for example to register broadcast receivers), the function to retrieve it can be given a Context which internally uses Context.getApplicationContext() when first constructing the singleton.

public class App extends Application {


    @Override
    public void onCreate() {
        super.onCreate();

        // init your model before any activities starts
        // in this way you will always know where your application starts
        initModel();
    }

    private void initModel() {
        Context applicationContext = getApplicationContext();

        // initialize volley http client
        RequestManager.initializeWith(applicationContext);
        ImageManager.initializeWith(applicationContext);

        // initialize your singleton
        Model.INSTANCE.initialize(applicationContext);

        // initialize your preferences
        PreferencesManager.initializeInstance(applicationContext);
    }
}
public enum Model {
    INSTANCE;
    public void initialize(Context context){
        // save your context as field
        // or do whatever you want here..
    }
}
  • Always remember that Application runs on UI thread. Implementation of your onCreate method should be as quick as possible since the time spent in this method directly impacts the performance of starting the first activity, service, or receiver in the process.

  • If you override onCreate method, be sure to call super.onCreate().

  • You can save your static, global data, that can be changed frequently, right before your Application be killed. Lets check the case when you need to save some GPS location statistic, it is really a bad practice to save new location each time when it arrives. This will dry user battery very fast. Also save it once, on some stop collection method (triggered by user or some other condition, like timer interval), is not enough, as your Application can be killed by the system in background, to allocate memory for other foreground system components or applications. So, to be sure that your data will be not lost, we can use onLowMemory and onTrimMemory methods. Every Activity and Service implements ComponentCallbacks interface, just peek the best place to do it. Remember that even after ComponentCallbacks invocation, application may be not killed or be killed with some delay.

// in Application, Activity or Service

ComponentCallbacks2 mComponentCallbacks = new ComponentCallbacks2() {
    @Override
    public void onTrimMemory(int level) {
        if(level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE){
            saveGlobalData();
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    }

    @Override
    public void onLowMemory() {
        // call it here to support old operating systems
        saveGlobalData();
    }

    private void saveGlobalData() {
        // save your stats to database
    }
};
  • You can use onTrimMemory with ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN to handle when your application goes to background
@Override
    public void onTrimMemory(int level) {
    if(level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
        // app in background
    }
}
  • Save context in your onCreate() to have possibility to get it from any where in your project. But note that it will cause a lot of Spaghetti code. This is highly not recommended.
Context context = App.getContext();
public class App extends Application {
    private static Context mContext;
    @Override
    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }
    public static Context getContext() {
        return mContext;
    }
}    
  • In some cases when we need to handle that all our Activities are destroyed (App is off). ActivityLifecycleCallbacks can help you to deal with it:
// set ActivityLifecycleCallbacks
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks(){

    private int mScreensCounter;

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
      mScreensCounter++;
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
       mScreensCounter--;

       if(mScreensCounter == 0) {
          //... Application is Off
       }

       if(mScreensCounter < 0) {
           mScreensCounter = 0;
       }
    }

    //...
});