Back in Part 4 of the series I mentioned that the Activity naturally functions like a controller and not a View. If you examine the hook methods (like onPause, onResume, etc) and how the Activity is created it becomes clear that the Activity isn’t a View. Views should be dumb. Controllers are smart and handle logic. These hooks are all logical points of execution and not related to View behavior. Since writing that post, all my projects have used the approach I outline here. It’s very solid architecture and it doesn’t leave me feeling unsettled like the approach from Part 4. It provides clear separation of MVC parts without the overlap.
Hopefully you’ve read through the previous parts in the series because we’re going to be building off those ideas and making some slight adjustments.
- The Activity will function as a controller.
- The View will subclass either RelativeLayout, LinearLayout, FrameLayout, etc….
- Handlers are not used for passing Events between the parts. We’ll use an EventDispatcher from the Model and simple OnSomethingListeners from the View.
That’s it. It’s really just a couple of simple changes. So let’s get started with the specifics. The example is a timer and you can get the full sources here. (I’m going to leave out a lot of the details that are not in the scope of this post. Feel free to check out the other stuff on your own. There’s some cool stuff in there.)
Our simple example only has 1 activity called MainActivity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | public class MainActivity extends Activity { private AppModel model; private MainView view; private Handler handler; private boolean isTimerRunning = true; private long initTime = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initTime = System.currentTimeMillis(); model = AppModel.getInstance(); view = (MainView)View.inflate(this, R.layout.main, null); view.setViewListener(viewListener); setContentView(view); handler = new Handler(); } @Override protected void onResume() { super.onResume(); timerRun.run(); } @Override protected void onPause() { super.onPause(); handler.removeCallbacks(timerRun); } @Override protected void onDestroy() { super.onDestroy(); view.destroy(); } /** * Simple runnable to update our current time in the model */ private Runnable timerRun = new Runnable() { @Override public void run() { if (isTimerRunning) { long change = System.currentTimeMillis() - initTime; initTime = System.currentTimeMillis(); model.setElapsedTime(model.getElapsedTime() + change); // controller is responsible for updating the model handler.postDelayed(timerRun, 100); } } }; /** * This is how we receive events from the view. * The view takes user actions * The controller/activity responds to user actions */ private MainView.ViewListener viewListener = new MainView.ViewListener() { @Override public void onToggleTimer() { isTimerRunning = !isTimerRunning; view.setPausedState(isTimerRunning); // controller can call method directly on the view if (isTimerRunning) timerRun.run(); } @Override public void onAddTime(long amountToAdd) { model.setElapsedTime(model.getElapsedTime() + amountToAdd); } }; } |
This should all look very familiar. There are only 3 important things I want to point out here:
- On line 14 is where we are creating the View. In this instance I inflate a resource xml layout file. Notice it’s casted to a custom object.
- Lines 15 and 58 are how the View sends events to the Controller. This should be super familiar because this is how we listen to button events and other standard Android events. In our case we’re listening for custom events from our View.
- The controller is allowed to make direct method calls on the View. On line 62 you can see the state of the Stop/Start button is set.
And that’s all the changes for the Activity to be the Controller. The rest of this class just keeps track of a timer and like a good controller, updates the model property. Let’s check out the View.
You’re probably very familiar with creating “views” in xml, but did you know they can be linked to a class? Here’s what main.xml looks like and why in the controller I was able to type cast it to “MainView”:
<com.musselwhizzle.mvc.views.MainView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > |
The layout file points to MainView. Let’s check out that code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | public class MainView extends LinearLayout { /** * The interface to send events from the view to the controller */ public static interface ViewListener { public void onToggleTimer(); public void onAddTime(long amountToAdd); } private static boolean DEBUG = false; private static final String TAG = MainView.class.getSimpleName(); private static final long AMOUNT_TO_ADD = 1234 * 60 * 2; private Digit d1, d2, d3, d4, d5; private Button toggleBtn, addBtn; private AppModel model; /** * The listener reference for sending events */ private ViewListener viewListener; public void setViewListener(ViewListener viewListener) { this.viewListener = viewListener; } /** * Constructor for xml layouts */ public MainView(Context context, AttributeSet attrs) { super(context, attrs); model = AppModel.getInstance(); } /** * Exposed method so the controller can set the button state. */ public void setPausedState(boolean isTimerRunning) { String txt = (isTimerRunning) ? getContext().getString(R.string.stop) : getContext().getString(R.string.start); toggleBtn.setText(txt); } /** * Remove the listener from the model */ public void destroy() { model.removeListener(AppModel.ChangeEvent.ELAPSED_TIME_CHANGED, elapsedTimeListener); } /** * Does the work to update the view when the model changes. */ private void bind() { int milli = (int)Math.floor((model.getElapsedTime() % 1000)); int secs = (int)Math.floor((model.getElapsedTime() / 1000) % 60); int mins = (int)Math.floor((model.getElapsedTime() / 1000 / 60) % 60); if (DEBUG) { Log.i(TAG, "elapsed: " + model.getElapsedTime()); Log.i(TAG, "secs: " + secs); Log.i(TAG, "mins: " + mins); } d1.showTime((int)Math.floor(mins/10)); d2.showTime(mins % 10); d3.showTime((int)Math.floor(secs/10)); d4.showTime(secs % 10); d5.showTime((int)Math.floor(milli/100)); } /** * Find our references to the objects in the xml layout */ @Override protected void onFinishInflate() { super.onFinishInflate(); toggleBtn = (Button)findViewById(R.id.toggleBtn); addBtn = (Button)findViewById(R.id.addTimeBtn); d1 = (Digit)findViewById(R.id.digit1); d2 = (Digit)findViewById(R.id.digit2); d3 = (Digit)findViewById(R.id.digit3); d4 = (Digit)findViewById(R.id.digit4); d5 = (Digit)findViewById(R.id.digit5); DigitObjectPool pool = new DigitObjectPool(getContext(), 10); d1.setPool(pool); d2.setPool(pool); d3.setPool(pool); d4.setPool(pool); d5.setPool(pool); toggleBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewListener.onToggleTimer(); } }); addBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewListener.onAddTime(AMOUNT_TO_ADD); } }); model.addListener(AppModel.ChangeEvent.ELAPSED_TIME_CHANGED, elapsedTimeListener); bind(); } /** * The listener for when the elapsed time property changes on the model */ private EventListener elapsedTimeListener = new EventListener() { @Override public void onEvent(Event event) { bind(); } }; } |
So again there are only a couple of important things (as related to MVC) going on here.
- The view is binding to the elapsed time on the model although this isn’t new.
- Instead of using a Handler to send events we’re just using a standard listener as defined on lines 5 and 21.
Everything else that happens in this class is just View-related stuff to make it pretty. Notice in this case MainView subclasses LinearLayout and gets all the references to the components in the onFinishInflate() method. Alternatively, you don’t have to use an xml resource file and could just create the layout in code in the constructor.
So that’s it! It’s just a couple of simple changes, but it works much better than having the Activity function as a View. A lot of you guys have been asking for this update. I hope this helps and clears up the idea. Happy coding!
Get the sources from here.

24 responses so far ↓
1 Android Architecture: Part 4, The View - Android Developer // Jul 9, 2012 at 2:27 pm
[...] UPDATE #2: Thanks for all the questions, comments, and feedback guys. I’ve finally gotten around to making a blog post about the Activity as a Controller. It’s over here if you want to check it out. [...]
2 Deepak // Jul 17, 2012 at 7:13 am
Thank you ! Your codes teach a lot more than I wish for
3 Antoine Grondin // Jul 21, 2012 at 3:53 am
Thanks for the update! I’m a student and your series introduced me to a clear, understandable example of MVC (I didn’t cover that yet in my curriculum, but needed some architecture knowledge for my personal projects), and your series made just that. I was waiting for this update, as I am since going thru great loops to make the Activity works has Views .
The way I made it so far is to put the Controller in a Service that invoke the Activities, but I feel it is perfectible. Now your new post clarify much, I really appreciate it!
4 Tal Weiss // Jul 25, 2012 at 4:44 am
Thank you for the great post – again!
If I could add to the wish-list of future posts, I would love to see an expanded example with web services (how exactly do they fit in), Fragments (controllers?) and testing (maybe even TDD?).
Thanks again!
5 Fernando Gutiérrez // Jul 30, 2012 at 5:50 pm
Thanks for the article, I was looking forward to it.
I’d also like to know your opinion about what role fragments should play in a MVC architecture.
6 musselwhizzle // Jul 30, 2012 at 6:07 pm
Hi Fernando, Thanks for the comments. I (often) use fragments as controllers.
7 cameo // Sep 24, 2012 at 6:55 am
Hi musselwhizzle,
Thanks for the practical blog series again! I can learn a lot from it. I am looking forward to your opinion on MVP. here is a reference(http://magenic.com/Blog/AnMVPPatternforAndroid.aspx).
BTW, i suggest you to put your code to Github.
8 cameo // Sep 24, 2012 at 6:59 am
I think fragment is quite flexible. It can be used as view and controller depending on the context. I am interested in how fragments communicate with the hosting activity via listeners.
9 Jack Le // Nov 22, 2012 at 9:46 am
Thanks again for so helpfull article serie. This serie teach me alot of things. So now, Im trying to apply this model to my app. So I have some questions and I hope that you can give me some advices or solutions.
When you using this model, did you have some interfaces for view, and controllers. Did you using a base controller like BaseActivity for all activities and if you have what you do with it?
Can one controller has many models and a view can contain other views.
Thanks for reading my comments and sorry for my terrible enlgish.
Best regards,
Jack Le
10 musselwhizzle // Nov 23, 2012 at 2:38 pm
Hi Jack, You’re welcome. To answer your questions
1) Do I use interfaces for views & controllers? Most of the time no. I only use interfaces when it’s needed and a lot of times that isn’t on a view.
2) Do I use a BaseActivity for all my activities and if you have what you do with it? Absolutely! Stuff that goes in there are things like analytics, dialogs, common convenience methods, and common onSave stuff.
3) Can one controller have many models? Yes. Let’s say you have a user model which is used to populate other specific list data to your app (Youtube’s navigation and content for instance). You can have as many model as you need. If you extend the definition of model a little bit there are things like VOs (Value Objects) and POJOs (Plain Old Java Objects). These objects just hold values or properties (or state). In a typical app I might have 15-30 of them.
4) Can a view can contain other views? Sure. You may or may not decide to expose that your view contains subviews. That’s up to you. The Principle of Least Knowledge might suggest not exposing it but it depends. It might not be so bad if you did for views.
Hope that helps!
Cheers
josh
11 Jack Le // Nov 24, 2012 at 9:18 am
Dear Josh
Thanks for clearly answer.
You have helped me alot.
Thank you so much.
jack,
12 Tim Kurtz // Jan 4, 2013 at 9:43 pm
I really learned a lot from your posts on this. I come from the VB world and am trying to migrate to Android.
I’m working on a project which uses an SQLite database to track projects. In this architecture I have one model for Projects and three views:
* Project List
* Project Edit/Add (click the list or menu option to edit or add a record)
* Project Show (click the list to display the project record)
I get the impression that I should have one activity that controls all three views… or should I have one activity for each view?
If I add another model, ProjectVisits with similar views for the visits. Should I:
* have another activity that controls those three views
* have a separate activity for each view
* have one activity that controls every view – Project and ProjectVisits
13 musselwhizzle // Jan 4, 2013 at 11:52 pm
Hi Tim, Generally you have 1 activity per 1 UI screen. I would base the decision upon that. Alternatively, you could do 1 activity and 3 fragments that switch in and out. You would see something like this in combination with the PullMenu/PullSlider menu pattern that’s emerging.
14 Tim Kurtz // Jan 5, 2013 at 12:42 am
That makes sense to me. The earlier post with the different list classes confused me the listcontroller and listadapter and no listactivity.
Is there a reason the earlier example had an app class at the root of the package and this one doesn’t? Is it not needed with only one activity? What’s it’s purpose? I’m thinking it has something to do with clumping activities together like I’m going to be doing and getting an instance of the application which this example didn’t need since it was only one activity.
I really appreciate your blog and comments on this and how fast you responded.
Tim
15 musselwhizzle // Jan 5, 2013 at 1:05 am
The “app” class you’re referring to a subclass of “Application.” It can be used for general setup.
16 Stephen // Jan 20, 2013 at 12:52 am
Great post… it’s really interesting to see MVC applied to Android. I was hoping you could do another post at some point demonstrating the following:
- How web-service calls are integrated into the MVC pattern.
- How exactly you propagate model change events to multiple views in your application.
- Tied to the above, how you store the model data that is shared by multiple views/activities. Do you store it centrally somewhere?
Thanks
17 Tim Kurtz // Jan 29, 2013 at 10:27 pm
This has been a really great series of articles. If I understand this right…I have a database of projects and I want the app to list them, show a particular project the user selected and add a project so based on the Single Responsibility Principle I need a ProjectListActivity, ProjectShowActivity and ProjectAddActivity. ProjectListActivity and associated view classes. And each …Activity will handle calls to the other two as needed and each will handle their menu items. And I don’t need an overall Project Controller activity that calls the others as needed? Is that correct?
18 Tim Kurtz // Jan 29, 2013 at 10:31 pm
Followup, actually ProjectListActivity will redirect to the other two as needed and when one of them are done control will revert to ProjectListActivity?
I come from years of VB programming so this is fairly new to me. I understand the MVC concept it’s the implementation that confuses me sometimes.
19 musselwhizzle // Feb 2, 2013 at 1:20 am
Yup. Sounds like you got the right idea to me. Good luck!
20 Randy // Feb 2, 2013 at 10:22 pm
Thanks for the tutorial. Its been been very helpful.. I like your previous design better for my application. What I don’t like about the new version is the dynamically creating the activities. However, I fairly new to Android developer. But, I have seen large corporation Android code and the initial version makes sense. I know it does not fit nicely into the MVC box, but seems easier to understand and support. What are your thoughts?
21 Luciane // Feb 19, 2013 at 12:57 pm
Hi,
Love your blog and learned a lot here. I have a questions about cursors. I’m developing a app for a tablet and trying to apply mvc following your steps. I have a couple ListActivitys that have ListViews with CursorAdapters. How would you deal with cursors and where would they come in the MVC architecture? Should I avoid using them at all? When I look at it, they should be restricted to the persistance layer but, the ListView is heavily coupled with it. So… Any light on the matter?
Thank you very much… I already have your blog on my RSS reader.
22 musselwhizzle // Feb 20, 2013 at 12:33 am
Hi Luciane, “they should be restricted to the persistance layer but, the ListView is heavily coupled with it”. Yea this is why I don’t use CursorAdapters too often. I instead just parse my data from the DB, Contacts, data provider, whatever, myself in my business layer and create Value Objects. It decouples the Adapter from having to know about the internals of how the data was stored. Would I recommend avoiding CursorAdatpers? No, I think I’d be executed by the Android community if I said that. They certainly have their use. The good news is ListView to Adatper is very decoupled. Any kind of coupling should happen at the concrete instance of the Adapter: MyCursorAdapter, MyArrayAdapter, MyBaseAdapter, etc… These are all polymorphic types but the internals are very concrete (this is okay and good) but the client never sees this. In terms of getting the adapter to the view, sometimes I have my controller activity/fragment aggregate (pass) the Adapter into the View and sometimes I’ll have the View compose (create) it’s own Adapter based upon the data in the model. I hope that helps. Thanks for the comment. Good luck on your project!
23 William // Feb 23, 2013 at 1:41 am
Hi, excellent posts! Read all of them, passed them on to my friends and i’ve been using what i learned here to guide me through my recent projects.
With a specifc one i found myself with a few doubts.
I have an Activity that must be the controller for a ListView that has a list with 2 different types of value, each one with a different layout. The itens to be displayed on the list come from my Model.
Following the sequence that you’ve shown here, my Activity retrieves the Model from the DAO, initializes and inflates my View and registers a ListViewListener on the View.
Now, after the View was inflated and all the View’s controls were found and had their events set to point to the ListViewListener, how should i get the View to access the same Model instance from the Activity and its values without having a Singleton model?
I need this Model on the view, so it can be passed to my CustomListViewAdapter (that needs the model’s values to do the 2 different layout thing) and also to register my View as a Listener to the model.
Sorry for the giant post and if i asked any stupid questions here. I’m new to the MVC on Android.
Thanks for the great posts and for all the help!
24 musselwhizzle // Mar 4, 2013 at 10:46 pm
Hi William,
Thanks for the comment and sorry for the late reply. You can always pass a reference of the model to the view and it’s even more proper MVC to do so than a Singleton. While certainly not necessary Singleton application model does 2 things: 1) it makes it easy to get a reference from anywhere and 2) it makes the data persist in memory even when switching activities until the application is destroyed. I hope that answers your question.
Leave a Comment