I Do It For the Cookies I Do It For the Cookies I Do It For the Cookies
    • No menu assigned
    svg svg
    I Do It For the Cookies

    By Eisvidas January 7, 2020 In Android

    What is ‘Context’ in Android?

    Every Android developer has come across the word Context. While it is one of the most abused objects out there, it is equally the most mysterious and difficult to understand. Having a big-picture understanding of context is crucial for a good working application and for avoiding those nasty memory leaks.

    WHAT IS CONTEXT

    The exact definition of Context by Android:

    A Context is an “Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc. “

    Remember, everything in Android is a component; views, services, system resources, activities, etc. Context is basically the bridge between these components and the application itself. You have to let these components “know” in which context they are in. This is the reason why you cannot create these components merely by using ‘new’.

    Consider the following Toast:

    Toast.makeText(MainActivity.this, "Hello There! I am a Toast", Toast.LENGTH_SHORT).show();

    The object is basically saying; “Hey, I am trying to make this piece of text here, in which activity should I do it in?” And so we pass MainActivity.this because we are calling the method from MainActivity.

    However, if the method is being called inside a Fragment we have to pass getActivity() instead. A Fragment never extends Context and so we have to get it from the Activity it is attached to.

    WHAT WE CAN DO WITH CONTEXT

    Creating new objects: Creating new views, adapters, listeners…

    TextView tv = new TextView(getContext());
    ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);

    A Context is able to pass the following information that view objects require:

    • A reference to an Activity for event attributes such as onClick.
    • Styled attributes.
    • Associated screen size and dimensions for converting size units (such as sp and dp) to pixels.

    Explicitly starting a component:

    Intent intent = new Intent(getApplicationContext(),SecondActivity.class);
    startActivity(intent);
    
    Intent downloadIntent = new Intent(this, DownloadService.class);
    downloadIntent.setData(Uri.parse(fileUrl));
    startService(downloadIntent);

    When we explicitly start a component, we pass two pieces of information:

    • A Context of the application package implementing the class.
    • The component class that is to be used for the intent.

    Access and use system services:

    context.getSystemService(LAYOUT_INFLATER_SERVICE) 
    getApplicationContext().getSharedPreferences(*name*, *mode*);

    We use Context to fetch a handle to system-level services or retrieve the contents of a preference file.

    Inflating an XML Layout file:

    LayoutInflater inflater = LayoutInflater.from(context);
    inflater.inflate(R.layout.my_layout, parent);

    We provide a Context to obtain a LayoutInflater object which we can then use to pass a layout XML file.

    UNDER THE HOOD

    • Context is an abstract superclass that is at the root of the hierarchy inherited by the ContextWrapper.
    • ContextWrapper is an adapter or proxying implementation of Context which wraps its functions and delegates the calls to another Context.
    • ContextImpl is an implementation of the Context API providing us with the base Context object for Activity and other application components.
    • ContextThemeWrapper is, as the name implies, a theme from an Activity defined in the AndroidManifest.xml file as ‘android:theme’. Service and Application do not require a theme and so they inherit directly from ContextWrapper.

    Here is a simple analogy that may help you to better understand Context:

    Imagine that you are the boss of a company with 100 employees. This is a big environment and so the boss needs a manager or assistant to help him run the company more efficiently, such as quickly getting access to a file or getting someone to make a coffee.

    • The boss is our Android Application.
    • Our Assistant/manager is the Context.
    • The files and coffee are our system resources.

    As the boss of our leading Android App, we can, therefore, call our assistants (Context) when we need to get information about different parts of our application like Activities, Fragments, and application resources.

    Examples of getting context

    • this – Returns the current Context of the Activity which is alive at that moment.
    • Activity.getApplicationContext – Points to an application instance that is non-UI but is long-living. It is tied to the life-cycle of the entire application.
    • ContextWrapper.getBaseContext – Only relevant when used with a ContextWrapper which allows us to modify behavior without changing the original Context.
    • View.getContext – Returns the Context of the view object usually of the currently alive Activity.
    • getActivity – Used with Fragments. It returns the base Activity the Fragment is associated with.

    Generally, it is best practice to use Context provided directly to you from the enclosing component you are working with. The reference can be held safely as long as it does not extend beyond the life-cycle of that component.

    Avoid memory leaks

    A memory leak can occur when the Garbage Collector is unable to release an object from the working memory, even though that object is no longer used by that application. Consequently, an application will consume more resources than needed, which will eventually lead to a fatal OutOfMemoryError exception and therefore discourage users from using your app.

    Here are some common ways memory leaks can occur:

    Background tasks running even after an activity has finished:

    Background tasks such as AsyncTask are given access to an activity but keep on running in the background even when the Activity is destroyed, such as a screen being rotated. The Activity’s instance cannot be Garbage Collected until the AsyncTask is finished which can cause a memory leak.

    new AsyncTask<Void, Void, Void>() {
    	@Override
    	protected Void doInBackground(Void... voids) {
    		try {
    			Thread.sleep(10000); //A Thread runs in the background for 10 seconds...
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    }.execute();

    You may want to consider creating a Java class with a context.getApplicationContext() rather than the conventional way of getting Context like getContext or ‘this’. This approach ensures that we are not merely tied to the Activity but instead the whole application which survives orientation changes. Alternatively, you could put the same code inside a class inheriting AsyncTask and run it this way:

    new myAsyncTask.execute();

    Forgetting to de-register event handlers:

    This is a common problem I see many programmers make. A good example is the use of system services, an object retrieved by a Context, they assist our applications by performing useful background work such as using a sensor. We register this service as an event listener because we want to notify Context that changes are occurring:

    void registerListener() {
           SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
           Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
           sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
    }
    
    View smButton = findViewById(R.id.sm_button);
    smButton.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View v) {
            registerListener();
            nextActivity();
        }
    });

    The above will cause the service to maintain a reference to the associated Activity even if destroyed, causing a memory leak. We, therefore, must remember to unregister the Activity as a listener before it is destroyed:

    private void desregistrarSensor () {
    	if (sensorManager != null && sensor != null) {
    		sensorManager.unregisterListener(this, sensor);
    	}
    }

    Static variables holding a reference to an Activity’s context:

    Another big mistake programmers make is declaring objects such as TextViews or Buttons as static. If the Activity is referenced to these static objects, the Garbage Collector will not clear this up after the Activity is destroyed.

    public class MainActivity extends BaseActivity {
    
        private static Button mButton;
        private static TextView mTextView;

    The solution is quite simple:

    Never use static variables for views or activities or contexts!

    The Inner Class variable is static:

    Inner classes (anonymous) are created inside another class or method and usually hold a reference to the Context of that particular activity. If the Activity is destroyed, there is always the chance that our inner class will retain the reference, resulting in a memory leak.

    private static class NonLeakyThread extends Thread {
    
            TextView view;
            Activity activity;
    
            public NonLeakyThread(TextView view, Activity activity) {
                this.view = view;
                this.activity = activity;
            }

    We can overcome this issue by using weak references. We let the Garbage Collector know that we’re not particularly worried about holding onto this inner class and so we let it go. Rather than passing the Context as a strong reference to the inner class, we pass it as a weak reference one:

     private static class NonLeakyThread extends Thread {
    
            WeakReference<TextView> view; //Tell the Garbage Collector to automaticlaly clear up weak refences
            WeakReference<Activity> activity;
    
            public NonLeakyThread(TextView textView, Activity activityArg) {
                this.view = new WeakReference<>(textView);
                this.activity = new WeakReference<>(activityArg);
            }

    CONCLUSION

    I hope this post has given you a clearer understanding of Context and the ramifications of improper use. By investing time into learning this one simple thing, we can avoid any future frustration and spending endless hours trying to debug our code.

    References:

    • Causes of memory leaks: https://blog.nimbledroid.com/2016/05/23/memory-leaks.html
    • An in-depth look into Context: https://www.freecodecamp.org/news/mastering-android-context-7055c8478a22/
    • Another great post on Context: https://wundermanthompsonmobile.com/2013/06/context/

    Leave a reply Cancel reply

    © 2020 Arrayly

    To Top