1. MVC: Model-view-controller, classic mode, easy to understand, the main disadvantages of two:
  • The View’s dependence on Model results in the View also containing business logic;

  • Controllers are going to be thick and complicated.

2.MVP: Model-view-Presenter, an evolution of MVC, replaced Controller with Presenter, mainly to solve the first shortcoming above, decouple the View from the Model, but the second shortcoming is still not solved.

3.MVVM: model-view-viewModel, which is an optimization mode for MVP, uses bidirectional binding: changes to the View are automatically reflected in the ViewModel and vice versa.

MVC

To put it simply: We usually write the Demo is MVC, controller is our activity, model (data provider) is to read the database, network requests, we generally have special classes to deal with these, View generally use custom controls.

But it’s all just beautiful to look at.

Imagine more and more activity code in real development, there is no separation of model and Controller at all, and controls need relational data and business.

So the real MVC is the MC (V), the Model and the Controller can’t be separated, and the data and View are heavily coupled. That’s the problem with it. A simple example: get weather data and display it on the screen

  • Model layer
public interface WeatherModel { 
    void getWeather(String cityNumber, OnWeatherListener listener); }...public class WeatherModelImpl implements WeatherModel { 
        @Override 
    public void getWeather(String cityNumber, final OnWeatherListener listener) { 
        /* Data layer operation */ 
        VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html, 
                Weather.class, new Response.Listener<weather>() { 
                    @Override 
                    public void onResponse(Weather weather) { 
                        if(weather ! =null) { 
                            listener.onSuccess(weather); 
                        } else{ listener.onError(); }}},new Response.ErrorListener() { 
                    @Override 
                    public void onErrorResponse(VolleyError error) { listener.onError(); }}); }}Copy the code
  • Controllor (View) layer
public class MainActivity extends ActionBarActivity implements OnWeatherListener.View.OnClickListener { 
    private WeatherModel weatherModel; 
    private EditText cityNOInput; 
    privateTextView city; .@Override 
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        weatherModel = new WeatherModelImpl(); 
        initView(); 
    } 
    // Initialize the View
    private void initView(a) { cityNOInput = findView(R.id.et_city_no); city = findView(R.id.tv_city); . findView(R.id.btn_go).setOnClickListener(this); 
    } 
    // Display the result
    public void displayResult(Weather weather) { WeatherInfo weatherInfo = weather.getWeatherinfo(); city.setText(weatherInfo.getCity()); . }@Override 
    public void onClick(View v) { 
        switch (v.getId()) { 
            case R.id.btn_go: 
                weatherModel.getWeather(cityNOInput.getText().toString().trim(), this); 
                break; }}@Override 
    public void onSuccess(Weather weather) { 
        displayResult(weather); 
    } 
    @Override 
    public void onError(a) { 
        Toast.makeText(this, failed to obtain weather information, toast.length_short).show(); }private T findView(int id) { 
        return(T) findViewById(id); }}Copy the code

Here’s a quick example:

  • Controls in an activity must care about business and data in order to know how to present themselves. In other words, it’s hard for two people to get the data, one to show the UI, and then do it without talking to each other.

  • All the logic is in the activity.

This is a perfect example of MVC’s two weaknesses. How does MVP solve the first one

MVP

As you can see from the above figure, the View is split into Presenter and View from MVC, which really implements the separation of logic processing and View. Write an example below: simulate a login interface, enter the user name and password, you can log in and clear the password

  • Model layer
/** Define the business interface */ public interface IUserBiz { 
    public void login(String username, String password, OnLoginListener loginListener); 
} 
/** Result callback interface */ public interface OnLoginListener { 
    void loginSuccess(User user); 
    void loginFailed(a); 
} 
/** The implementation of the specific Model */ public class UserBiz implements IUserBiz { 
    @Override 
    public void login(final String username, final String password, final OnLoginListener loginListener) 
    { 
        // Simulate the child thread time-consuming operation
        new Thread() 
        { 
            @Override 
            public void run(a) 
            { 
                try 
                { 
                    Thread.sleep(2000); 
                } catch (InterruptedException e) 
                { 
                    e.printStackTrace(); 
                } 
                // The simulated login succeeded
                if ("zhy".equals(username) && "123".equals(password)) 
                { 
                    User user = new User(); 
                    user.setUsername(username); 
                    user.setPassword(password); 
                    loginListener.loginSuccess(user); 
                } else{ loginListener.loginFailed(); } } }.start(); }}Copy the code
  • View

The View layer is defined as an interface. We don’t care about data. We don’t care about logic. If you only care about interacting with the user, this is what the login interface should do (think of the interface as a container, with inputs and outputs).

Get user name, get password, reality progress bar, hide progress bar, jump to other interface, display failure dialog, clear user name, clear password. Next, define the interface:

public interface IUserLoginView  {  
    String getUserName(a);  
    String getPassword(a);  
    void clearUserName(a);  
    void clearPassword(a);  
    void showLoading(a);  
    void hideLoading(a);  
    void toMainActivity(User user);  
    void showFailedError(a);  
}
Copy the code

Then the Activity implements this interface:

public class UserLoginActivity extends ActionBarActivity implements IUserLoginView { 
    private EditText mEtUsername, mEtPassword; 
    private Button mBtnLogin, mBtnClear; 
    private ProgressBar mPbLoading; 
    private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this); 
    @Override 
    protected void onCreate(Bundle savedInstanceState) 
    { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_user_login); 
        initViews(); 
    } 
    private void initViews(a) 
    { 
        mEtUsername = (EditText) findViewById(R.id.id_et_username); 
        mEtPassword = (EditText) findViewById(R.id.id_et_password); 
        mBtnClear = (Button) findViewById(R.id.id_btn_clear); 
        mBtnLogin = (Button) findViewById(R.id.id_btn_login); 
        mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading); 
        mBtnLogin.setOnClickListener(new View.OnClickListener() 
        { 
            @Override 
            public void onClick(View v) 
            { mUserLoginPresenter.login(); }}); mBtnClear.setOnClickListener(new View.OnClickListener() 
        { 
            @Override 
            public void onClick(View v) 
            { mUserLoginPresenter.clear(); }}); }@Override 
    public String getUserName(a) 
    { 
        return mEtUsername.getText().toString(); 
    } 
    @Override 
    public String getPassword(a) 
    { 
        return mEtPassword.getText().toString(); 
    } 
    @Override 
    public void clearUserName(a) 
    { 
        mEtUsername.setText(""); 
    } 
    @Override 
    public void clearPassword(a) 
    { 
        mEtPassword.setText(""); 
    } 
    @Override 
    public void showLoading(a) 
    { 
        mPbLoading.setVisibility(View.VISIBLE); 
    } 
    @Override 
    public void hideLoading(a) 
    { 
        mPbLoading.setVisibility(View.GONE); 
    } 
    @Override 
    public void toMainActivity(User user) 
    { 
        Toast.makeText(this, user.getUsername() + 
                " login success , to MainActivity", Toast.LENGTH_SHORT).show(); 
    } 
    @Override 
    public void showFailedError(a) 
    { 
        Toast.makeText(this."login failed", Toast.LENGTH_SHORT).show(); }}Copy the code
  • Presenter

Presenter takes input from the View layer, passes it to the Model layer for processing, and then calls back to the View layer for output to the user!

public class UserLoginPresenter { 
    private IUserBiz userBiz; 
    private IUserLoginView userLoginView; 
    private Handler mHandler = new Handler(); 
//Presenter must have access to the View and Model implementation classes
    public UserLoginPresenter(IUserLoginView userLoginView) 
    { 
        this.userLoginView = userLoginView; 
        this.userBiz = new UserBiz(); 
    } 
    public void login(a) 
    { 
        userLoginView.showLoading(); 
        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() 
        { 
            @Override 
            public void loginSuccess(final User user) 
            { 
                // Needs to be executed in the UI thread
                mHandler.post(new Runnable() 
                { 
                    @Override 
                    public void run(a) 
                    { userLoginView.toMainActivity(user); userLoginView.hideLoading(); }}); }@Override 
            public void loginFailed(a) 
            { 
                // Needs to be executed in the UI thread
                mHandler.post(new Runnable() 
                { 
                    @Override 
                    public void run(a) 
                    { userLoginView.showFailedError(); userLoginView.hideLoading(); }}); }}); }public void clear(a) 
    { userLoginView.clearUserName(); userLoginView.clearPassword(); }}Copy the code

Consider this example:

  • With the IUserLoginView interface (protocol), controls in an activity don’t need to care about data at all, just implement the interface to display the UI “step by step” in each method. In other words, we had two people working on the feature, one of them working on the data and creating the interface (protocol), and the other implementing the interface directly with the Activity, displaying the UI in each callback with his eyes closed. It was a great collaboration.

  • MVP successfully addresses the first shortcoming of MVC, but the logical processing is still mixed into activities.

MVC to MVP simply adds an interface and lowers a layer of coupling. So, using the same MVP to MVVM is to add an interface. I would recommend MVP mode for actual projects, MVVM is still complicated and a little over-designed for small and medium projects, so I won’t go into it here.

modular

The diagram above is a common architectural approach for a project

  • At the bottom is the base library, which houses business-neutral modules: basic network requests, image compression, etc., which can be divided as needed into logic modules, generic UI modules, and third-party libraries. (An independent SVN branch is recommended.)

  • The middle layer is the common business layer, which houses the common business modules (and business-related) for the company’s multiple Android projects, such as login process, file upload/download, etc.

  • The top layer is the application layer. For example, the company has three Android projects: LbBoss,BV, and BVHD. We can also extract the common layer for similar projects (such as BV and BV PAD versions here, the common layer is BVCommon).

To create a new APP, we usually have two methods of module division:

  • By type

  • Division by business

Each package is a service module, and each module is classified by type.

  • How to choose

I suggest small and medium-sized new projects by type is better, because the amount of code is not much to start by business is not practical, a package only put a few files?? Besides, the early stage business is not stable, until the development of mid-term business finalize the design, and then the difficulty of reconstruction is not big.

The module division mentioned above does not belong to modularization or plug-in. It is only a simple package structure that is different, and app is still an APP without any change. Modularity usually refers to the division of business into different Modulers (type is Library), each of which is independent of each other, while APP (type is Application) is just an empty shell that depends on all modulers.

Each red arrow is a business module, and the red box shows that our app contains only simple services: custom Application, entry Activity, build.gradle compilation, package configuration. Take a look at the project dependencies:


The biggest difference with this architecture is that different business modules are completely separated, and the benefit is that the development of different modules is never coupled to each other, because you have no access to module B’s API from module A. In this case, the communication between modules needs to be solved urgently. The Intent can handle the jump of some activities, but the real business scenario is much more than two interface hops. The business common methods, tool classes and data cache that you encapsulated before are now unavailable to other modules, and the controls and fragments that could have been reused cannot be shared. However, these are coupled with the business and cannot be accessed to the underlying base library.

Intermodule communication

There are two solutions to the above problems. According to the actual situation of the project, if the early stage of the project has been excellent and there is a perfect basic library, the communication between different modules is not very much, you can realize it by yourself. If the project is relatively large, it is suggested to use Alibaba’s open source library for frequent calls between different businesses.

Their implementation

First, each Moduler has a directory called include, which contains three classes. Here’s a BBS forum module for example.

  • IBBSNotify: a stack of interfaces that function as the external callback of the module and can only be triggered passively.

  • IBBService: contains a bunch of interfaces that are exposed to other modules, such as enterBbsActivity

  • IBBSServiceImpl: This is obviously an implementation of IBBService. For example, enterBbsActivity is how to jump to the forum interface and what data to transmit.

Each module has a method and callback, in and out, how to use other modules? ModulerManager implements an external interface for all modules. As A hub for each module, the A module tells The ModulerManager that I want to jump to the forum module. Then call ModulerManager IBBService enterBbsActivity, IBBSServiceImpl is IBBService concrete implementation (polymorphism) and then call IBBSServiceImpl. BBS enterBbsActivity jump to interface.

Communication is solved, actually trampling pit is just beginning:

  • The app here is newly created by us, so the app module of the previous project should be reduced to library:
apply plugin: 'com.android.library'
Copy the code

Build. Gradle configuration for app:

apply plugin: 'com.android.application'
Copy the code

The nature has changed greatly. The custom application, build.gradle, code obfuscations and so on are all moved to app

  • R.java is not final in lib-type modulers, and all switch case statements are replaced with if else

  • Be sure to build another common module for common data, caching, etc

  • There are also many common features, such as sharing, push, and try to separate businesses into Common

  • Other details related to the project

conclusion

In fact, the final product released by plug-in is also an APK, but the size can be controlled (some modules can be removed at will), allowing users to dynamically load child APKs. Therefore, plug-in is the dynamic loading of APK. Intents can be used to jump directly to another APK.

The intent only specifies that an Activity should be skipped, and that subsequent interactions are not in your control. The two apKs are also running in separate processes and data cannot be shared. Plug-ins allow two APKs to run in one process and can be developed exactly like the same APK. However, I think plug-in is only suitable for multi-department parallel development, such as Alipay super app, general APP development unless special needs, otherwise not used.

There are also mature frameworks for plugins, which I won’t go into detail here. In addition, everyone has different habits. Componentization and modularization are similar in my opinion. There is no need to tangle with the two terms.