Practice of Android Dagger (IV) MVVM mode Kotlin

Keywords: Android Java Google github

introduce

It's just a personal study note. If there's something wrong, please leave a message

Learn some basic knowledge first

@Binds

Declare that Person is provided by Student

public class Person {
    @Inject Person(){}
}
// Student
public class Student extends Person {
    @Inject Student(){}
}

@Module
public abstract class MainModule {

  @Binds
  public abstract Person bindMainTarger(Student student);
}

@IntoSet @IntoMap @IntoXXXX

The article here is very detailed, so I won't rewrite it any more

https://blog.csdn.net/io_field/article/details/71170727

Let's first introduce how to design MVVM when it is not dagger2

If you don't know how to use databinding, the following video is recommended

Android Data Binding practice - Beginner Level

https://www.imooc.com/learn/719

Android Data Binding practice - Advanced

https://www.imooc.com/learn/720

Let's put it simply

  override fun onCreate(savedInstanceState: Bundle?) {
        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this,R.layout.activity_main)
        binding.vm = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)
        binding.vm.start()
    }

But in most cases, we customize ViewModelProvider.Factory, and then create
To build viewmodels with different parameters

Custom Factory
public class ViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private ViewModelFactory(Application application, TasksRepository repository) {
        mApplication = application;
        mTasksRepository = repository;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        if (modelClass.isAssignableFrom(MainViewModel.class)) {
            //noinspection unchecked
            return (T) new MainViewModel(mApplication, mTasksRepository);
        }
        throw new IllegalArgumentException("Unknown ViewModel class: " + modelClass.getName());
    }
}

The above part has made it clear that there are some complicated problems in building dagger2, and the following is the introduction of dagger2. You will see why so many people like dagger2

dagger2 actual MVVM

Refer to Google:
https://github.com/googlesamples

ViewModel related
// dagger2 custom key
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)

ViewModelFactory

class ViewModelFactory @Inject constructor(
        private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>)
    : ViewModelProvider.Factory {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator: Provider<ViewModel>? = creators[modelClass]
        if (creator == null) {
            for ((key, value) in creators) {
                if (modelClass.isAssignableFrom(key)) {
                    creator = value
                    break
                }
            }
        }
        if (creator == null) throw IllegalArgumentException("unknown model class " + modelClass)
        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

ViewModel

class WelcomeViewModel @Inject constructor(application: Application) : AndroidViewModel(application) {
    // Self defined function
    fun start(){
        // ...
    }
}
scope statement
@Scope
@Retention(RUNTIME)
annotation class ActivityScope

@Scope
@Retention(RetentionPolicy.RUNTIME)
annotation class FragmentScope
module

Overall situation

@Module
internal object AppModule {

    @Singleton
    @Provides
    @JvmStatic
    fun getHttpService(): IOfficialApi {
        # Here is my custom library. You can change it to normal retrofit
        return RetrofitClient.Builder(
                IOfficialApi::class.java,
                true,
                IConstantPool.sCommonUrl,
                headers = { HashMap() }
        ).create()
    }


}

Corresponding activity

@Module
internal abstract class WelcomeViewModelModule 

    @Binds
    @IntoMap
    @ViewModelKey(WelcomeViewModel::class)
    abstract fun bindMainViewModel(viewModel: WelcomeViewModel): ViewModel

}

AllActivitiesModule

@Module
internal abstract class AllActivitiesModule {

    @Binds
    abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @ActivityScope
    @ContributesAndroidInjector(modules = [WelcomeViewModelModule::class])
    abstract fun contributeWelcomeActivitytInjector(): WelcomeActivity
}

Let's talk about it alone
Contributesaandroidinjector actually simplifies the following code
Let's look at the compiled kapt file

public abstract class AllActivitiesModule_ContributeWelcomeActivitytInjector {
  private AllActivitiesModule_ContributeWelcomeActivitytInjector() {}

  @Binds
  @IntoMap
  @ActivityKey(WelcomeActivity.class)
  abstract AndroidInjector.Factory<? extends Activity> bindAndroidInjectorFactory(
      WelcomeActivitySubcomponent.Builder builder);

  @Subcomponent(modules = WelcomeViewModelModule.class)
  @ActivityScope
  public interface WelcomeActivitySubcomponent extends AndroidInjector<WelcomeActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<WelcomeActivity> {}
  }
}
component
@Singleton
@Component(
        modules = [
            AndroidInjectionModule::class,
            AndroidSupportInjectionModule::class,
            // Globalmodule
            AppModule::class,
            // each activity Corresponding design
            AllActivitiesModule::class
        ]
)
interface AppComponent {


    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        fun build(): AppComponent
    }

    fun inject(app: App)
}

Use

Please build the code above first

App

public class App extends Application implements HasActivityInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.builder()
                .application(this)
                .build()
                .inject(this);
        // ARouter
        ARouter.init(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingAndroidInjector;
    }
}

BaseActivity

abstract class BaseActivity<in T : ViewDataBinding, vm : ViewModel>(
    // custom layout
    private val layoutResID: Int)
:AppCompatActivity(){
    // mvvm
    private lateinit var binding: T

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory

    override fun onCreate(savedInstanceState: Bundle?) {
        // injection
        AndroidInjection.inject(this)
        // create
        super.onCreate(savedInstanceState)
        // Initializing Binding
        binding = DataBindingUtil.setContentView(this, layoutResID)
        // binding
        val vm = ViewModelProviders.of(this, viewModelFactory).get(getViewModelClass())
        // DataBinding
        init(binding, vm)
    }
        /** MVVM */
    abstract fun getViewModelClass(): Class<vm>
    abstract fun init(binding: T, vm: vm)
}

WelcomeActivity

class WelcomeActivity : BaseActivity<ActivityWelcomeBinding,WelcomeViewModel>(
        R.layout.activity_welcome
) {


    override fun getViewModelClass() = WelcomeViewModel::class.java

    override fun init(binding: ActivityWelcomeBinding, vm: WelcomeViewModel) {
        binding.viewModel = vm
        vm.start()
    }
}

Is dagger2 very easy to use? It's comfortable~~

Posted by cosmicsea on Wed, 01 Jan 2020 00:19:28 -0800