How to use AndroidInjection in base class? - dagger-2

I have a BaseUiFragment in base module, need inject a UiComponent.
public abstract class BaseUiFragment extends Fragment {
#Inject
UiComponent mUiComponent;
#Override
public final void onAttach(Context context) {
AndroidSupportInjection.inject(this); //this is subclass
super.onAttach(context);
}
}
#Subcomponent
public interface BaseUiFragmentSubcomponent extends AndroidInjector<BaseUiFragment> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<BaseUiFragment> {
}
}
#Module(subcomponents = BaseUiFragmentSubcomponent.class)
public abstract class BaseUiFragmentModule {
#Binds
#IntoMap
#FragmentKey(BaseUiFragment.class) // key in MapProviderFactory
abstract AndroidInjector.Factory<? extends Fragment>
bindBaseUiFragmentInjectorFactory(BaseUiFragmentSubcomponent.Builder builder);
private BaseUiFragmentModule() {}
}
In app module, UiComponentModule provide UiComponent, MainFragment extends BaseUiFragment.
#Module
public class UiComponentModule {
#Provides
static UiComponent provideUiComponent() {
return new UiComponent() {};
}
}
#Singleton
#Component(modules = {AndroidSupportInjectionModule.class, BaseUiFragmentModule.class, UiComponentModule.class})
public interface ApplicationComponent extends AndroidInjector<MainApplication> {
#Component.Builder
abstract class Builder extends AndroidInjector.Builder<MainApplication> {
}
}
public class MainFragment extends BaseUiFragment {
#Override
public View onCreateViewImpl(Bundle savedInstanceState) {
return new View(getContext());
}
}
when AndroidSupportInjection.inject(this); run, it does not work.
Because DispatchingAndroidInjector's maybeInject() return false
injectorFactories has (BaseUiFragment.class, ...) not has (MainFragment.class, ...), but AndroidSupportInjection.inject(this); this is MainFragment.
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) { // factoryProvider is null
return false;
}
// ...
}
So, How to use AndroidInjection(AndroidSupportInjection) in base class?
After a few days of analysis:
Google's inject impl: it's only instance.getClass()
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
injectorFactories.get(instance.getClass());
if (factoryProvider == null) {
return false;
}
// ...
}
My impl: traversal it and its superclass,the problem is solved, but it use reflection that get the factoryProvider.
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<? extends Fragment>> factoryProvider
= injectorFactories.get(fragment.getClass());
Class fragmentSuperclass = fragment.getClass().getSuperclass();
while (factoryProvider == null && fragmentSuperclass != Fragment.class) {
factoryProvider = injectorFactories.get(fragmentSuperclass);
fragmentSuperclass = fragmentSuperclass.getSuperclass();
}
if (factoryProvider == null) {
return false;
}
// ...
}
So, is it only this way? And Google can Change the implementation?

You have only created a subcomponent that knows how to inject BaseUiFragment. Since that is all that dagger can see it will only know how to generate code to handle injecting the BaseUiFragment.
You need to create a subcomponent for each leaf of your inheritance hierarchy.
Something like this is how i like to do my fragment components
#Subcomponent
public interface MainFragmentComponent extends AndroidInjector<MainFragment> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainFragment> {}
#Module(subcomponents = MainFragmentComponent.class)
abstract class BindingModule {
#Binds
#IntoMap
#FragmentKey(MainFragment.class)
abstract Factory<? extends Fragment> mainFragmentComponentBuilder(Builder impl);
}
}

Related

#Inject constructor with parameters

I saw a method of using #inject annotation with parameter constructor. I found no use in #module in all parts of the project. I don't understand how this code injects or provides parameters in the constructor.
Can you help me analyze it?
Where is the datamanager provided?
In the whole project, #module + #provide is not used to provide datamanager. I only know that #inject can only annotate the parameterless constructor. I don't know where to instantiate the parameterless datamanager object. Thank you for your help
application:
public class Scallop extends Application {
private ApplicationComponent applicationComponent;
#Override
public void onCreate() {
super.onCreate();
applicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
public ApplicationComponent getApplicationComponent() {
return applicationComponent;
}
}
application module:
#Module
public class ApplicationModule {
private Scallop application;
public ApplicationModule(Scallop application) { // 提供类的构造器,传入Applicaton
this.application = application;
}
#Provides
#Singleton
Application provideApplication() {
return application;
}
#Provides
#ApplicationContext
Context provideContext() {
return application;
}
#Provides
#Singleton
Retrofit provideRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return retrofit;
}
#Provides
#Singleton
GankIOService provideGankIOService(Retrofit retrofit) {
return retrofit.create(GankIOService.class);
}
}
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
Application getApplication();
DataManager getDataManager();
}
```
one class:
#Singleton
public class DataManager {
private GankIOService gankIOService;
private PreferencesHelper preferencesHelper;
#Inject
public DataManager(GankIOService gankIOService, PreferencesHelper preferencesHelper) {
this.gankIOService = gankIOService;
this.preferencesHelper = preferencesHelper;
}
}
fragment module:
#FragmentScope
#Component(modules = FragmentModule.class, dependencies = ApplicationComponent.class)
public interface FragmentComponent {
void inject(HomeFragment homeFragment);
void inject(GanHuoPageFragment pageFragment);
void inject(XianDuFragment xianDuFragment);
void inject(XianDuPageFragment xianDuPageFragment);
void inject(PicturesFragment picturesFragment);
void inject(MoreFragment moreFragment);
}
#FragmentScope
#Documented
#Scope
#Retention(value = RetentionPolicy.RUNTIME)
public #interface FragmentScope {
}
```
here Can't understand constructor with parameter is #inject
public class GanHuoPagePresenter extends BasePresenter<GanHuoPageContract.View>
implements GanHuoPageContract.Presenter {
private DataManager dataManager;
private Disposable disposable;
#Inject
public GanHuoPagePresenter(DataManager dataManager) { // here here
this.dataManager = dataManager;
}
#Override
public void detachView() {
super.detachView();
if (disposable != null) {
disposable.dispose();
}
}
#Override
public void getGanHuo(String category, final int page) {
final List<GanHuo> ganHuoList = new ArrayList<>();
Observable<BaseResponse<GanHuo>> observable = dataManager.getGanHuo(category, page);
disposable = observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Function<BaseResponse<GanHuo>, ObservableSource<GanHuo>>() {
#Override
public ObservableSource<GanHuo> apply(#NonNull BaseResponse<GanHuo> ganHuoBaseResponse)
throws Exception {
return Observable.fromIterable(ganHuoBaseResponse.getResults());
}
}).filter(new Predicate<GanHuo>() {
#Override
public boolean test(#NonNull GanHuo ganHuo) throws Exception {
return !ganHuo.getType().equals("福利");
}
}).subscribe(new Consumer<GanHuo>() {
#Override
public void accept(GanHuo ganHuo) throws Exception {
ganHuoList.add(ganHuo);
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
getView().showError(throwable.getMessage());
}
}, new Action() {
#Override`enter code here`
public void run() throws Exception {
getView().showList(ganHuoList, page);
}
});
}
}
This is how it is used in V in MVP mode:
#Inject GanHuoPagePresenter presenter
That's constructor injection. By marking a constructor with #Inject Dagger knows about the object and can create it when needed. There's no need for modules, e.g. the following is a valid Dagger setup to create some Foo.
public class Foo {
#Inject
public Foo() {}
}
#Component
interface MyComponent {
Foo getFoo();
}
That's not true that #Inject can only annotate the parameterless constructor. From documentation
Injectable constructors are annotated with #Inject and accept zero or more dependencies as arguments.
I found "your" project on Github so let's see where dependencies for GanHuoPagePresenter come from.
#Inject
public GanHuoPagePresenter(DataManager dataManager) {
this.dataManager = dataManager;
}
#Inject
public DataManager(GankIOService gankIOService,PreferencesHelper preferencesHelper){
// gankIOService is provided by ApplicationModule and preferencesHelper uses constructor injection
this.gankIOService = gankIOService;
this.preferencesHelper = preferencesHelper;
}
#Inject
public PreferencesHelper(#ApplicationContext Context context){
// context is provided again by ApplicationModule
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
}

Dagger 2 Subcomponents for Encapsulation

How do I add a Subcomponent to a Module with an argument constructor?
Adding code here in addition to providing a github link:
ExampleApplication.java
public class ExampleApplication extends DaggerApplication {
#Inject
Database database;
#Override
public void onCreate() {
super.onCreate();
Timber.plant(new Timber.DebugTree());
Timber.i(database.name());
}
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerApplicationComponent
.builder()
.application(this)
.build();
}
}
ApplicationComponent.java
#ApplicationScope
#Component(modules = {
ApplicationModule.class,
AndroidSupportInjectionModule.class,
ActivityBindingModule.class,
DatabaseModule.class,
})
public interface ApplicationComponent extends AndroidInjector<ExampleApplication> {
Database database();
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
ApplicationComponent build();
}
#Override
void inject(ExampleApplication instance);
}
DatabaseModule.java
#Module(subcomponents = DatabaseComponent.class)
public class DatabaseModule {
#Provides
#ApplicationScope
Database provideDatabase(
#NumberOfCores int numberOfCores,
DatabaseComponent.Builder databaseComponentBuilder) {
return databaseComponentBuilder
.databaseImplModule(new DatabaseImplModule(numberOfCores / 2))
.build()
.database();
}
}
DatabaseComponent.java
#Subcomponent(modules = DatabaseImplModule.class)
public interface DatabaseComponent {
// #PrivateToDatabase <- Is this a qualifier? A scope? Neither?
Database database();
#Subcomponent.Builder
interface Builder {
Builder databaseImplModule(DatabaseImplModule databaseImplModule);
DatabaseComponent build();
}
}
DatabaseImplModule.java
#Module
public class DatabaseImplModule {
DatabaseImplModule(int concurrencyLevel) {}
#Provides DatabaseConnectionPool provideDatabaseConnectionPool() {
return new DatabaseConnectionPool();
}
#Provides DatabaseSchema provideDatabaseSchema() {
return new DatabaseSchema();
}
}
Database.java
public class Database {
#Inject
public Database() { }
public String name() {
return "I have a name";
}
}
I tried to take a look at the dagger subcomponents documentation, specifically the section Subcomponents for encapsulation. I tried to create a code example
to see if I could make it work, but with no luck. Am I missing something in the documentation? (There is a line of code commented out the #PrivateToDatabase which I am not sure what kind of annotation it is).

Android How to add external dependency (context) to SubComponent builder in Dagger 2.1.0

I am using dependency injection according to google sample
The only external dependency I can pass is through AppComponent builder
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
MainTabActivityModule.class,
CoreActivityModule.class
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MyApplication myApplication);
}
and injected in app like this
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent
.builder()
.application(myApplication)
.build().inject(myApplication);
...
}
According to document injecting in Activity looks like this. I added what I would like to achieve.
public class YourActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
AndroidInjection
//.builder() THIS IS WHAT I WANT TO ACHIEVE
//.addActivityContext(this) THIS IS WHAT I WANT TO ACHIEVE
//.build() THIS IS WHAT I WANT TO ACHIEVE
.inject(this);
super.onCreate(savedInstanceState);
}
}
but the question is how can I add additional parameter to subComponent.
#Subcomponent
public interface CoreActivitySubComponent extends AndroidInjector<CoreAppActivity> {
// #Subcomponent.Builder
// interface Builder {
// Builder addContext(Context context) //did not work
// CoreActivitySubComponent build(); //did not work
// }
//==or using abstract class
// in this option I do not know where to add parameter to this builder
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
}
}
Did you add the ContextModule to your #Subcomponent similar to this answer?
I think your #Subcomponent should look something like this:
#Subcomponent(module = {ContextModule.class})
interface MainTabActivityComponent extends AndroidInjector<CoreAppActivity> {
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
abstract Builder addContextModule(ContextModule contextModule);
#Override
public void seedInstance(CoreAppActivity instance) {
addContextModule(new ContextModule(instance));
}
}
}
And finally don't forget to include this #Subcomponent in your binding-module.
One last question: is this really required?
I found that using the AndroidInjector on the Application as well as Activities and Fragments will give me the correct corresponding Context when I inject it.
The problem was that Dagger 2.1.0 method AndroidInjection.inject(this); which is supposed to be used in Activity and Fragment, do not provide any builder to add external dependency.
I wanted to create general module which depends on Activity/Fragment context.
sample:
public class ToastController {
private Context context;
#Inject
public ToastController(Context context) {
this.context = context;
}
public void showToast(#StringRes int res) {
Toast.makeText(context, context.getText(res), Toast.LENGTH_SHORT).show();;
}
}
But I was not able to generalize it to the level, that I could provide just one context modude, instead I had to do create binds module for every single Activity/ Fragment that uses this module.
#Singleton
#Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
MainTabActivityModule.class,// IMPORTANT CLASS
})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(MyApplication myApplication);
}
This is a place, where I provide context module for each Activity
#Module
public abstract class MainTabActivityModule
#ContributesAndroidInjector(modules = ContextMainTabActivityModule.class)//THIS MODULE
abstract MainTabActivity contributeMainActivity();
}
and Context is provided using #Binds annotation
#Module
public abstract class ContextMainTabActivityModule {
#Binds
abstract Context provideContext(MainTabActivity featureActivity);
}
=====================
It can be done by overriding method seedInstance according to sample
I tried this, but it did not work for me
#Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<CoreAppActivity> {
abstract Builder addContextModule(ContextModule contextModule);
#Override
public void seedInstance(CoreAppActivity instance) {
addContextModule(new ContextModule(instance));
}
}
next class
#Module
public class ContextModule {
private CoreAppActivity coreAppActivity;
#Provides
Context getContext() {
return coreAppActivity.getBaseContext();
}
public ContextModule(CoreAppActivity coreAppActivity) {
this.coreAppActivity = coreAppActivity;
}
}

ListEditor with polymorphic types

I have searched around and been trying to figure out if I can use the editor framework with polymorphic types. I found this post at Using GWT Editors with a complex usecase which is close to what I am trying to do.
I am fairly new to the editor framework, so any help would be much appreciated.
For example, here is some of the code,
Data Transfer Objects:
public class Employee{
public List<Contact> contacts
}
public class Contact {
public class ContactEmail extends Contact {}
public class ContactAddress extends Contact {}
public class ContactPhoneNumber extends Contact {}
Editors:
public interface ContactBaseEditor<T extends Contact> extends Editor<T> {}
public class AddressEditor extends Composite implements Editor<ContactAddress>, ContactBaseEditor<ContactAddress>{}
public class EmailEditor extends Composite implements Editor<ContactEmail>, ContactBaseEditor<ContactEmail>{)
public class PhoneNumberEditor extends Composite implements Editor<ContactPhoneNumber>, ContactBaseEditor<ContactPhoneNumber>{}
ContactEditor class:
public class ContactEditor extends Composite implements IsEditor<ListEditor<Contact, ContactEditorWrapper>> {
private class ContactEditorSource extends EditorSource<ContactEditorWrapper> {
#Override
public ContactEditorWrapper create(final int index) {
ContactEditorWrapper contactEditor = new ContactEditorWrapper();
communicationContactsPanel.add(contactEditor);
return contactEditor;
}
#Override
public void dispose(ContactEditorWrapper subEditor) {
subEditor.removeFromParent();
}
#Override
public void setIndex(ContactEditorWrapper editor, int index) {
communicationContactsPanel.insert(editor, index);
}
}
private ListEditor<Contact, ContactEditorWrapper> editor = ListEditor.of(new ContactEditorSource());
public ListEditor<Contact, ContactEditorWrapper> asEditor() {
return editor;
}
}
ContactEditorWrapper:
class ContactEditorWrapper extends Composite implements ContactBaseEditor<Contact>, ValueAwareEditor<Contact> {
private SimplePanel panel = new SimplePanel();
#Path("") ContactBaseEditor<Contact> realEditor;
public ContactEditor() {
initWidget(panel);
}
#Override
public void setValue(Contact value) {
if (value instanceof Address) {
realEditor = new AddressEditor();
panel.setWidget((AddressEditor)realEditor);
}
else if (value instanceof Email) {
realEditor = new EmailEditor();
panel.setWidget((EmailEditor)realEditor);
}
else if (value instanceof PhoneNumber) {
realEditor = new PhoneNumberEditor();
panel.setWidget((PhoneNumberEditor)realEditor);
}
else {
realEditor = null;
}
}
}
Main Editor class:
public class AddEmployeeEditor extends Composite implements Editor<Employee> {
#UiField
ContactEditor contacts;
interface Driver extends SimpleBeanEditorDriver<Employee, AddEmployeeEditor> {
}
public AddEmployeeEditor(final Binder binder) {
driver = GWT.create(Driver.class);
driver.initialize(this);
List<Contact> list = new ArrayList<Contact>();
list.add(new Address());
list.add(new Email());
list.add(new PhoneNumber());
list.add(new PhoneNumber());
Employee employee = new Employee();
employee.setContacts(list);
driver.edit(employee);
}
}
Can anyone tell me if this will work, am I going in the right direction or ?
Thanks in advance,
Mac
I have updated the code above to now contain the ContactEditorWrapper class that Thomas advised.
That code has good chances to break: there's no guarantee that an editor returned by the EditorSource won't be used to edit another value in the list.
You should create a wrapper editor implementing ValueAwareEditor and with the actual editor as a child editor with #Path(""); and create the appropriate ContactBaseEditor in the setValue method.
class ContactEditor extends Composite implements ValueAwareEditor<Contact> {
private SimplePanel panel = new SimplePanel();
#Path("") ContactBaseEditor realEditor;
public ContactEditor() {
initWidget(panel);
}
#Override
public void setValue(Contact value) {
if (contact instanceof ContactAddress) {
realEditor = new AddressEditor();
}
else if (contact instanceof ContactEmail) {
realEditor = new EmailEditor();
}
else if (contact instanceof ContactPhoneNumber) {
realEditor = new PhoneNumberEditor();
}
else {
realEditor = null;
}
panel.setWidget(realEditor);
}
Note however that only the field/sub-editors from ContactBaseEditor will be edited, whichever the actual implementation being used. If there are additional fields/sub-editors in some ContactBaseEditor subclass, you'd have to implement ValueAwareEditor and handle things by-hand in the setValue and flush methods.

GWT-GIN Multiple Implementations?

I have the following code
public class AppGinModule extends AbstractGinModule{
#Override
protected void configure() {
bind(ContactListView.class).to(ContactListViewImpl.class);
bind(ContactDetailView.class).to(ContactDetailViewImpl.class);
}
}
#GinModules(AppGinModule.class)
public interface AppInjector extends Ginjector{
ContactDetailView getContactDetailView();
ContactListView getContactListView();
}
In my entry point
AppInjector appInjector = GWT.create(AppGinModule.class);
appInjector.getContactDetailsView();
Here ContactDetailView is always bind with ContactsDetailViewImpl. But i want that to bind with ContactDetailViewImplX under some conditions.
How can i do that? Pls help me.
You can't declaratively tell Gin to inject one implementation sometimes and another at other times. You can do it with a Provider or a #Provides method though.
Provider Example:
public class MyProvider implements Provider<MyThing> {
private final UserInfo userInfo;
private final ThingFactory thingFactory;
#Inject
public MyProvider(UserInfo userInfo, ThingFactory thingFactory) {
this.userInfo = userInfo;
this.thingFactory = thingFactory;
}
public MyThing get() {
//Return a different implementation for different users
return thingFactory.getThingFor(userInfo);
}
}
public class MyModule extends AbstractGinModule {
#Override
protected void configure() {
//other bindings here...
bind(MyThing.class).toProvider(MyProvider.class);
}
}
#Provides Example:
public class MyModule extends AbstractGinModule {
#Override
protected void configure() {
//other bindings here...
}
#Provides
MyThing getMyThing(UserInfo userInfo, ThingFactory thingFactory) {
//Return a different implementation for different users
return thingFactory.getThingFor(userInfo);
}
}