This article has been published exclusively by guolin_blog, an official wechat account

preface

Android architecture is probably one of the most talked about topics in the forums, with MVC MVMVMS and MVVMS, followed by modularity and plug-ins. The debate about which architecture is better has never stopped. In my opinion, it is meaningless to compare these models without reference to actual projects. All models have advantages and disadvantages, and there is no good or bad. The more advanced the architecture is, the more complex it is to implement, and it requires more learning cost and more manpower. Therefore, the key of technology selection depends on the characteristics of your own project, the level of your team, the allocation of resources, and the limitation of development time. These are the key points! However, many teams put the cart before the horse and put MVVM into their own projects.

Now I will talk about my understanding of Android architecture from two parts: code level, mainly MVC and MVP. Project level, mainly is how to build the whole project, how to divide modules.

Popular understanding of MVC and MVP

Conclusion first:

  • MVC: Model-view-controller, classic mode, easy to understand, the main disadvantages of two:
  1. The View’s dependence on Model results in the View also containing business logic;
  2. Controllers are going to be thick and complicated.
  • MVP: Model-view-Presenter, an evolution of MVC, has replaced Controller with Presenter, mainly to solve the first shortcoming above, decoupling the View from the Model, but the second shortcoming is still not solved.
  • MVVM: Model-view-ViewModel, an optimized 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

1. 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. 2, 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 layer is defined in the form of interface, we don’t care about data, don’t care about logic processing! If you only care about interacting with the user, the login interface should do something like this (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
  • The View layer takes input from the user, passes it to the Model layer for processing, and then calls it 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

We have the IUserLoginView interface (protocol). Controls in an activity don’t need to care about data at all, just implement the interface to “step by step” display the UI 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. 2. MVP successfully addresses the first shortcoming of MVC, but the logic 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

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

  • By type:
  • Division by service: Each package is a service module, and each module is classified by type.
  • How to choose I suggest small and medium-sized new projects according to the type is better, because the beginning of the code is not much according to the business points unrealistic, 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.

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.

  • 1. Firstly, each Moduler has a directory called include, which contains three classes. Here, a BBS forum module is taken as an example. EnterBbsActivity IBBSServiceImpl: enterBbsActivity IBBSServiceImpl: Obviously IBBService implementation, such as enterBbsActivity is how to jump to the forum interface, what data to pass.

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

Build. Gradle configuration for shell app:

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

The nature has changed greatly. The custom application, build.gradle, code obliquation, etc are all moved to app B.R.Java. Moduler is not final in Lib. All switch case statements are replaced with if else C. Be sure to build another common module for common data, caching, etc. D. There are also many common functions, such as sharing, push, try to separate business into common E. Other details related to the project

  • The open source library ARouter is designed to continue module to module communication and is easy to use.

other

Plug-in: The final product released by plug-in is also an APK, but the size can be controlled (some modules can be removed at will), and users can 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.