Square had the same idea, but generalized, so I would kindly suggest to check it out if it fits your needs more: https://github.com/square/AssistedInject
// top level build.gradle
//..
allprojects {
repositories {
// ...
maven {
url "https://maven.pkg.github.com/halcyonmobile/view-model-factory-generator"
credentials {
username = project.findProperty("GITHUB_USERNAME") ?: System.getenv("GITHUB_USERNAME")
password = project.findProperty("GITHUB_TOKEN") ?: System.getenv("GITHUB_TOKEN")
}
// https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token
}
}
}
Note: you only need one maven declaration with "halcyonmobile/{specific}", every other package will be accessible.
kapt {
correctErrorTypes = true
}
ext {
viewModelGeneratorVersion = *latestVersionHere*
}
dependencies {
implementation "com.halcyonmobile.viewmodelfactorygenerator:factory-annotation:$viewModelGeneratorVersion"
kapt "com.halcyonmobile.viewmodelfactorygenerator:processor:$viewModelGeneratorVersion"
}
By default Kapt replaces every unknown type with NonExistentClass which can happen in this case when dagger's annotation processing runs before the viewModelGenerator's. In order to change that behaviour you have to set the correctErrorTypes flag. See more
- Annotate your viewModel with the ViewModelFactory annotation
- Optionally add scope to the ViewModel, this will be used in the generated Factory
- Annotate any dependencies which come from dagger with @Provided.
Example:
@Singleton
@ViewModelFactory
class MainViewModel extends ViewModel {
MainViewModel(@Provided Context context, int x) {
}
}
- Inject the generated factory builder.
- Create the factory given the non-dagger dependencies
Example:
public class MainActivity extends AppCompatActivity {
@Inject
MainViewModelFactoryBuilder factoryBuilder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AndroidInjection.inject(this);
ViewModelProvider(this, factoryBuilder.build(getIntent().getIntExtra("MAIN_ACTIVITY_X", 0)).get(MainViewModel.class);
}
}
- By default the parameters annotated with @Provided will be expected to come from dagger, this behaviour can be changed however.
- In ViewModelFactory annotation one can change the default meaning of @Provided by changing it's value to ViewModelFactory.ProvidedVia.MANUAL, in such case everything annotated with @Provided is expected to come from the generated build function while everything else expected from dagger
Example:
@Singleton
@ViewModelFactory(ViewModelFactory.ProvidedVia.MANUAL)
class MainViewModel extends ViewModel {
MainViewModel(@Provided Context context, int x) {
}
}
Usage of generated class:
public class MainActivity extends AppCompatActivity {
@Inject
MainViewModelFactoryBuilder factoryBuilder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AndroidInjection.inject(this);
ViewModelProvider(this, factoryBuilder.build(this).get(MainViewModel.class);
}
}
Some people had issues with the generated code after 2.25.2. Couldn't figure out yet what's the cause, probably something changed in the Component generation. For now if this happens to what you can do is injecting Provider instead of the direct class, so it will be:
public class MainActivity extends AppCompatActivity {
@Inject
Provider<MainViewModelFactoryBuilder> factoryBuilder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AndroidInjection.inject(this);
ViewModelProvider(this, factoryBuilder.get().build(getIntent().getIntExtra("MAIN_ACTIVITY_X", 0)).get(MainViewModel.class);
}
}
Copyright (c) 2020 Halcyon Mobile.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.