Context and Fragmentation: Two things I learned in early Android development

By Sean Kollipara

Introduction

This past summer, the Cogent mobility team decided to switch from a cross-platform development toolkit to the native iOS and Android SDKs.  Our previous toolkit was based on Javascript, HTML, and CSS.  My years of web development experience put me in a great position to work with a toolkit like that.  However, our switch to native development meant a dive into the world of Java that I had never taken before.  Five months have since passed, and I have learned a lot about the Android platform and Java.  This blog post intends to cover a couple topics that I think will be helpful to individuals who are new to native Android development.

Context

One of the big issues that I had in the beginning was grasping the concept of context. In Java, context refers to the conditions under which code is executing and how those conditions might have an effect on the execution of the code.  Truly, it is not that far off from the plain dictionary definition of the word. In Android development, context is widely used when creating objects or retrieving system resources.  Creating a dialog box, getting the shared preferences, or creating an intent are all examples of actions that require context.  Many objects in the Android SDK are also contexts.  The biggest one is an Activity.  Another example is a Service. In code, context is passed as a parameter. When writing code in an Activity (or any other object that is considered a context), developers can simply use the this keyword to refer to the current object as context.  However, when writing code in objects that do not have their own context, the context of the parent object should be passed as a parameter in the constructor. For example, an AsyncTask—a class which is used to do work on a background thread—does not have its own context.  So, when the developer needs to do something in a background thread but requires context, the this keyword cannot be used like it can be in an Activity or a Service.  The solution here is to extend AsyncTask, creating an object that has context as one of its member variables.  Then, pass context as a parameter in the constructor and set the member variable equal to the parameter.  That way, when calling the custom AsyncTask from within an Activity, you can do something like new MyAsyncTask(this).  Within MyAsyncTask, you now have the Activity’s context available for use. One of the contexts available is the application context, which can usually be retrieved using getApplicationContext(). This is often a convenient way to get context when you find yourself writing code in a class that isn’t considered to be its own context.  However, in most cases, it is incorrect to use the application context because it leads to excessive memory usage by your app.  Instead, find a way to pass the current Activity’s context between your classes.

Fragmentation

It’s no secret that Android has a fragmentation problem.  This is the phenomenon by which the Android user base is spread across a variety of versions of the OS.  A quick look at the Android developer dashboards illustrates that there are many versions of Android in use on a regular basis.  This can quickly become a developer’s nightmare. Being in the enterprise mobility space, we need to target as close to 100% of Android devices as we can.  This means that we have no choice but to tackle the fragmentation issue.  The dashboards show that Gingerbread, a version of Android that was released almost three years ago, still accounts for 24% of the Android userbase.  The issue here is that many of the features available in the later API levels are not available in Gingerbread.  Perhaps the biggest difference is the introduction of the ActionBar and Fragment classes in Android 3.0.  (It should be noted that, as ironic as it is, the Fragment class is unrelated to the fragmentation problem.) Luckily, there are some tools out there to help address this issue.  The first tool is a set of support libraries, which can be downloaded through the Android SDK Manager.  There are three versions (4, 7, and 13) of the support library, where the version number corresponds to the API level with which the library was released.  When dealing specifically with Gingerbread, the most useful support library is v4, which backports features like the ActionBar and Fragments.  There is, however, a problem with the support libraries: it leaves the developer to implement either the native class or the support class based on the version that the device is running.  This makes for a lot of ugly conditional code. Enter ActionBarSherlock—often called ABS—which is the second tool that helps solve the fragmentation issue.  ABS is a set of classes that serve as a wrapper around the native classes and the support classes.  It takes care of all of that ugly conditional code (mentioned at the end of the previous paragragh) for you.  When creating your activities, simply use the ABS classes rather than the ones in the API (ex: extend the SherlockActivity class instead of the Activity class).  If you are adding ABS to an existing project, be sure to import its classes rather than the Android API classes.  For example, your project will not compile if an activity extends SherlockActivity but imports android.view.Menu instead of com.actionbarsherlock.view.Menu. Finally, we can target specific code to specific versions of Android using some built-in constants along with conditionals.  This is useful when you want some code to run only on specific versions of Android.  I had to use this in one of our apps when I encountered a bug that was introduced in Android 4.0 and fixed in Android 4.4. I found some code that fixed the bug in a hacky kind of way, so I only wanted it to execute on the versions of Android that had the bug.  The code was set up like this: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) { // … // hacky code that fixes the bug // … } Another use case for targeting code to specific versions of Android is when functionality changes between versions.  KitKat introduces a number of techniques to reduce battery consumption.  One specific change is the timing at which Alarms are broadcast.  In Jelly Bean and older, alarms were broadcast at the exact time for which they were set.  Beginning in KitKat, alarms are broadcast sometime near (but not exactly at) the time for which they are set.  So, if you want to set an alarm for an exact time on both KitKat and pre-KitKat versions, you need to target slightly different code to different versions: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent); } else { alarmManager.set(AlarmManager.RTC_WAKEUP, time, pendingIntent); }

Closing

Context and fragmentation can cause a headache for those new to Android development.  They certainly caused me some trouble, and a few Google searches show that others have similar experiences when they are new to Android development. I hope that this post has helped to clarify these problems and assist developers in solving them.  If you have any questions, suggestions, or tips of your own, feel free to leave them in the comments. kk-hero           Happy coding!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s