Android MVPR Architecture Mode

Source: Internet
Author: User

One of the reasons I've been trying to get Google's IO App to be unit tested recently is to verify that Freeman and Pryce summarize unit tests in references. Even now I'm not refactoring any of the Activity in iosched, but I've felt what they say in refactoring the code.

The activity I'm refactoring now is sessiondetailactivity, and if you've been watching me, you'll know what activity I'm talking about, but if you're only looking at my post for the first time, you can look at the following diagram to see What is the interface of sessiondetailactivity?

As I said in the preface to this series of posts, there are a few problems that must be solved to make sessiondetailactivity available for unit testing. As I said in my previous article in this series, it is a challenge to unit-test its dynamically constructed View, but in that blog post, I mentioned that my solution to the problem is not a blog post, because there is a cyclic dependency between view and Presenter.

Cyclic dependencies are a symptom of big problems with Android application architectures: Both Activity and Presenter violate the principle of single responsibility, and they need at least two things to do: Bind the data to the View and respond to the user's input. This is why the Sessiondetailactivity class is used as the Model for Android development, allowing the class to have more than 1000 lines of code.

I believe there is a better way to structure our application, and in the next blog post, I will propose a new architecture with the following features:

  1. To break the multiple responsibilities that are normally the responsibility of Presenter and Activity

  2. Breaking the cyclical dependencies that exist between View or Activity and Presenter

  3. Allows us to construct a dependency injection of all objects that show data to the user and the corresponding user input

  4. It is easy for UI-related business logic to be unit tested, and it is not possible to be built to perform their duties without the necessary dependencies, and to modify the behavior of objects by leveraging aggregation and polymorphism.

In this blog post, I'll try to summarize the reasons for developing a new Android application architecture.

Why do I need a new architecture? Activity/fragment/presenter will become bloated.

Activity and Fragment (hereafter I will collectively refer to activities, but what I say also applies to Fragment) is a typical violation of the principle of single responsibility:

  • Handling of View Events

  • Update Data Model

  • Call Other View

  • Interacting with system components

  • Handling System Events

  • Update View based on system events

As Richa says, most of these duties are stripped from the activities, but even if we do, activities violates the principle of single responsibility. Even the simplest activities need to bind the Model's data to the View and make a corresponding user input, for example:.

[Code]java Code:
public class Sessiondetailactivity extends Baseactivity implements Loadermanager.loadercallbacks, Observable scrollview.callbacks {//... @Override protected void onCreate (Bundle savedinstancestate) {//responsibi Lity 1:responding to User's action (in this case, a click) maddschedulebutton.setonclicklistener (New View.onclickl                Istener () {@Override public void OnClick (view view) {Boolean starred =!mstarred;                Sessionshelper helper = new Sessionshelper (sessiondetailactivity.this);                Showstarred (starred, true);                Helper.setsessionstarred (Msessionuri, starred, mtitlestring); if (Build.VERSION.SDK_INT >= build.version_codes.                            Jelly_bean) {maddschedulebutton.announceforaccessibility (starred? GetString (r.string.session_details_a11y_session_added): GetString (r.string.session_details_a11 y_session_removed));                 }/* [analytics:event] * Trigger:add or remove a session from My Schedule. * CATEGORY: ' Session ' * ACTION: ' starred ' or ' unstarred ' * label:session tit                 Le/subtitle. * [/analytics] */analyticsmanager.sendevent ("Session", starred?)            Starred ":" Unstarred ", mtitlestring, 0L);         }        });         ...//responsibility 2:fetching and binding data to the view Loadermanager Manager = Getloadermanager ();        Manager.initloader (Sessionsquery._token, NULL, this);        Manager.initloader (Speakersquery._token, NULL, this);    Manager.initloader (Tag_metadata_token, NULL, this); }

The sessiondetailactivity in Google iosched apps is a great example of how Activity can become bloated even if it is only responsible for binding data to View and responding to user input. Even if we peel this part of the code from Sessiondetailactivity, there is a class with more than 700 lines of code. Don't believe me? You can go to see the source code, Presenter also because the activity of the reason become bloated: Presenter is usually responsible for binding data and response to user input, so Presenter also need to be like Activity by stripping additional responsibilities to be thin.

Activities/fragment/presenter usually has a cyclic dependency between the View

Activities typically perform the duties of binding data to a view and responding to user input through the loop between them and the view (for example, View as a Setcontentview () method parameter). Here's an example:

[Code]java Code:
Maddschedulebutton.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (Vie                W view) {Boolean starred =!mstarred;                Sessionshelper helper = new Sessionshelper (sessiondetailactivity.this);                Showstarred (starred, true);                Helper.setsessionstarred (Msessionuri, starred, mtitlestring); if (Build.VERSION.SDK_INT >= build.version_codes.                            Jelly_bean) {maddschedulebutton.announceforaccessibility (starred? GetString (r.string.session_details_a11y_session_added): GetString (r.string.session_details_a11                y_session_removed));                 }/* [analytics:event] * Trigger:add or remove a session from My Schedule. * CATEGORY: ' Session ' * ACTION: ' starred ' or ' unstarred ' * label:session tit                 Le/subtitle. * [/anAlytics] */analyticsmanager.sendevent ("Session", starred?)            "Starred": "Unstarred", mtitlestring, 0L); }        });

Sessiondetailactivity holds a reference to Maddschedulebutton, and Maddschedulebutton also holds a reference to sessiondetailactivity. I will say that such loops rely on limiting the way we are typically used to implement UI-related business logic in activities.

MVP Presenter have the same cyclic dependencies as they are with View, and before I can explain in detail, I have to briefly describe the differences between view and MVP patterns in the traditional Android application architecture.

The view in MVP mode is like I defined, just one of the three MVP models, usually defined as an interface, and typically implemented in view in Activity,fragment or Android legacy architectures. The view in the Android legacy architecture is like its name, which is a subclass of view.

Using the view and Presenter in MVP mode is simply recreating the same cyclic dependency between view and activities in the Android legacy architecture between them.

Presenter needs the view in the MVP mode so that they can bind the data to the view in the VIEW,MVP mode in MVP mode, which requires a reference to the Presenter so that it can pass the click and other UI-related events to Presenter. Square's blog post has the implementation of the MVP model of cyclic dependence.

Cyclic dependencies can cause problems when you want to build objects (or typically) for unit tests. However, in general, we do not consider the issue of cyclic dependencies between the MVP mode view and Presenter or activities and view, because activities and Fragment are initialized by the system, and because we are not injecting them with dependency injection Activity and/or Fragment dependence. Instead, we are simply initializing any dependencies that the Activity requires in the OnCreate () method:

[Code]java Code:
public class MyActivity extends Activity implements Mvpview {View Mbutton;        @Override protected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate);        Setcontentview (r.layout.activity_browse_sessions);        ... final Presenter Presenter = new Presenter (this);                Mbutton.setonclicklistener (New View.onclicklistener () {@Override public void OnClick (View v) {            Presenter.onbuttonclicked ();    }        }); }}

Initializes a hybrid class that relies on the OnCreate () method, however, restricts our use of composition and polymorphism to implement UI-related business logic. Here's an example of how you should use polymorphism to implement UI-related business logic: Suppose you develop a user-used app, and users have different privileges at different levels, then they need to verify or answer questions from other users to improve their ratings. We can imagine that there are many buttons to complete different functions depending on the level of completion, or the initial state of View determined by the user level. Polymorphism provides us with a neat, extensible way to implement this logic: we create a Presenter to bind different levels to the user, regardless of the level of the user, we can upload the View of the MVP pattern to a specific Presenter subclass, And let the subclass handle the corresponding Click event or render the UI based on the user's level. Of course, there are many ways of architecting Android apps that allow us to take advantage of polymorphism in the presence of View-cycle dependencies in Presenter and MVP models, but these methods are not elegant enough, or they make a great contribution to the completion of unit testing.

The remainder of this blog post is not enough for me to describe the solutions I remember, but I can briefly say why it is not ideal to solve the cycle-dependent approach between View and Presenter in the MVP model. You can imagine that we could just create a View or Presenter of MVP mode without any reliance on their duties. In other words, we can look like this:

[Code]java Code:
public class MyActivity extends Activity implements Mvpview {     View Mbutton;     @Override    protected void onCreate (Bundle savedinstancestate) {        super.oncreate (savedinstancestate);        Setcontentview (r.layout.activity_browse_sessions);        //...        Final Presenter Presenter = new Presenter ();        Presenter.setview (this);        Mbutton.setonclicklistener (New View.onclicklistener () {            @Override public            void OnClick (View v) {                presenter.onbuttonclicked ();}}        );}    }

This way we can solve the problems mentioned above through polymorphism, but this does not break the cyclic dependency. What it can do is allow us to create an object in an invalid state. This is not the most concise solution, put it in Freeman and Pryce words:

"Create or not create, no need to try"

We want to make sure that a valid object is always created, partially creating an object and then completing it by setting its properties is fragile ...

Conclusion

Presenter and activities violate the principle of single responsibility, and they are often responsible for binding data to View and responding to user input, which can make activities and Presenter bloated.

Presenter and activities often have multiple responsibilities because of their cyclical dependence on the view, and even if such circular references do not cause problems, it will be more difficult to unit test the view and/or Presenter, and limit our use of polymorphism to implement the UI phase The business logic of the shutdown.

As I said before, I think there will be a way to apply the architecture without the above martyrs, and in the next blog post, I will present an alternative architecture.

Android MVPR Architecture Mode

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.