After half a month’s study of AAC components, I finally came to the last step. I hope this article will help you. This demo architecture RxJava + Retrofit + MVVM, and around playing Android API (thank Hongyang) with us to build our MVVM project.

Building an MVVM Architecture from Scratch series of articles (continuing updates) : Android builds MVVM from scratch (1) ————DataBinding Android builds MVVM from scratch (2) ————ViewModel Android builds MVVM from scratch (3) ————LiveData Android builds MVVM architecture from scratch (4) ————Room (from beginner to advanced) Android builds MVVM architecture from scratch (5) ————Lifecycles Android builds MVVM architecture from scratch (6) ———— Uses Android to play ———— Use the Android API to take you to build the MVVM framework (final)

Before writing this article I was thinking, why is there an MVVM framework after MVP, why are we using MVVM?

When MVC became MVP. It’s just that the code logic is simpler, the View and Model are decoupled, and you’re less upset when you take on someone else’s project. But the trade-off is interface explosion, which is why you don’t use it at all when you’re doing small projects. So today we’re going to talk about MVVM. MVVM has the advantages of the MVP without having to write as many interfaces as the MVP. ViewModel does everything with LiveData, and because it has a life cycle, it’s more robust, it doesn’t have to write as much judgment code, and more importantly, LiveData replaces those interfaces, which is amazing.

Here I would like to say my feelings, the use of frame is a personal opinion. Everyone is different. I build here, it’s my insights and ideas. You can use my ideas to build your own MVVM projects.


1. Create a new project (this section will complete the function of a banner AD)

Open the DataBinding

// Load the project build.gradle under the anroid tag
dataBinding {
        enabled = true
    }
Copy the code

Adding dependencies

 //okhttp, Retrofit, rxjava
    implementation 'com. Squareup. Okhttp3: okhttp: 3.8.0'
    implementation 'com. Squareup. Retrofit2: retrofit: 2.3.0'
    implementation 'com. Squareup. Retrofit2: converter - gson: 2.3.0'
    implementation 'com. Squareup. Retrofit2: adapter - rxjava2:2.3.0'
    implementation 'the IO. Reactivex. Rxjava2: rxjava: 2.1.7'

    // RxJava memory leak caused by not collecting in time
    implementation 'com. Trello. Rxlifecycle2: rxlifecycle - components: 2.1.0'
    implementation 'android. Arch. Lifecycle: extensions: 1.1.1'

    //Room dependency references
    implementation 'android. Arch. Persistence. Room: the runtime: 2.1.4'
    annotationProcessor 'android. Arch. Persistence. Room: the compiler: 2.1.4'

    //Room works with RxJava
    implementation 'android. Arch. Persistence. Room: rxjava2:2.1.4'
    implementation 'the IO. Reactivex. Rxjava2: rxandroid: 2.0.2'

    / / advertising banner
    implementation 'com. Youth. The banner, the banner: 1.4.10'

    //glide
    implementation 'com. Making. Bumptech. Glide: glide: 4.9.0'
    annotationProcessor 'com. Making. Bumptech. Glide: the compiler: 4.9.0'
Copy the code


2. Create our Base

2.1 create BaseViewModel

Because when we create a BaseActivity, we definitely import our BaseViewModel. So we’re going to create the BaseViewModel first. We know that we’re going to encapsulate all the common and duplicate code in our Base. Of course, we don’t know what we’re going to do with our BaseViewModel yet, so let’s create it first, and then what do we need and what do we need

// AndroidViewModel inherits AndroidViewModel, because when you want to use context, you can get getApplication()
public abstract class BaseViewModel extends AndroidViewModel {

    public BaseViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared(a) {
        super.onCleared(); }}Copy the code


2.2 create BaseActivity

There are two references in the baseActivity, DataBinding and ViewModel, which are added with generics,

//ViewDataBinding is the parent of all DataBinding classes
public abstract class BaseActivity<VM extends BaseViewModel.VDB extends ViewDataBinding> extends AppCompatActivity {   
    // Get the current activity layout file and initialize our Binding
    protected abstract int getContentViewId(a);
    
    // Process logical business
    protected abstract void processLogic(a);


    protected VM mViewModel;
    protected VDB binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getContentViewId());
        // Initialize our binging
        binding = DataBindingUtil.setContentView(this, getContentViewId());
        AppCompatActivity is the lifeOwner of any compatactivity
        binding.setLifecycleOwner(this);
        // Create our ViewModel.
        createViewModel();
        processLogic();

    }

    public void createViewModel(a) {
        if (mViewModel == null) {
            Class modelClass;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
                modelClass = (Class) ((ParameterizedType) type).getActualTypeArguments()[0];
            } else {
                // BaseViewModel is used by default if no generic arguments are specified
                modelClass = BaseViewModel.class;
            }
            mViewModel = (VM) ViewModelProviders.of(this).get(modelClass); }}}Copy the code

3. Simply encapsulate our Retrofit

I’m just briefly encapsulating our Retrofit here. RxJava + Retrofit + MVP(RxJava + Retrofit + MVP) For beginners,VIP MVP frame!! The contents of encapsulation include:

  • Support all network request types, get, POST, PUT… Nonsense!! Retrofit already does everything)
  • Upload files and monitor the upload progress
  • Support download file and breakpoint download and monitor the download progress
  • Support online cache when network is available (validity period when network is connected)
  • Disconnect from the network, support offline cache (offline cache validity period)
  • Whether to request the same URL more than once while the network is still requesting it
  • Support network request failure, automatic reconnection
  • Cancelling network requests is supported


Retrofit’s interfaces are as follows:

public interface RetrofitApiService {
    / / wanAndroid banner
    @GET("banner/json")
    Observable<ResponModel<List<BannerBean>>> getBanner();
}
Copy the code


The simple encapsulation is as follows, encapsulating a singleton RetrofitManager:

public class RetrofitManager {
    private static RetrofitManager retrofitManager;
    private Retrofit retrofit;
    private RetrofitApiService retrofitApiService;
    private RetrofitManager(a) {
        initRetrofit();
    }
    
    public static RetrofitManager getInstance(a) {
        if (retrofitManager == null) {
            synchronized (RetrofitManager.class) {
                if (retrofitManager == null) {
                    retrofitManager = newRetrofitManager(); }}}return retrofitManager;
    }
    
    public static RetrofitApiService getApiService(a) {
        return retrofitManager.retrofitApiService;
    }
    
    private void initRetrofit(a) {
        retrofit = newRetrofit.Builder() .baseUrl(SystemConst.DEFAULT_SERVER) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); retrofitApiService = retrofit.create(RetrofitApiService.class); }}Copy the code

Fourth, to achieve our functions

4.1 create MainViewModel

The first step is to create our MainViewModel and add and load our banner interface. The Repository data layer (also known as the Model layer) is as follows:

public class MainViewModel extends BaseViewModel {

    public MainViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared(a) {
        super.onCleared();

    }

    public MutableLiveData<List<BannerBean>> getBanners(){
        // Since I use LiveData, I don't think I need to switch to the main thread. LiveData can do that for us
        // Call the interface and return our MutableLiveData
      
       >
      
        final MutableLiveData<List<BannerBean>> liveData = new MutableLiveData<>();
        RetrofitManager.getInstance().getApiService().getBanner()
                .subscribeOn(Schedulers.io())
                .subscribe(new Consumer<ResponModel<List<BannerBean>>>() {
                    @Override
                    public void accept(ResponModel<List<BannerBean>> listResponModel) throws Exception { liveData.postValue(listResponModel.getData()); }},new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {}});returnliveData; }}Copy the code


4.2 in the MainActivity

  • Start by changing the XML to our DataBinding layout
  • Add a button and click to request our interface
  • Add third party Banner to display our data

XML is as follows:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        <com.youth.banner.Banner
            android:id="@+id/banner"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            />

        <Button
            android:text=Click request
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </RelativeLayout>
</layout>
Copy the code


MainActivity inherits our BaseActivity and specifies our ViewModel and DataBinding.

    public class MainActivity extends BaseActivity<MainViewModel.ActivityMainBinding> {

    @Override
    protected int getContentViewId(a) {
        return R.layout.activity_main;
    }

    @Override
    protected void processLogic(a) {
        initBanner();
        binding.btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) { getBanner(); }}); }private void getBanner(a) {
        mViewModel.getBanners().observe(this.new Observer<List<BannerBean>>() {
            @Override
            public void onChanged(List<BannerBean> bannerBeans) { updateBanner(bannerBeans); }}); }private void initBanner(a) {
        binding.banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR_TITLE_INSIDE);
        // Add the image loader to the banner
        binding.banner.setImageLoader(new GlideImageLoader());
    }

    private void updateBanner(List<BannerBean> data) {
        if (data == null || data.size() <= 0) {
            return;
        }
        List<String> urls = new ArrayList<>();
        List<String> titles = new ArrayList<>();
        for (int i = 0; i < data.size(); i++) { urls.add(data.get(i).getImagePath()); titles.add(data.get(i).getTitle()); } binding.banner.setBannerTitles(titles); binding.banner.setImages(urls); binding.banner.start(); }}Copy the code


Follow the project and you will run through a simple MVVM project. The next canto will be the final canto. If MVVM series can help you, please give the host a thumbs up. thank you

As with this article, simple MVVM will pass. I’m not going to post the demo link here. The next post, the final post, will put a link to the final demo. Final running effect:





Another class covered in this article is ResponModel

public class ResponModel<T> implements Serializable {
    public static final int RESULT_SUCCESS = 0;

    private T data;
    private int errorCode;
    private String errorMsg;

    public T getData(a) {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public int getErrorCode(a) {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg(a) {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public boolean isSuccess(a){
        returnRESULT_SUCCESS == errorCode; }}Copy the code


BannerBean:

public class BannerBean implements Serializable {

    /** * desc: imagePath: imagePath: imagePath: https://wanandroid.com/blogimgs/67c28e8c-2716-4b78-95d3-22cbde65d924.jpeg * isVisible : 1 * order : 0 * title : Type: 0 * URL: https://url.163.com/4bj */

    private String desc;
    private int id;
    private String imagePath;
    private int isVisible;
    private int order;
    private String title;
    private int type;
    private String url;

    public String getDesc(a) {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public int getId(a) {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getImagePath(a) {
        return imagePath;
    }

    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }

    public int getIsVisible(a) {
        return isVisible;
    }

    public void setIsVisible(int isVisible) {
        this.isVisible = isVisible;
    }

    public int getOrder(a) {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public String getTitle(a) {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getType(a) {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getUrl(a) {
        return url;
    }

    public void setUrl(String url) {
        this.url = url; }}Copy the code


GlideImageLoader:

public class GlideImageLoader extends ImageLoader {
    @Override
    public void displayImage(Context context, Object path, ImageView imageView) { Glide.with(context).load(path).placeholder(R.mipmap.ic_launcher) .error(R.mipmap.ic_launcher) .centerCrop().into(imageView); }}Copy the code

Finally, don’t forget to add Internet access. Come on, LiveData has replaced the interface.