Recently I’ve spent time trying to answer questions from students and Stack Overflow users. I have found many questions about what to put in an Activity, how to get access to a Context when you need one, how to do some async thing on the UI thread, why use Fragments, etc. Each question is basically “how do I do this thing that Android makes difficult?”. Inevitably most answers by others provide hacky solutions that, while they technically work, should be ignored. These questions represent a fundamental misunderstanding about one or more class within the Android framework. The right answer to these questions is usually “you’re misunderstanding what this class is used for so you shouldn’t be doing this thing in the first place.” I’ve even found entire libraries trying to solve problems which are only problems because a class within the Android framework is being misused. I’m reminded of this infograph for Robospice. The fundamental assumption of Robospice is that “AsyncTasks have one big trouble: they are poorly tied to the Activity lifecycle.” While that is true that AsyncTasks are poorly tied to the Activity lifecycle, it’s not a problem, it’s a feature. Asynchronous anything shouldn’t be tied to the Activity or its lifecycle. This library exists so that developers can misuse the Activity class. I’ve tried to find a unifying answer to these types of misunderstandings but so far I’ve been unable. Then this week I re-read the chapter from Clean Code by Robert Martin about naming conventions. When I got to the Add Meaningful Context
section something clicked with me. Everything that we need to understand some fundamental Android components are right there in their names. So let’s talk about processes, threads, applications, activities, tasks, fragments, and contexts and get a better understanding of the single responsibility of each of these components. This will help us understand when to use certain classes and why you may be misusing them.
Process
In the section “Use Solution Domain Names” Martin writes:
Remember that the people who read your code will be programmers. So go ahead and use computer science terms, algorithm names, pattern names, math terms, and so forth.
This is exactly how the Process
got its name. Process
in Android is nothing more than a general computing process. It is something that the computer cares about. It is certainly not a concern of the user, and rarely your concern as a developer. If you are thinking about the process, relying on multiple processes, or communications between processes, you are thinking about how the computer works and what it is doing. On Android, each application will get started in its own Linux process. When the system needs to reclaim memory, it ultimately decides to kill a process. The five states of a process are foreground, visible, service, background, and empty. So a Process is little more than a set of instructions and memory space, and can be destroyed as a single unit by the system.
Task
In the section “Use Problem Domain Names”, Martin writes:
When there is no “programmer-eese” for what you’re doing, use the name from the problem domain. At least the programmer who maintains your code can ask a domain expert what it means….. The code that has more to do with prime domain concepts should have names from from the problem domain.”
What is the fundamental problem Android devices solve? They help people get things done. That’s incredibly simplistic but think back to what people were envisioning when smart phones were invented. The shear possibilities of what users might be able to do one day was unknown. But whatever tools developers design they all essentially enabled humans to do stuff. This is a task. Task is a human-centric term. It’s a thing that contains an unknown number of steps but has a central theme. What are some examples of tasks from every day life? Clean the house, drive to work, shop at the grocery store. These are all tasks. People may be juggling any number of tasks in their life, but essentially a person is only ever working on one task at a time. A person can start and stop tasks. A person can switch between tasks. This is how Android works. A user starts a Task
when he clicks a launcher icon. A user can view his recent tasks in the recent tasks list. A user can pause and restart tasks. He can even stop a task entirely by dismissing it from the recent tasks list. But importantly the user is only ever working within one task at a time.
Activity
Just like task, activity is a term pulled from the problem domain. Each task is made up of more than one activity. While a person may be working on a task, he does so by completing individual activities. What are some activities within the task of cleaning the house? A person may be currently folding the laundry. They may finish folding the laundry and start cleaning the shower. A person can pause an activity before he finishes it and transition to another one within the same task. When a person transitions tasks he inherent stops one activity as part of the first task and start another activity in the second task. Transitioning from the task of cleaning the house to the task of driving to work inherently involves stopping the activity of folding the laundry and starting the activity of walking to the car. While a person may move between activities, he can only ever be working on one activity at a time. No one folds the laundry and scrubs the toilet at the same time, as much as that would be entertaining to watch someone try. A person moves through activities linearly even if he jumps back and forth. Android implements this exact analogy. An Activity
is a user-facing concept. It describes something that a user is doing. A Task
contains one or more Activities
, but a user is interacting with one and only one Activity
at a time.++ This Activity
takes up their entire focus by taking up the entire screen. A user can start and stop Activities
as he moves through the Task
. The Task
remembers which Activities
the user was previously viewing and makes it easy to return to each one though the backstack. As a user-facing concept, an Activity
is responsible for showing stuff to the user and responding to his input. Because the Activity
is a user-facing component, it should in no way be involved with “behind the scenes” operations. These operations include reading/writing to/from the database, handling web requests, or doing long-running computations. All of these should be completely decoupled from the Activity
and removed from the purview of the user. The only time the computer intervenes in the lifecycle of an Activity is when something changes which directly affects the user. These are called device configuration changes. The best known example is of course device rotation. This helps us understand why trying to preserve AsyncTasks
across rotation is a bad idea. Robospice looks like a great tool for the developer who was previously trying to start an AsyncTask
from an Activity
. However, he should not have been doing this in the first place. There are cleaner architecting patterns that handle these situations.
Fragment
At some point an Activity
becomes too big and needs to be broken down into smaller pieces. We don’t immediately have a term for this. Again these “parts of activities” are user facing so we shouldn’t necessarily expect a good solution-domain name. We might come up with names like “sub activity” or “component” or “part” or “partial activity”. But this strays into “Avoid Disinformation” territory. Martin says:
Programmers must avoid leaving false clues that obscure the meaning of code. We should avoid words whose entrenched meanings vary from our intended meaning.
A “sub activity” would easily be confused with a subclass of an Activity
. “Component” and “part” are too vague. “Partial activity” implies that one partial activity is incomplete; that we need more than one to construct an activity. If I had to guess, the internal debate about what to call this thing was some of the most intense when designing the framework. The original developers settled on the “fragment” and I honestly can’t think of a better word. Like activities, fragments are still about getting things done, just on a smaller scale. Activities like scrubbing the shower and mopping the kitchen both start with the smaller fragment of gathering cleaning supplies. So the gather cleaning supplies fragment can be reused in different activities, without having to know anything about mopping the kitchen or scrubbing the shower. But if an activity such as water the plants is small enough then it doesn’t need to be broken into fragments. Some fragments are small enough, though that they can be performed at the same time. Wiping down the shower tile and wiping down the shower glass while separate fragments, can still be done at the same time. When a person stops one task or one activity within a task, they inherently stop a fragment too. A person may move between several fragments in an activity without stopping the activity itself. For example, a person may stop gathering cleaning supplies and start putting water in the mop bucket all without stopping the activity of mopping the floor. A Fragment
in Android works just the same way. An Activity
contains zero or more Fragments
. Two Activities
can contain separate instances of the same Fragment
. If a Fragment
is small enough and closely correlates with another Fragment
, they may be displayed at the same time, for example using the master/detail design pattern. A user can navigate through Fragments
in any order. If a developer chooses, an Activity
can allow the user to navigate back through Fragments
with the back button. When when a user stops or pauses an Activity
, that Activity
in turns stops or pauses its Fragments
. Like an Activity
, a Fragment
is a user-facing concept. As such it should not be starting asynchronous tasks, interacting directly with the database or web services. It is also going to be affected by changes in the device configuration and have lifecycle methods to handle these changes.
Thread
Thread, like process, is a solution-domain name. Just like a process, a thread is nothing more than an implementation of a basic computer science concept. Android kept things simple and relied directly on Java’s Thread
. Most threads in Android are Java Thread
s. They work so well that Android only needs to subclass Thread
once, to create a HandlerThread
. Like process, thread is a computer-facing concept. Using threads is a way of doing multiple things at once. Just like the conceptual threads in computer science, multiple Thread
instances can exist within one Process
on Android. The user should never care about what is happening on which thread, or how many threads there are in the first place. Developers only care about threads as a way of talking with the computer and asking it do work faster. Because the developer is talking with a computer, he has to think like a computer. This means that threading concerns are relatively difficult to understand and debug. They require thinking in computer-time, not human-time and thinking about memory access and possibly limiting which component can update any memory location at any given time. One of the threads, the “main” or “UI” thread is responsible for listening to and communicating with the user. This thread should not be doing any “behind the scenes” operations. All activities live in this thread and do all of their work there. Each application will start in one thread and it is up to the the developer to decide if he needs more threads. HandlerThread
s are a great option to perform “behind the scenes” operations.
Application
Like process and thread, application is a solution-domain name and is a basic computer science concept. An application is a piece of software that helps a user do some task(s). An application is basically the entire collection of the code that a developer writes to help users perform tasks. However application has become a concept that is equally recognized by users and developers. Therefore it has some user-facing concerns, but not always. The bank and supermarket are examples of physical applications. They were created by others to help people get stuff done. At the supermarket, there are the parts that people directly interact. But there are also the “behind the schemes” parts that help the supermarket help people, like the warehouse and accounting office. Several applications can be combined in a single task to greatly extend what a person can get done, even if the applications were built by different people. If a person is working on the task of baking a cake, he can travel to the supermarket to perform the activity of getting supplies, then return to his own house and continue the rest of the baking task. As long as somebody is interacting with an application, it exists. If a person is at home baking a cake, he can feel confident that his house exists.** Part of starting a task is starting an application. Starting the task of cleaning the house, implies that you’ve already built a home. When an application is no longer used by anyone it closes down. When a person is finished going to the supermarket, and so is everyone else, the supermarket closes up. When a person is finished with all the tasks in his home, he shuts everything down. An Application
in Android works the same way. As part of starting a new Task
, and thus a new Activity
, Android will start an Application
. Normally, a developer will create only one Application
for all of his Activities
. If a user is finished with all of the Activities
, Android may shut down and destroy the Application
. During a Task
, a user can transition to another Application
to help him accomplish additional Activities
. From a camera Application
, a user can start an email Application
to send an email with picture attachment. These two Applications
can be build by different developers but exist within the same Task. If a user force-stops the task while in the email application, the system will stop the Activity
in the email application. If the user instead pauses and resumes the task, the system will immediately restart the Activity
in the email application. If a user launches the email application directly, the system will start a new task. The Activities
from the email application previously started in the camera task are unaffected by any started directly started from the email task. In other words two instances of the same Activity
can live in two different tasks. Applications
may exist even if the user isn’t interacting with any Activities
. If an Application
is only doing work behind the scenes there may be no Activities
present, but the Application
will live on.
Context
Dave Smith has a great post about the different types of Contexts
and what they can and can’t do. But what is a Context
? Simple. A Context
is a context. In our Android Fundamentals course, I describe the Context
as a hook into the operating system. Since the system starts Activities
, you use a Context
to ask the system to start an Activity
. Since the system inflates layouts, you use a Context
to ask the system to inflate a layout. Since the system broadcasts BroadcastIntents
, you use a Context
to send a broadcast or register a BroadcastReceiver
. Since the system provides system services, you use a Context
to get access to system service. But you can’t do all of these things all of the time. If you are inside a Service
, it doesn’t make sense to ask the system to display a dialog or start another Activity
. If you are inside an Application
, the system doesn’t know the current Activity
‘s Theme, so it doesn’t make sense to ask the system to inflate a layout. So when you ask the system to do things, you have to tell the system in what context you wants something done. The system will decide if what you’re asking it to do is appropriate for the context. This is how Context
gets its name. So just like Task
and Activity
, Context
is a problem-domain name. There are many subclasses of Context
but the main ones are Application
, Activity
, and Service
. Each of these components describe different contexts for asking the system to do things. Dave Smith’s article has a great explanation of what each can do. Contexts
are temporary things and they change over time. When dealing with Contexts
, I always say to myself “this is the context in which this class operates and may not be the right context for any other class”. One of the big problems and sources of memory leaks is from passing around a Context
and having other objects hold onto it. If the Context
is an Activity
, that Activity
will live on after it’s lifecycle is over and never be garbage collected. In this case, passing around a Context
is usually a bad idea and something to be avoided. When you do have to pass a Context
, make sure it’s something that is contextual to as many components as possible. That usually means passing an ApplicationContext
. When you are passed a Context
, don’t assume that whoever passed it also thought about your needs. Again don’t assume your method has been given the correct Context
.
public void doSomething(Context context) { // don't assume somebody passed you an Application ...
// mContext = context;
// ... instead always grab the ApplicationContext
mContext = context.getApplicationContext();
...
}
So What?
What is the value in understanding where these words come from? Many questions I get from students are of the form “how do I get X to do Y?” But they first need to ask themselves “should X be doing Y?” In order to answer this question, they need to ask “what is the single responsibility of X?” For all of the Android framework components, this responsibility is directly encoded in the name. This name can be mapped back to either problem or solution domain names. The solution-domain names like process, thread, and application are well understood by developers. The problem-domain terms like task, activity, context, and fragment are only understood when we describe the problem in real-world terms using real-world analogies. So the next time you ask how to do some async thing from an Activity, or how to get a system component via a Context, or what the differences is between process, thread, and task, just look to the name for an answer.
++ This analogy will break down in Android N which allows multiple Activities on the screen at the same time.
** I realize this is a silly statement.
This was my last blog post at Big Nerd Ranch, which I didn’t have time to get the review and polish of my other posts. I still think it’s a valid argument and so I wanted to share it.
Naming convention: I’ve used lowercase italicized words when describing a real physical thing (activity), lowercase non-italicized words when referring to a computer science concept (process), and tic-marked words when referring to an actual Android concept or class (Activity
).
Thanks – this is a good read. You say “Robospice looks like a great tool for the developer who was previously trying to start an AsyncTask from an Activity. However, he should not have been doing this in the first place. There are cleaner architecting patterns that handle these situations.”
What architecting patterns do you recommend instead? I know from a google search there are many possible options. I’d like your opinion.
Never thought about Task, Activity etc. on the lines of naming convention. Pretty interesting.
great read, thank you!
Great article. Thanks!
Really great read. Especially on your explanation of `Context`.
Contexts are so hard to understand on Android and I wish the Android documentation was more clear about what it’s used and it’s purpose – and that all contexts are not equal.
Nice Article
That was good!
Thanks.
Good question pbmorton. The general goal is to have a persistent layer that fragtivities can easily hook up to and ask for data or push new data to. There are many ways to do this. Google has some good architectural patterns to achieve this. You can read more here. You can directly inject the persistence layer into fragtivities like in the MVP example. In that case they inject the repository (persistence layer) into their presenters. An example is AddEditTaskActivity. It’s not important that they are using the presenter pattern. What is important is how the repository makes it to the fragtivity layer. If you are going to use injection, I highly recommend using a D.I. tool like Dagger2. They then use callbacks to return data from the repository to the fragtivities. The problem with the callback patter is (as they rightfully acknowledge) is that you can really only request data synchronously. It would require more work to return data asynchronously. You’d have to manage the list of callbacks as fragtivities are created/destroyed, each hooking up to the repository. You also have to be careful that you don’t leak memory by having your repository hold reference to the callbacks. One way to mitigate this is to use Loaders. Using Loaders is shown here. If you are constrained to using just classes within the Android framework (no 3rd party libs) then Loaders are your best bet. They seamlessly reattach to fragtivities and basically glue the persistent layer to the view** layer. Another pattern uses Eventbus. The persistence layer is still provided to the views** by D.I. However async communication is made through Eventbus. I used this on a side project a while ago and on client projects in the past. You can look at a sample here. My friend Bill Phillips has some good writing on the pitfalls of this approach. As such, I don’t really use this pattern any more. Another approach is to use RxJava. The persistence layer is again provided to the views** by D.I. In this case though the persistence layer supplies data through an observable pipeline. The views** can synchronously post data to the persistence layer and asynchronously receive data through Observables. The key takeaway is that the persistence layer wraps all of these “behind the scenes” components: web service, database, in memory cache and that the views** have access to this wrapper only. You provide this persistence layer to the views** in a way that the views** can live and die all day long and never have a problem. ** By view layer, I am being vague. This could be the presenter + view layer, controller + view layer, or view model + view layer. It doesn’t matter, it’s all the same.
Great post.
I’m relatively new to Android Development and this article really helped me out. It’s really great that the SDK can be understood even without looking in the docs. I think that self-explanatory code is one of the things that distinguishes average programmer from a good one. And “Uncle” Bob Martin does his best to show it to the world of software developers. Thank you for this post.