Anti-Patterns

Much of this book has been focused on what you should do. In contrast, this chapter is focused on what you should not do.

All platforms have their anti-patterns: things that are technically possible but are not in the best interests of the users of that platform. Android is no exception. Some anti-patterns are simply annoying to users, while other anti-patterns can significantly infringe upon a user’s use of their Android device, or even the user’s freedom.

Much as the Hippocratic Oath directs doctors to “first, do no harm”, Android application developers owe it to the users of their apps to avoid these anti-patterns to the greatest extent possible.

Prerequisites

This chapter assumes that you have read much of the book, particularly the core chapters.

Leak Threads… Or Things Attached to Threads

Leaking a thread means that you start a thread and never cause it to stop. For example, you might start a thread that runs in an infinite loop, doing some work and then sleeping for a while. The problem with infinite loops is that “infinite” is an awfully long time.

All threads should clean up, in a timely fashion, when the component (e.g., activity, service) that started the thread is destroyed — or, in the case of an activity, perhaps just moved into the background.

How you ensure that the thread gets cleaned up is up to you. For threads doing transactional work, such as literally running a database transaction, it may be fine to just let them run to completion and shut down of their own accord. For “infinite” loops, there should be some way to tell the thread that it is no longer needed, such as via an AtomicBoolean flag, or using something more structured than a plain timing loop, such as a ScheduledExecutorService.

Also, bear in mind that you are responsible for threads that are created, on your behalf, by other things that you do. The most common leak scenario here comes with listeners associated with system services, like LocationManager and SensorManager. If you register a LocationListener via requestLocationUpdates() and fail to unregister that listener, you will not only be leaking the listener, but the component associated with that listener, and every system resource tied to that listener, such as any background threads.

The Costs

Threads are intrinsically static in scope. Hence, any object they can reach, directly or indirectly, cannot be garbage-collected while the thread is still running. Hence, if an activity forks a thread, it might do so using an anonymous inner class:


new Thread() {
  public void run() {
    // do something
  }
}).start();

Instances of an inner class — anonymous or otherwise — have an implicit reference back to the object that created them. Hence, the Thread would hold onto the Activity that created the thread, which in turn would hold onto all of its widgets and so forth. None of that can be garbage-collected until after the thread terminates, even if the activity is destroyed.

The Counter-Arguments

I want the thread to keep running even after the activity is destroyed

In this case, the thread should be created and managed by a service, not simply leaked. Not only does this give you an opportunity to clean up the thread when needed, but it also alerts Android that you are still trying to do some work, so Android will not necessarily terminate your process very quickly.

However, be careful about assuming that you can have a thread — even one managed by a service — run forever, as you will see in the next couple of sections.

I do not know when the thread is no longer needed

Then you have a serious design problem.

A common variation on this theme is:

The thread is needed so long as I have an activity in the foreground

This is a bit tricky, as Android does not really expose the concept of applications being in the foreground, just activities.

The safest course of action is to have the thread be managed by a service, then keep track of whether or not you have an activity in the foreground. For example, in onPause() of each activity, use postDelayed() to return control to you after a short delay, and in onResume(), update a timestamp of your last return to the foreground (held in a static data member). When the Runnable for postDelayed() executes, check that timestamp — if it is too old, you know that none of your activities are in the foreground, and you can stop the service, having it stop your thread.

Use Large Heap Unnecessarily

Encountering an OutOfMemoryError certainly sucks. These are caused either by a memory leak or by trying to use more memory than is practical given the device. For example, loading up lots of bitmaps can easily chew up your available heap space.

To some, therefore, android:largeHeap seems to be the perfect solution.

Added in API Level 11, android:largeHeap tells Android to give you a much larger heap size than is normally given to a process. So, instead of having 32MB or 48MB or so of heap, you might have 256MB of heap.

The right solution, in most cases, is to fix the underlying memory problem, not to mask it by requesting an over-sized heap.

The Costs

To you, having hundreds of megabytes of extra heap may be a blessing. To the user, it may be a curse. That memory has to come from somewhere, and the “somewhere” is from other processes. Your app will force other apps’ processes to be terminated far more quickly than normal, which may slow the user down when she tries to switch between your app and others. Your app may even materially harm the functionality of other apps, who have their processes terminated before they can finish their work, just to satisfy your memory craving.

Bear in mind that Android does not employ swap space (the Linux equivalent of a Windows pagefile). Hence, whereas Windows can allocate lots of memory and slows down as it goes, Android is far more limited, in accordance with its mobile roots.

Furthermore, in many cases, adding more heap space does not eliminate the problem, any more than spraying air freshener gets rid of the dead cat in your living room that is causing the odor. With a memory leak, for example, all the larger heap does is increase the time before you eventually run out of memory.

The Counter-Arguments

I really need to be able to manipulate large chunks of memory

There are certainly apps for which android:largeHeap is justified, such as complex data editors, such as image editors, video editors, etc.

Hence, in practice, the real anti-pattern is not using android:largeHeap, but rather in doing so for apps where the user would not feel that the resulting effects are justified. For example, neither a Twitter client, nor a banking app, should need a large heap, even if the developer is running into memory management issues.

Android makes it too hard to manage memory, so I need a large heap

There is no question that developing mobile applications is challenging, particularly when it comes to memory management. That is not unique to Android — embedded systems developers are used to writing apps where the heap size is better measured in KB instead of MB, for example.

Outside of bitmaps and massive data sets, though, it is a bit difficult to actually run out of memory. While a TextView may take up 1KB of heap space, it takes a lot of TextView widgets to chew through a 48MB heap.

The reason why bitmaps tend to trip up developers is that Android makes using them too easy. For example, it is simple to set a bitmap as a background of some container like a LinearLayout, where developers then blindly ignore the fact that if the bitmap is not precisely the size of the container, Android will need to scale the image, consuming more heap space.

Misuse the MENU Button

The MENU button on Android devices is designed to display either the options menu (on Android 1.x/2.x devices that are not using an action bar backport or the action bar overflow menu).

The MENU button is not designed for any other purpose. Some developers have taken to using it for arbitrary aims, and that is a mistake.

The Costs

The MENU button does not exist on many Android devices. In particular, devices designed for Android 3.0 and higher do not need a MENU button. Some will have them, but most will not. Hence, anything that requires the MENU button will simply be unavailable on those devices.

And, as of Android 4.4, Google is putting increasing pressure on device manufacturers to dump the MENU button, making it less likely to appear in the future.

The Counter-Arguments

Well, if I keep targetSdkVersion below 11, I can have a soft MENU button

This is true, insofar as a menu affordance will be added to the system bar or navigation bar on devices that lack a dedicated MENU button.

Whether the user is expecting to use this button is another thing entirely.

As more and more users run Android 3.0+ devices, they will use more and more apps that have android:targetSdkVersion set to 11 or higher. The remaining handful of apps that do not will be “weird”. In particular, they may not notice the menu affordance, as they are not looking for one, or they may not know what it does, as they are not used to needing it.

Moreover, eventually, other things will drive you to want an android:targetSdkVersion higher than 10, as the menu affordance is not the only feature driven by this value. The sooner you can remove your dependence on a menu affordance, the sooner you can upgrade your android:targetSdkVersion to solve other problems that you are encountering.

I think the action bar is ugly, a waste of space, or otherwise bad

That’s nice. It does not mean that you need a menu affordance and a tie to a MENU button.

For example, well-written games will have a menu integrated into the game UI itself. This was often done even before Android 3.0, since the options menu UI would not look much like the game’s UI, and the developer wanted a consistent look-and-feel.

So long as the user recognizes how to reach the menu (e.g., a three-dots or three-bars icon), the menu does not have to be driven by Android, but instead could be handled by your app directly. You can see this in the Google Navigation app, which avoids an action bar but still displays its own menu from its own on-screen menu affordance.

Interfere with Navigation

Some developers try to take over the device. They attempt to block the use of anything not related to their app: the HOME key, the recent tasks list, the notification drawer, etc.

Android treats such behavior as malware. Android is designed to keep control of the device in the hands of the user and tries very hard to prevent apps from stealing that control.

The Costs

While there are certain cases where blocking navigation outside the app may seem justified (see the counter-arguments, below), there is simply too much opportunity for malfeasance. Users tend to want to use their devices on their terms, not necessarily the terms of some random developer. Malware authors, in particular, love to learn about script-kiddie hacks that allow them to control a device, and by extension, control the users.

The Counter-Arguments

I am writing a lock screen

No, you are not. You are writing something that you think is a lock screen. Really what you are writing is something that weakens device security… if the app in question is designed to be downloaded and run on arbitrary devices.

Android devices can be rebooted into “safe mode”. Much like the Windows boot option that bears the same name, “safe mode” only runs apps that are part of the system firmware, not any third-party apps.

So, let’s assume that the user installs your “lock screen”. Inevitably, part of the setup of a third-party “lock screen” is to disable any sort of security that is part of the native lock screen, so the user does not have to unlock things twice. Even though your lock screen may implement all sorts of security, all somebody else has to do is reboot the device in safe mode, and they now have complete access to the device, including the ability to uninstall your lock screen. By contrast, the native lock screen is in force even if the device reboots in safe mode.

I am writing a parental control app

Rebooting in safe mode is within the motor-control skills of your average three-year-old child. Hence, the primary limitation is whether or not the child knows how to reboot the device in safe mode, which they can learn from the Internet, friends, etc. And, if the device is really an adult’s device, where the “lock screen” allows access to a subset of child-friendly apps, the real risk is not from the child rebooting the device in safe mode, but from the crook who steals the device rebooting in safe mode.

I am writing a lock screen designed to run on whole-disk-encrypted devices

While whole disk encryption — available on Android 4.0+ — does solve the issue of rebooting in safe mode, bear in mind that users then cannot disable the required password security on the native lock screen, as that is tied into the whole disk encryption process.

I am writing a kiosk app

Here, the term “kiosk app” refers to an app that represents the functionality of a single-purpose device. For example, a restaurant might want to distribute menus to customers in the form of a tablet app; the menu app would be the “kiosk app”.

In this case, the owner of the device is the one trying to lock it down to be single-purpose. That is completely reasonable… except that it runs counter to the behavior of standard consumer builds of Android.

The right solution, in this case, is to create custom firmware for the single-purpose devices. This firmware can set up the kiosk app to be the home screen (thereby blunting the effectiveness of HOME, BACK, etc.), and modifications to the firmware can apply access controls to other aspects of the device (e.g., notifications). Unfortunately, there are few (if any) businesses set up to help create such single-purpose firmware for single-purpose devices.

Use android:sharedUserId

If you are creating more than one application, where those applications should be sharing data, you may be tempted to use android:sharedUserId. This attribute, applied to the root <manifest> element in your manifest, allows two or more apps to share a Linux user account. That will allow these apps full access to the other apps’ files. The limitations are that you must use the same value for sharedUserId and that all such apps must be signed with the same signing key.

However, this is a fairly crude and somewhat risky approach to sharing information between apps. In most cases, you will be better served using any of the structured IPC options within Android, such as remote services and content providers.

The Costs

First, you must make the decision to use android:sharedUserId before you ever ship your app in production. Should you change the sharedUserId value — or switch from no value to a new value — when your change is installed, the new version of your app will have no rights to access the old version of your app’s files. This is unlikely to turn out well.

Second, it will be up to you to maintain data integrity of these files in the face of simultaneous access from multiple apps. SQLite should handle this for you for your databases, as it is set up to use process-level locking — this is why SQLite can be used as the out-of-the-box database solution for Web frameworks like Rails. However, any other sort of file, including SharedPreferences, will lack that coordination, unless you somehow arrange to do it yourself. And even the SQLite-level coordination has its limits, as one app has no way to know about another app’s changes to the data, except by re-querying the database.

Third, using android:sharedUserId limits your flexibility. You cannot use it with third-party apps. You cannot readily sell one of your apps in your suite, as then it becomes a third-party app and can no longer be signed by the same signing key as are the rest of your apps. Basically, sharedUserId causes multiple separate APKs to behave, in some respects, as one larger APK.

The Counter-Arguments

I need to ensure only my apps can share the data, not others

Use a signature-level permission. This gives you the same level of security as does android:sharedUserId without most of the risks.

Writing IPC code is tedious

So is writing cross-process data integrity code.

Implement a “Quit” Button

Perhaps the most contentious question and answer on Stack Overflow’s android tag is “Quitting an application - is that frowned upon?”. This exchange is nearly three years old (as of the time of this writing), yet the answer receives both upvotes (and a few downvotes) with some regularity.

Other Android experts, such as Reto Meier, have weighed in on the issue and have offered similar recommendations – that is, do not have a “quit” or “exit” button in your app.

(here, “button” is shorthand for any command-style interface, and includes menu options, action bar items, and the like by extension)

The reason is simple: whatever your “quit” or “exit” button does should be happening in other conditions as well, and handling those other conditions should eliminate the need for the button.

If the app moves into the background for any reason, you need to treat the user and her device with respect. This means stopping background threads that are not needed, releasing system resources like the GPS radio (immediately or after a modest delay), and the like. The user should not need to “quit” your app to accomplish this, because your app will move to the background for other reasons, such as incoming phone calls, or the user pressing the HOME button.

The Costs

You might think “well, what’s the harm in having the ‘quit’ button that, say, just calls finish()?”

First, rarely is it that simple. Calling finish() will return the user to the previous activity, and so for any multi-activity app, there will be scenarios where finish() is not really “quit”. The only simple thing you can universally do is have “quit” bring up the home screen, in which case all you have done is waste screen real estate duplicating the HOME button functionality. Worse, the developer might say “oh, well, I will just terminate my process when they press ‘quit’”, and that anti-pattern is coming up next in this chapter.

Second, the user will start to think that they need to press “quit”, or else bad things might happen. They will see an explicit “quit” option and start to wonder “well, gee, when am I supposed to press that, and what happens if I do not?” This, in turn, will lead to the user going out of their way to make sure to press your “quit” button, even if doing so does not actually change anything about the behavior of your app, courtesy of the placebo effect.

The Counter-Arguments

I need to let the user log out of the app, so I need a “quit” button

No, you need a “logout” button that clears your cached authentication credentials (e.g., sets a static data member to null), then brings up the login activity using FLAG_ACTIVITY_SINGLE_TOP and FLAG_ACTIVITY_CLEAR_TOP to wipe out all other activities in your process. And, probably, you need to have some sort of inactivity-based “timeout” that also logs out the user (e.g., sets that static data member to null).

I am running stuff in the background, so I need a “quit” button

No, you need a “stop that background stuff” button, preferably with a shorter, more specific label. And, you need that to also be available from the Notification that you are using with your foreground service, where applicable.

Terminate Your Process

Closely related to the above anti-pattern is to forcibly stop your process, such as via System.exit(), Runtime.exit(), Process#killProcess(), and so forth. These are often used in concert with an in-app “quit” button, or sometimes for other reasons (e.g., could not figure out how to handle an exception gracefully).

The Costs

Simply put, Google has warned, repeatedly, that there may be side effects from terminating your own process, rather than having Android do proper cleanup first.

The Counter-Arguments

I am using a C library that is buggy, so I need to terminate my process

Fix the bugs in the library. For example, C libraries that rely too heavily on global variables may need to be adjusted to use session handles that get passed around.

Well, it is not my C library, but one from a third party, so I need to terminate my process

Find a library that is Android-compatible, then. It is likely that you will encounter other problems with this library, if it is not designed to work on Android (e.g., not set up to work properly on ARM CPUs).

There is a bug in Android for which I have found no workaround short of terminating my process

This is one of the few legitimate reasons for terminating a process, but it is so rare that it is difficult to find a citation of a place where such a bug (and workaround) exists.

I need to do something from my top-level exception handler!

Set relevant static data members to null, then start up your launcher activity, using FLAG_ACTIVITY_SINGLE_TOP and FLAG_ACTIVITY_CLEAR_TOP to wipe out all other activities in your task. This should reset you to your original state, as if the user had launched the app.

Try to Hide from the User

Some developers view the user as the enemy. These developers try to insulate their app from the user, to make data inaccessible to the user, to make the app “unkillable” by the user, etc. In many cases, this is at the behest of some enterprise, wanting to exert control over the user’s use of the app or even the device.

Android is a consumer operating system. It is designed to put power in the hands of whoever is holding the device and can authenticate themselves to the device (e.g., via a password on the lock screen). Enterprises and malware authors have much the same interests: they wish to take control away from the user and give the control to somebody else. Android defends against malware; enterprises get caught in the crossfire.

Inevitably, the right solution here will be an enterprise remix of Android, designed to be loaded on enterprise-supplied devices, that put the control in the hands of the enterprise.

The Costs

Simply put, you are wasting your time, which could be better spent on other pursuits.

With respect to data, if your app can access that data, by definition, a sufficiently talented user can get at the data:

With respect to the process, the user can force-stop any installed app via the Settings app. And, even if you use script-kiddie tricks to try to prevent access to Settings, the user can nuke your app from orbit via the command line, using the full Android SDK or third-party tools.

The Counter-Arguments

I am creating an app for an enterprise, and we need to control the app

Then you further need to control the device, which leads to the “enterprise flavor of Android” solution mentioned earlier in this section.

I am creating a lock screen/parental control app/kiosk app

Please see the counter-arguments for “Interfering with Navigation” from earlier in this chapter.

Use Multiple Processes

Some Android professionals recommend the use of android:process to have components run in separate processes from the main one for an application. For example, you might have all of your activities in the main process but isolate a service in a separate process. Or, you might have some memory-intensive activity (e.g., an image editor) run in a separate process.

As with most of these anti-patterns, while the android:process feature is valid, it is rarely necessary. To some extent, developers get caught up in process isolation from its use on servers and forget that mobile devices typically have fewer resources — RAM and CPU — than do their server counterparts. Few of Google’s apps use android:process; even complex apps like Gmail or the original Browser avoid it.

The Costs

Each process gets its own heap space, cutting into the heap available for other applications. As with the large-heap anti-pattern discussed above, this will tend to force other apps to be ejected from memory sooner than normal, with commensurate impacts on user experience.

Inter-process communication (IPC) is not cheap, compared with normal method invocation within a process. Hence, tightly-coupled processes will chew through more CPU than their single-process counterparts. While it is unlikely that you will see major performance implications (unless you are doing a preposterous amount of IPC), this will consume more battery than is otherwise warranted.

The Counter-Arguments

I am using a C library that is buggy, and you told me not to terminate my process

As noted earlier, fix the bugs in the library.

Hello? It is not my C library, but one from a third party!

Find a library that is Android-compatible, then.

I need more heap space

On Android 3.0 and higher, android:largeHeap is available, though its misuse is another anti-pattern, discussed above. However, prior to Android 3.0, android:largeHeap was not an option. One workaround used by some apps is to fork several processes, thereby getting several “small” heap allocations (e.g., 32MB) instead of just one.

In cases where android:largeHeap is indeed justified, using multiple processes as a workaround on older Android versions is justified as well. However, bear in mind that IPC overhead is non-trivial, so have a plan to dump the multiple processes and use android:largeHeap once you drop support for Android 1.x/2.x.

I want my UI not to freeze when doing background work

Use threads, not processes, for this.

Hog System Resources

Some of these anti-patterns, like the multiple-process one just now, are really concrete sub-types of a more general anti-pattern: assuming yours is the only app running on the device. While your app may be the only one running in the foreground (assuming that you actually are in the foreground), there are other apps in the background, and ones that soon will come to the foreground. You need to “play nice” and ensure that these other apps will have their fair share of system resources.

One example is open files on external storage. For some devices — but not all – there is a limit of 1,024 simultaneously open files. In principle, that should be plenty. However, if some app — maybe yours? — opens a whole bunch of files, it is possible that other apps trying to access external storage at that point will crash because the limit was hit.

The Counter-Arguments

Um, well, I’m just more important than those other developers
:facepalm::