Introduction to the

background

MVP architecture has been very popular in the Android world for a few years and has become a mainstream framework. It makes the business logic and UI operations relatively independent and makes the code structure clearer.

MVVM is a big hit on the front end, but on the Android side, you don’t see a lot of people using it, and the introduction of MVVM is mostly about DataBinding or introducing ideas. The occasional mention of apps is a translation of Architecture Components on Google’s website.

I believe that when you read other people’s blogs or official documents, there will always be some pits. Or the introductory tutorial is too complicated (a bunch of principles and fancy diagrams are written in front of you, but they don’t really work, and are you sure it’s really an introductory tutorial?). Or it could be as simple as a “Hello World” and nothing more.

I couldn’t stand it, so I decided to intervene in your life.

directory

“Android – simple MVVM tutorial” is roughly divided into two parts: application, principle. Adopt step by step way, the content is simple, in line with the law of human learning, hope we use the least time to master MVVM.

  • Application of article: 01 Hello MVVM (Quick Start)02 Repository 03 Cache 04 State Lcee (Load/null/error/Content view) 05 Simple Data Source 06 Load 07 DataBinding 08 RxJava209 Dagger210 Abstract 11 Demo 12-N To be determined

  • 01 MyLiveData (the simplest LiveData) 02- N to be determined (not to read the source code, that is too boring, it is intended to take you from 0 to pull an Architecture)

About the questions

I have limited ability and energy, if you find something wrong or have a good suggestion, feel free to make an issue in the Github repository that accompanies this tutorial. What? Why not blog? Taking into account the domestic reproduction of the basic disregard of copyright, generally speaking, you are not in the source to see this article, so I generally can not see the message.

Tutorial code

https://github.com/ittianyu/MVVM

The application section is placed under the APP module, and the principle section is placed under the Implementation module. Each section of code uses a different package name and is independent of each other. Hyperlinks are not allowed in wechat articles. If you want to collect a set of articles, please pay attention to github warehouse.

preface

This video is also based on the beginning of episode 04, so it may be a little hard to understand, although I really want to get to the bottom of it, but I can’t tell you how to use dagger here, so I don’t recommend watching it if you haven’t used it before. Personally, I say that the dagger is too cumbersome to use compared to Spring, but I can’t do it on a mobile device, sacrificing simplicity for better performance. The introduction of the dagger not only provides decoupling and convenience, but also makes the project more difficult to get started, depending on the team.

Official release notes

Now the official version has been released, please update the library version number in time

Lifecycle = '1.0.0' lifecycle = '1.0.3' room = '1.0.0'Copy the code

Ap requirements

  • Can use dagger and dagger-Android

Environment configuration

Add the dagger and dagger-Android libraries

 // dagger annotationProcessor "com.google.dagger:dagger-compiler:$rootProject.dagger" compile "com.google.dagger:dagger-android:$rootProject.dagger" compile "com.google.dagger:dagger-android-support:$rootProject.dagger" // if you use the support libraries annotationProcessor "com.google.dagger:dagger-android-processor:$rootProject.dagger"Copy the code

refactoring

UserActivity

Dependency injection is primarily about reducing the duplication of code created. In UserActivity, the userViewModel is actually created this way.

 userViewModel = ViewModelProviders.of(this).get(UserViewModelModule.class);Copy the code

This is intolerable. Kill decisively (delete this code and let the dagger set a value automatically). Accordingly, you need to inject annotations on the userViewModel. And call code for injection. (Later)

 @Inject UserViewModel userViewModel;Copy the code

UserViewModel

However, it is not as simple as this. You will find that UserViewModel also has UserRepository in it and needs similar processing.

I have to mention the dagger inconveniences here. The Dagger needs to specify which class to inject, whereas Spring can set the scanned package directly (no matter which class to inject).

Because of the above reasons, if you want to inject an object into UserViewModel, you have two options:

  1. Call the injection code to inject the UserViewModel

  2. When you build the UserViewModel, pass in the object you want to build.

In fact, the dagger injection code is very uncomfortable to write. It’s not that the code is complicated or too much, but it violates the principle of dependency injection: a class should not know how to implement dependency injection.

For once injected into an Activity, there is a way to hide the injected code through the base class. And that kind of thing can’t be tolerated multiple times. There e is no doubt that this is a sending proposition. No, give me a problem set.

So, let’s change it. Remove the singleton build of UserRepository and add a constructor with arguments.

@Singleton public class UserViewModel extends ViewModel { UserRepository userRepository; . @Inject public UserViewModel(UserRepository userRepository) { this.userRepository = userRepository; }... }Copy the code

This constructor is used when the constructor adds Inject to indicate the build.

UserRepository

As you get here, you’ll see that we need to provide UserRepository to the UserViewModel, which is a really difficult task.

There are three objects in UserRepository that need to be injected. He changed it all in one go.

@Singleton public class UserRepository { private Context context; private UserDataSource remoteUserDataSource; private UserDataSource localUserDataSource; @Inject public UserRepository(Context context, @Remote UserDataSource remoteUserDataSource, @Remote UserDataSource localUserDataSource) { this.context = context; this.remoteUserDataSource = remoteUserDataSource; this.localUserDataSource = localUserDataSource; }}Copy the code

There are custom annotations Remote and Local, mainly because the two types of injection are the same and are used to indicate where to inject. All of you who know Dagger should know that. No more explaining.

So we need to inject UserRepository with two data sources, which are not that simple

RemoteUserDataSource

@Singleton public class RemoteUserDataSource implements UserDataSource { private UserApi userApi; private LocalUserDataSource localUserDataSource; @Inject public RemoteUserDataSource(UserApi userApi, LocalUserDataSource localUserDataSource) { this.userApi = userApi; this.localUserDataSource = localUserDataSource; }... }Copy the code

LocalUserDataSource

@Singleton public class LocalUserDataSource implements UserDataSource { private UserService userService; @Inject public LocalUserDataSource(UserService userService) { this.userService = userService; }... }Copy the code

Provider

Finally, we have changed the previous code, so we need to provide such objects to inject.

UserActivityModule

The hardest part to write is here, because we created the ViewModel without arguments, and here we did take arguments, so we had to create an anonymous factory class to do this. Is actually very simple, is modelClass. GetConstructor (UserRepository. Class). NewInstance (UserRepository);

 @Module public class UserActivityModule {     @Provides     UserViewModel provideUserViewModel(UserActivity activity, final UserRepository userRepository) {         return ViewModelProviders.of(activity, new ViewModelProvider.Factory() {             @NonNull             @Override             public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {                 try {                     return modelClass.getConstructor(UserRepository.class).newInstance(userRepository);                 } catch (NoSuchMethodException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 } catch (IllegalAccessException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 } catch (InstantiationException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 } catch (InvocationTargetException e) {                     throw new RuntimeException("Cannot create an instance of " + modelClass, e);                 }             }         }).get(UserViewModel.class);     } }Copy the code

UserViewModelModule

This is the object that provides the UserViewModel generation.

@Module public class UserViewModelModule { @Singleton @Provides UserRepository provideUserRepository(Context context, @Remote UserDataSource remoteUserDataSource, @Local UserDataSource localUserDataSource) { return new UserRepository(context, remoteUserDataSource, localUserDataSource); } @Remote @Singleton @Provides UserDataSource provideRemoteUserDataSource(UserApi userApi, LocalUserDataSource localUserDataSource) { return new RemoteUserDataSource(userApi, localUserDataSource); } @Singleton @Provides UserApi provideUserApi() { return RetrofitFactory.getInstance().create(UserApi.class); } @Local @Singleton @Provides UserDataSource provideLocalUserDataSource(UserService userService) { return new LocalUserDataSource(userService); } @Singleton @Provides UserService provideUserService(UserDao userDao) { return new UserServiceImpl(userDao); } @Singleton @Provides UserDao provideUserDao() { return DBHelper.getInstance().getDb().getUserDao(); }}Copy the code

BaseModule

Note above that the Context is required, so put it in the base Module for easy reuse.

What? Where did you get BaseApplication? Don’t worry. It’s about to happen.

@Module public class BaseModule { @Provides Context provideContext() { return BaseApplication.getAppContext(); }}Copy the code

Initialize the

There’s actually some initialization for the dagger that hasn’t been completed.

ActivitiesContributeModule

The following is provided by the Dagger – Android library for generating repetitive code.

 @Module public abstract class ActivitiesContributeModule {     @ContributesAndroidInjector(modules = {UserActivityModule.class})     abstract UserActivity userActivityInjector(); }Copy the code

Component

 @Singleton @Component(modules = {         UserViewModelModule.class,         BaseModule.class,         ActivitiesContributeModule_UserActivityInjector.class,         AndroidInjectionModule.class,         AndroidSupportInjectionModule.class, }) public interface BaseApplicationComponent {     void inject(BaseApplication application); }Copy the code

BaseApplication

Don’t forget to register in the sse document.

public class BaseApplication extends Application implements HasActivityInjector { @Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector; private static Context appContext; public static Context getAppContext() { return appContext; } @Override public void onCreate() { super.onCreate(); appContext = getApplicationContext(); DaggerBaseApplicationComponent.create().inject(this); } @Override public AndroidInjector<Activity> activityInjector() { return dispatchingActivityInjector; }}Copy the code

The above is really hard to explain, or I’m too tired to explain, it’s nearly 1:00 again, I have to go to work tomorrow, alas.

other

Initialization, annotation injection, and object provisioning are all done, but the injection seems to be missing.

As mentioned earlier, objects should not know exactly how they are injected, so we are better off putting it into the base class. Dagger android also provides one of these classes.

public class UserActivity extends DaggerAppCompatActivity

There are DBHelper. GetInstance (). The init (this); Please place it before the Activity’s super.oncreate. Of course, it is recommended to initialize it in Application. Why don’t I do that? Because I have n demo examples in one project, and I don’t want to get confused.

 @Override protected void onCreate(@Nullable Bundle savedInstanceState) {     DBHelper.getInstance().init(this);     super.onCreate(savedInstanceState); }Copy the code

Well, I don’t think there’s anything else. If I missed it, check out my demo, which has a link to Github at the beginning of the article.

conclusion

Unable to summarize. This essay is a failure. Maybe I’m tired. Originally planned week more, but found month more all too tired. Besides, MY focus is no longer on Android. I wrote this series of articles mainly because of the company’s technology sharing meeting. To be honest, THERE is no motivation anymore.

I’m going to stop for a while, maybe months, maybe years. Of course, I will also write other series of tutorials, if you are interested, please follow my subscription number IT Tianyu.

It is said that 8 hours for survival and the rest time for development. In fact, I have too many things to do in my spare time, but I am still free from poverty (poverty) and down (flow). I am too tired.