8

I want to know, If Android Supports MVC (Model View Controller) structure ? If support then 1. What is Controller ? 2.What is Model ? and 3. What is View ?

Please clear me. I have some confusion about this.

Mohit
  • 2,940
  • 2
  • 24
  • 32
Helal Khan
  • 867
  • 3
  • 10
  • 25
  • 3
    possible duplicate of http://stackoverflow.com/questions/2925054/mvc-pattern-in-android – Mohit Aug 27 '12 at 09:39
  • Check out my blog posts on this starting here [Android Architecture: MV?](http://doridori.github.io/Android-Architecture-MV%3F/) – Dori Apr 03 '15 at 17:33

5 Answers5

24

Which design patterns are used on Android?

Model-View-Control works fine

The actual Activity class doesn't extend android's View class, but it does, however, handle displaying a window to the user and also handle the events of that window (onCreate, onPause etc).

This means, that when you are using a MVC pattern, your controller will actually be a pseudo View-Controller. Since it is handling displaying a window to the user, with the additional view components you have added to it with setContentView, and also handling events for atleast the various activity life cycle events.

In MVC, the controller is supposed to be the main entry point. Which is a bit debatable if this is the case when applying it to android development, since the activity is the natural entry point of most applications.

So, Pseudo MVC in android :

Model = Entities or Classes with main Business Logic

View = layout, resources and widgets like EditText

Controller = Activity , Adaptor

enter image description here

Community
  • 1
  • 1
prayagupa
  • 30,204
  • 14
  • 155
  • 192
5

Model = Content Provider.

Controller = Activity, Fragment or Service.

View = XML layouts.

dsgriffin
  • 66,495
  • 17
  • 137
  • 137
Bartłomiej Mucha
  • 2,762
  • 1
  • 30
  • 36
3

MVC is already implemented in Android

View = layout, resources and built-in classes like Button derived from android.view.View.

Controller = Activity and Fragment

Model = the classes that implement the application logic

Chirag
  • 56,621
  • 29
  • 151
  • 198
1

Not really MVC but becoming more MVC with Room and LiveData

In classical MVC the controller is about decision making, which action to run next. The view reads the data from the model and updates it's own fields.

In Android activities are doing both, they make decisions what action to run in answer of an event AND they set the fields of the layouts. They also read data from the model and wire up widgets. The activities combine the logical tasks of both, the classical controller AND the classical view.

This is why I would not speak of MVC in most cases. There is no clean separation between controller and view. There is a clean separation between Java code and XML resources. This makes sense, as in bigger teams different people are responsible for the visual layout and the programming.

You may still code your own view components and address this part of the code as view. It is only the passiv part of the classical view, while the logic has joined the controller within the activities and fragments. I would not speak of a view but of components or widgets. The more intelligent widgets are, the more logic of the classical view they take up again.

On the other hand, if you apply libraries like Room Android is becoming much more MVC again. Room and LiveData enable views to observe changes in the model all way down to changes in the database. If you cleanly separate view stuff and reduce controllers to decision making, you can structure your architecture in a way, that it really deserves the name MVC again.

Bottom line

It depends on the developer. It is possible to apply real MVC to Android, but it is not the default case.

Blcknx
  • 1,921
  • 1
  • 12
  • 38
0

The primary goal of implementing the MVC pattern is that doing so allows you to then later 'pull out' any one of them and slap in a new one without little or no necessary change to the other two.

Model: All about the data. What is manipulated, what is stored and how.

View: All about the UI or presentation. What is displayed and how.

Controller: The event handler. Dictates when the other two are run in response to events.

In Android, this implementation of MVC has the controller in the form of a class that extends the Activity class. After all, it is this class that initially receives the 'events' that make up the lifecycle of an Android Activity (i.e. onStart(), onCreate(), onSuspend(), onStop(), onResume(), onDestroy). This lifecycle will likely change as Android evolves so it makes sense to represent it as the Controller component of the MVC pattern.

Again, with this MVC implementation, I can pull out any one of the three components and put in essentially a whole new interface (View), or a whole new database (Model), or an new Activity lifecycle (Controller) with no change to the other two. The only necessity is that each component respects the boilerplate template listed below.

In this implementation, the three MVC components are separately represented by three java classes: appView.java, appController.java, appModel.java

When reviewing each class, make note of the member variables, mController, mAppView, and mAppModel, and see how and when they are referenced in each java file. These member variables are 'the hooks' that allow each component to reference each other.

Further, you'll note mAppModel breaks down even further and uses an additional class called dbHelper. This allows you to separate 'what' data is manipulated from 'how' that data is manipulated and stored.

public class appController extends Activity {

    appView mAppView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mAppView = new appView(this);

        mAppView.onCreate(savedInstanceState);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        return mAppView.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        boolean result;

        switch (item.getItemId()) {
            case ....

                return true;

            case ....


                return true;

            default:

                result = mAppView.onOptionsItemSelected(item);
        }

        if ( !result )
            result = super.onOptionsItemSelected(item);

        return result;
    }


    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {

        mAppView.onCreateContextMenu(menu, v, menuInfo);
    }


    @Override
    public boolean onContextItemSelected(MenuItem item) {

        return mAppView.onContextItemSelected(item);
    }

    // When a startActivityForResult() is called
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        mAppView.onActivityResult(requestCode, resultCode, data);
    }


    @Override
    protected void onStop(){
        super.onStop();

        mAppView.onStop();
    }

    @Override
    protected void onRestart(){
        super.onRestart();

        mAppView.onRestart();
    }

    @Override
    protected void onDestroy(){
          super.onDestroy();

          mAppView.onDestroy();

          mAppView = null;
    }

The controller, appController, implements most of not all the 'Activity lifecycle' mehtods and in turn calls methods in the view, appView. That means the standard lifecycle methods, onStop, onResume, onDestroy, etc. are implemented not only in the Controller but also in the View portion of this MVC pattern. You'll see later that that is true in the Model portion as well.

You can see below in the View implementation, appView, the member variable, mController, is used to access Activity methods and yet allows the separation of the Activity (Controller) from the UI (the layout, the menu, etc.).

public class appView {


    private Activity mController;

    private Context mContext;

    private appModel mAppModel;


    public appView(Activity activity) {
        this((Context) activity);

        mController = activity;
    }

    // This can be called when there is not activity available.
    public appView(Context context){

        mContext = context;

        mAppModel = new appModel(this);
    }

    protected void onCreate(Bundle savedInstanceState) {

        mController.setContentView(R.layout.whatever_you_want_activity);

        btnNewToDo = (Button) mController.findViewById(.....

        // The New button.
        btnNewToDo.setOnClickListener(......

        lvToDos = (ListView) mController.findViewById(......

        // One click will edit that selected item.
        lvToDos.setOnItemClickListener(........
    }

    public boolean onCreateOptionsMenu(Menu menu) {

        MenuInflater inflater = mController.getMenuInflater();

        inflater.inflate(R.menu.whatever_you_want_menu, menu);

        return true;
    }


    public boolean onOptionsItemSelected(MenuItem item) {

       ....
    }


    protected void onStop(){

        mAppModel.onStop();
    }

    protected void onRestart(){

        mAppModel.onRestart();
    }

    protected void onDestroy() {

        mController = null;

        mContext = null;

        if(mAppModel != null ){

            mAppModel.onDestroy();

            mAppModel = null;
        }
    }
}

The Model is listed below. See how this class takes in the view and the controller in its constructor. The controller is taken in as type Context and not as an Activity. This allows you to involve any object of type Context and not necessarily an Activity object.

Further, you'll see a helper class, dbHelper, being introduced in the constructor.

public class appModel {

    private appView mAppView;
    private Context mContext;

    // Holds the database helper
    private dbHelper mDBHelper;


    public appModel(appView appView){

        mAppView = appView;

        mContext = mAppView.getContext();

        mDBHelper = new dbHelper(mContext);
    }


    public boolean open() {

        if (mDBHelper == null) return false;

        return mDBHelper.open().isOpen();
    }


    public void close(){

        mDBHelper.close();
    }

    // The App might get destroyed with calling onDestroy, and so close it.
    protected void onStop(){

        // close the db connection...
        close();
    }

    protected void onRestart() {

        // db likely closed.
        open();
    }

    protected void onDestroy(){

        mAppView = null;

        mContext = null;

        mDBHelper.onDestroy();

        mDBHelper = null;
    }
}

As you can see below, SQLite is the database used in this application. However, switch out this helper class, dbHelper, and you can use a completely different database and the other components are none the wiser.

Included below, are some rudimentary methods (open, close, etc.) to give you an idea of the functions performed here. Further, note the method onDestroy() is here that closes the database connection. It is called by the View above which in turn is called by the Controller when it is destroyed.

It is this helper class that knows the names of the fields in the database. With this implementation, the view, the controller and even the Model doesn't really need to know the type of database used or even the field names frankly.

public class dbHelper extends SQLiteOpenHelper {

    private SQLiteDatabase mDB;

    private Context mContext;


    static final String DATABASE_NAME = "whatever";

    static final String DATABASE_FILE = DATABASE_NAME + ".db";

    static final String DBKEY_FIELD = "rowid";

    static final int DATABASE_VERSION = 5;

    // SQL Statement to create a new database.
    private static final String DATABASE_CREATE = "CREATE TABLE IF NOT EXISTS " + DATABASE_NAME
            + "(....  );";

    // SQL statement used to upgrade the database.
    private final String ALTER_TABLE = "ALTER TABLE " + DATABASE_NAME + " ADD COLUMN anewfield VARCHAR;";

    private final String SELECT_ALL = "SELECT " + DBKEY_FIELD + " AS _id, * FROM " + DATABASE_NAME + " ORDER BY somefield ASC";

    private static final String DROP_TABLE = "DROP TABLE IF EXISTS " + DATABASE_NAME;


    public dbHelper(Context controller) {
        super(controller, DATABASE_FILE, null, DATABASE_VERSION);

        mContext = controller;
    }

    // Called when no database exists or if there is a new 'version' indicated.
    @Override
    public void onCreate(SQLiteDatabase db) {

        db.execSQL(DATABASE_CREATE);
    }


    public dbHelper open() {

        try {

            mDB = getWritableDatabase();

        } catch (SQLException ex) {

            if (mDB != null && mDB.isOpen()) {

                mDB.close();
            }

            if (mDB != null) {

                mDB = null;
            }
        }

        return this;
    }


    public boolean isOpen() {

        return mDB != null && mDB.isOpen();
    }


    public void close() {
        super.close();

        // It's good to lose the reference here with the connection closed.
        mDB = null;
    }


    protected void onDestroy() {

        close();

        // Just making sure.
        mDB = null;

        mContext = null;
    }


    public SQLiteDatabase getDatabaseInstance() {

        return mDB;
    }



    private Cursor runQuery(String sqlStmt) {

        Cursor records;

        try {

            records = mDB.rawQuery(sqlStmt, null);

        } catch (RuntimeException ex) {

            // If something goes wrong, return an empty cursor.
            records = new MatrixCursor(new String[]{"empty"});
        }

        return records;
    }


    protected boolean dropTable() {

        boolean dropped;

        try {

            mDB.execSQL("DROP TABLE IF EXISTS " + DATABASE_NAME);

            dropped = true;

        } catch (SQLException ex) {

            dropped = false;
        }

        return dropped;
    }


    @Override
    public void onConfigure(SQLiteDatabase db) {
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
    }

    // Called when the database needs to be upgraded to the current version.
@Override
    public void onUpgrade(SQLiteDatabase _db, int _oldVersion, int _newVersion) {

        if ( _oldVersion >= _newVersion){

            Log.w(mContext.getClass().getSimpleName(), "Cannot 'upgrade' from version " + _newVersion + " to " + _oldVersion + ". Upgrade attempt failed.");
        }

        try {

            _db.execSQL(ALTER_TABLE);

        }catch(RuntimeException ex){

            Log.e(mContext.getClass().getSimpleName(), "Database upgrade failed. Version " + _oldVersion + " to " + _newVersion);

            throw ex;
        }

        // Log the version upgrade.
        Log.i(mContext.getClass().getSimpleName(), "Database upgrade. Version " + _oldVersion + " to " + _newVersion);
    }

}
Drawn
  • 435
  • 1
  • 7
  • 20