7

I am planning to use MVP pattern for my new Android project. I have done some sample code and I would like to know, have I implemented it correctly? Please give comments on the code and also post your suggestions.

my activity class I am extending it from my BaseView class and I am implementing an interface. this activity simply calls an webservice in a new thread and updates the value in the textview.

public class CougarTestView extends BaseView implements ICougarView,
        OnClickListener {
    CougarTestPresenter _presenter;
    public String activityName = "CougarHome";

    /** Called when the activity is first created. */`enter code here`
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState, activityName);
        setContentView(R.layout.main);
        _presenter = new CougarTestPresenter(this);
        getSubmitBtn().setOnClickListener(this);
        getCallInfoBtn().setOnClickListener(this);

    }

    private Button getCallInfoBtn() {
        return (Button) findViewById(R.id.btn_callinfo);
    }

    public void setServiceValue(String retVal) {
        // TODO Auto-generated method stub
        getResultLabel().setText(retVal);
        setPbar(false);
        // toastMsg(retVal);
    }

    public void ResetPbar() {
        getProgressBtn().setProgress(0);
    }

    public void setProcessProgress(int progress) {

        if (getProgressBtn().getProgress() < 100) {
            getProgressBtn().incrementProgressBy(progress);
        } else {
            setPbar(false);
        }
    }

    private TextView getResultLabel() {
        return (TextView) findViewById(R.id.result);
    }

    private Button getSubmitBtn() {
        return (Button) findViewById(R.id.btn_triptype);
    }

    private ProgressBar getProgressBtn() {
        return (ProgressBar) findViewById(R.id.pgs_br);
    }

    public void setPbar(boolean visible) {
        if (!visible) {
            getProgressBtn().setVisibility(View.GONE);
        } else
            getProgressBtn().setVisibility(View.VISIBLE);
    }

    @Override
    public void setHttpResult(String retVal) {
        // TODO Auto-generated method stub
        setServiceValue(retVal);
    }

    private void toastMsg(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
    }

    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.btn_triptype: {           
            try {
                _presenter.valueFromService(RequestType.CallInfo, 0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;
        }

        default:
            setServiceValue("default");
        }
    }


}

My activity class: in my activity class i am having a textview and a button. when i press the button , it call the webservice to get the data in the presenter class. the presenter class calls the webservice parses the response and sets the value in the textview of the activity.

My presenter class


public class CougarTestPresenter {
    ICougarView mIci;
    RequestType mRtype;
    public String result= "thisi s result i";
    Handler mHandle;


    public CougarTestPresenter(ICougarView ici) {
        mIci = ici; 

    }
    public void valueFromService(RequestType type, int x) throws Exception{
        String url = getURLByType(type);

        // GetServiceresult service = new GetServiceresult();
        // service.execute(url);
        Handler handle = new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {

                case Globals.IO_EXPECTION: {
                    Toast.makeText(mIci.getContext(), msg.toString(),
                            Toast.LENGTH_LONG).show();
                    NetworkConnectivityListener connectivityListener = NetworkConnectivityListener
                            .getInstace();
                    mHandle = CustomHandler.getInstance(mIci.getContext(),
                            connectivityListener, mIci);
                    connectivityListener.registerHandler(mHandle,
                            Globals.CONNECTIVITY_MSG);
                    connectivityListener.startListening(mIci.getContext());
                    mIci.setPbar(false);
                }
                    break;
                case Globals.RHAPSODY_EXCEPTION:{   
                    ExceptionInfo exInfo =null;
                        try {
                            exInfo = Utility.ParseExceptionData(msg.obj.toString());

                        } catch (JSONException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        } catch (Exception e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }                       
                        mIci.setServiceValue(exInfo.Message + exInfo.Type +exInfo.Detail);

                //      new HandleRhapsodyException(mIsa, exInfo);
                }
                break;
                default: {
                    Toast.makeText(mIci.getContext(), msg.toString(),
                            Toast.LENGTH_LONG).show();
                    mIci.setServiceValue(msg.obj.toString());
                }
                }

            }
        };

        ServiceResult thread = new ServiceResult(handle, url);
        mIci.setPbar(true);
        thread.start();

    }

    public String getURLByType(RequestType type) {
        // TODO Auto-generated method stub
        switch (type) {
        case CallInfo: {
            return ("www.gmail.com");
        }
        case TripType: {
            return ("www.google.com");
        }
        default:
            return ("www.cnet.com");

        }
    }

    private class ServiceResult extends Thread {
        Handler handle;
        String url;

        public ServiceResult(Handler handle, String url) {
            this.handle = handle;
            this.url = url;
        }

        public void run() {
            sendExceptionLog(handle);

        }
    }

    public void sendExceptionLog(Handler handle) {

        DebugHttpClient httpClient = new DebugHttpClient();

        HttpGet get = new HttpGet(
                "https://192.168.194.141/TripService/service1/");
        try {
            HttpResponse response = httpClient.execute(get);

            HttpEntity r_entity = response.getEntity();
            String xmlString = EntityUtils.toString(r_entity);
            // setdvrid.setText(xmlString + " "
            // + response.getStatusLine().getStatusCode());

            httpClient.getConnectionManager().shutdown();

            if (response.getStatusLine().getStatusCode() != 200) {
                handle.sendMessage(Message.obtain(handle, Globals.RHAPSODY_EXCEPTION,
                        xmlString));
            result= Utility.ParseExceptionData(xmlString).Message;
            }
            else
            {
                handle.sendMessage(Message.obtain(handle, Globals.SERVICE_REPONSE,
                        response.getStatusLine().getStatusCode()
                                + response.getStatusLine().getReasonPhrase()
                                + xmlString));
            }

        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            handle.sendMessage(Message.obtain(handle, Globals.OTHER_EXPECTION,
                    e.getMessage().toString() + "she"));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            handle.sendMessage(Message.obtain(handle, Globals.IO_EXPECTION, e
                    .getMessage().toString() + "he"));
        } catch (Exception e) {
            handle.sendMessage(Message.obtain(handle, Globals.OTHER_EXPECTION,
                    e.getMessage().toString() + "it"));
        }

    }

the below interface is implemented in the activity class and the instance of the activity class is sent as interface object to the constructor of the presenter class.

my view interface

public interface ICougarView {
public void setServiceValue(String retVal);
public void setProcessProgress(int progress);
public void setPbar(boolean b);
public void ResetPbar();
public Context getContext();
}
Gangnus
  • 24,044
  • 16
  • 90
  • 149

1 Answers1

12

Sorry for the late :) I've use MVP on Android this way.

Activities are presenters. Every presenter has a link to model(s) (sometimes it is services, sometimes not, depending from the task) and to view(s). I create custom view and set it as the content view for activity.

See:

public class ExampleModel {
   private ExampleActivity presenter;

   public ExampleModel(ExampleActivity presenter) {
       this.presenter = presenter;
   }
   //domain logic and so on
}

public class ExampleActivity extends Activity {
   private ExampleModel model;
   private ExampleView view;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       model = new ExampleModel(this);
       view = new ExampleView(this);
       setContentView(view);
   }
   // different presenter methods
}

public class ExampleView extends LinearLayout {

    public ExampleView(Context context) {
        super(context);
    }
}

Also, I've discussed this topic here.

I should warn you, that Activity shouldn't be considered as the view. We had very bad expirience with it, when we wrote with PureMVC which considered Activity as view component. Activity is excellently suitable for controller/presenter/view model (I've tried all of them, I like MVP the most), it has excellent instrumentation for managing the views (View, Dialog and so on) while it's not a view itself.

QuickNick
  • 1,921
  • 2
  • 15
  • 30
  • 5
    I've seen a lot of articles out there recommending Activity as the view, with a custom Presenter -- but to be honest your approach really makes much more sense to me (and seems easier to maintain). I'm a fairly inexperienced Java developer, though, so what do I know :) – Brian Lacy Jan 05 '12 at 21:49
  • Do you have any more thorough examples you could share? The above sample is a little brief, kinda trying to get my head around android MVP too :) – Jimmy Mar 17 '12 at 18:49
  • I have no open-source projects. In fact, I often solve such tasks where Activity is too big for controller. It's Fragment that is suitable for light-weight controller, and Activity is super-controller. Also, my expirience of team-leading can tell me that it's not MVP/MVC/MVVM that is difficult thing. The difficult thing for my colleagues is "which behavioral pattern is suitable". If you wish, you can mail me at niko89a-AT-yandex.ru . – QuickNick Mar 19 '12 at 09:07
  • If an action happens in the view (a button is clicked, for example), how does your presenter know about it? – Lo-Tan Jun 04 '13 at 21:22
  • As a rule, in the view class I write inner non-static class which implements OnClick/Touch/CheckedChangeListeners. Also I declare new listener for view class (for example, with methods onAccountButtonClick(MyView sender)) and then Activity/Fragment implements this interface. In onClick() methods of inner class I call mMyViewListener.onAccountButtonClick(MyView.this) and so on. – QuickNick Jun 13 '13 at 13:46
  • 11
    My comment here is way late, but I wanted to point out for the record that an Activity cannot be a Presenter and conform to MVP. What you have implemented here is not the MVP design pattern. It may work. It may be nice. But it aint MVP. Activities, as I understand them, contain View-centric code/widgets. Presenters don't. Presenters drive the UI logic through Interfaces. This is what makes the View unit-testable. I've also seen people call Activities controllers (as in MVC). I'm hoping this comment serves to eliminate these misconceptions. – onefootswill Sep 14 '13 at 14:55