The overall structure of Android official MVVM framework to realize componentization

Author: Dawish_ Big D
Brief book: http://www.jianshu.com/u/40e1f22c2c53

(1) The overall structure of component implementation of Android official MVVM framework
(2) Android official MVVM framework implements componentized ARouter in series with each module

Current project structure chart top: Github address of Demo: https://github.com/Dawish/GoogleArchitectureDemo

0 - MVVM component architecture diagram of demonstration project

1, Explanation of google's official MVVM framework

I compared MVC and MVP earlier Two pictures to understand the difference between MVC and MVP in Android Development However, compared with MVC, our MVP is much superior, but Android development has become popular with MVVM. Not long ago, google officially released the official library of MVVM. The official MVVM library mainly includes the following four parts:

1 - formal MVVM library components

Among them, only ViewModel is a component in MVVM structure, and the other three are auxiliary.

lifecycles is to process the life cycle of UI interface. In the Support library after version 26, lifecycle owner is implemented in AppCompatActivity and SupportActivity, and the life cycle of UI interface has been processed internally.

LiveData is an abstract class. We can store the data required by the UI page, that is, wrap the data in LiveData. We can observe the data changes in LiveData, but LiveData is associated with the UI life cycle. When the UI page is destroyed, the data change callback of LiveData will not be executed.

Room is a sqlite data persistence library. We can also use other ORM libraries.

2, MVVM architecture advantages

Two pictures to understand the difference between MVC and MVP in Android Development The first two pictures really show the difference between MVC and MVP. I'll also take a picture here to see MVVM:

2-MVVM architecture

From the above figure, there is no relationship between Model and View. ViewModel is a processing plant that associates View and Model:

3-ViewModel factory

Summary of MVVM benefits:

  1. View and Model are two-way bound. Changes on one side will affect the other. Developers don't have to manually modify the UI data. Well, it's automatic.

  2. You don't need findViewById, no buttonknife, no specific View to set the data binding listener, etc. These can be done with DataBinding. Isn't it comfortable?

  3. The two-way binding between View and Model supports life cycle detection. You won't worry about page destruction and callback. This is completed by lifeCycle.

  4. It will not lead to a huge amount of code in the Activity like MVC, nor will there be a large number of View and Presenter interfaces like MVP. The project structure is less coupled.

  5. The lower coupling makes each module developed and tested separately, which can be distributed to different developers.

3, MVVM componentization example project architecture analysis

The following figure shows the dependencies between project modules and projects:

4-MVVM componentization example project architecture diagram

The following figure shows the directory structure in the project Android Studio:

5 - project directory structure

3.1 explanation of each module and their relationship:

  • lib_opensource: third party build Gradle depends on. This project mainly includes support, lifecycle, room, fresco, retro fit, okhttp, RxJava and ARouter.

  • lib_coremodel: it stores the Model and ViewModel modules in MVVM, that is, data processing and data binding with UI pages. Dependency lib_opensource library.

  • lib_common: public library, mainly including various base s, various ui components, custom components, public activities, public fragments, and public utils. Dependency lib_coremodel library.

  • module_girls: sister function module. You can switch between library and application. You can be an app or a component module of other apps. Component compilation is app, otherwise it is module.

  • module_news: news function module, which can switch between library and application. It can be an app or a component module of other apps. Component compilation is app, otherwise it is module.

  • app_universal: customized version of app, componentized compile time module_girls and module_news is app, so you can't add these two as modules to compile, so the app is used during component compilation_ Universal depends on lib_common library, otherwise module can be_ Girls and module_news is added and compiled as a module.

  • app_specific: customized version of app, componentized compile time module_girls and module_news is app, so you can't add these two as modules to compile, so the app is used during component compilation_ Specific depends on lib_common library, otherwise module can be_ Girls and module_news is added and compiled as a module.

3.2 ARouter series modules

Use ARouter to jump to Activity and obtain fragments. Remember to read other people's component structure articles before, and have been struggling with the problem of obtaining fragments. What I want to say is that it is not super simple to obtain fragments with ARouter?

Typical application of ARouter

  • Mapping from external URL to internal page, and parameter passing and parsing
  • Cross module page Jump and decoupling between modules
  • Intercept the jump process and handle logics such as landing and embedding points
  • Cross module API calls to decouple components through control inversion

3.3 switching between component compilation and non component compilation

We found gradle. In the project root directory Add a Boolean variable in the properties file to identify the compilation mode by modifying this variable:

# Each time you change the value of "isModule", you need to click the "Sync Project" button
# isModule is the switch between "integrated development mode" and "component development mode"
isModule=false

Then in module_girls and module_ Build.com in news Switching is supported in gradle files:

if (isModule.toBoolean()) {
    //Component compilation is application
    apply plugin: 'com.android.application'
} else {
    //Non componentized compile time is library
    apply plugin: 'com.android.library'
}

android {
    compileSdkVersion build_versions.target_sdk
    buildToolsVersion build_versions.build_tools

    defaultConfig {
        minSdkVersion build_versions.min_sdk
        targetSdkVersion build_versions.target_sdk
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        //ARouter
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [moduleName: project.getName()]
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    dataBinding {
        enabled = true
    }
    lintOptions {
        abortOnError false
    }
    sourceSets {
        main {
            if (isModule.toBoolean()) {
                //Component compilation is app, and ndroid.xml needs to be written in the corresponding AndroidManifest file intent. action. Main entry Activity
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //Exclude all Java files in the debug folder in the integrated development mode
                java {
                    //The debug folder contains the Application class, which is not required for non componentization
                    exclude 'debug/**'
                }
            }
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    api project(':lib_coremodel')
    api project(':lib_common')
    implementation 'com.android.support:support-v4:26.1.0'
    annotationProcessor deps.arouter.compiler
}

As you can see above, there will be unnecessary AndroidManifest files for componentization and non componentization compilation. The application class under the debug folder is required for componentization, and this folder is excluded for non componentization.

6 - component and non component compilation switching

  • The AndroidManifest file under module is compiled by the componentized app, and the MAIN entry Activity is written
  • Under dubug is the Application class during the compilation of component app, initializing the resources required when running as an app, and so on. In non componentized compilation, in build Everything in the debug folder is excluded from the gradle file.

3.4 final notice:

In the following columns, we will introduce the use of ARouter to jump Activity and obtain Fragment during MVVM componentization, data binding to realize the mutual binding of data and UI, Rxjava2 and Retrofit2 dynamic data acquisition, and the encapsulation of AndroidViewModel.

A lib is posted below_ The AndroidViewModel I encapsulated in the coremodel library uses generics to determine the data type, and uses dynamic URL s to obtain data:

package google.architecture.coremodel.viewmodel;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.databinding.ObservableField;
import android.support.annotation.NonNull;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;

import google.architecture.coremodel.datamodel.http.ApiClient;
import google.architecture.coremodel.datamodel.http.ApiConstants;
import google.architecture.coremodel.datamodel.http.service.DynamicApiService;
import google.architecture.coremodel.util.JsonUtil;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;

/**
 * Created by dxx on 2017/11/20.
 */

public class BaseViewModel<T> extends AndroidViewModel {

    //Life cycle observation data
    private MutableLiveData<T>  liveObservableData = new MutableLiveData<>();
    //The UI uses observable data. ObservableField is a wrapper class
    public ObservableField<T> uiObservableData = new ObservableField<>();

    private final CompositeDisposable mDisposable = new CompositeDisposable();

    private static final MutableLiveData ABSENT = new MutableLiveData();
    {
        //noinspection unchecked
        ABSENT.setValue(null);
    }


    public BaseViewModel(@NonNull Application application, String fullUrl) {
        super(application);
        ApiClient.initService(ApiConstants.GankHost, DynamicApiService.class).getDynamicData(fullUrl).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() {
            @Override
            public void onSubscribe(Disposable d) {
                mDisposable.add(d);
            }

            @Override
            public void onNext(ResponseBody value) {
               if(null != value){
                   try {
                       liveObservableData.setValue(JsonUtil.Str2JsonBean(value.string(), getTClass()));
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
               }
            }

            @Override
            public void onError(Throwable e) {

            }

            @Override
            public void onComplete() {

            }
        });
    }

    /**
     * LiveData lifecycle detection is supported
     * @return
     */
    public LiveData<T> getLiveObservableData() {
        return liveObservableData;
    }

    /**
     * Reset the observed data when actively changing the data
     * @param product
     */
    public void setUiObservableData(T product) {
        this.uiObservableData.set(product);
    }

    public Class<T> getTClass(){
        Class<T> tClass = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        return tClass;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        mDisposable.clear();
    }
}

Github address of Demo: https://github.com/Dawish/GoogleArchitectureDemo



Reproduced at: https://www.jianshu.com/p/c0988e7f31fd

Keywords: Android mvvm

Added by ibolui on Fri, 21 Jan 2022 12:09:31 +0200