I'm using dagger2 in my application and I'm trying to inject a ViewModel into BottomSheetDialogFragment but I don't know how.
I have the BaseApplication class like this:
public class BaseApplication extends DaggerApplication {
#Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent
.builder()
.application(this)
.build();
}
}
And the ViewModelFactory:
#Module
public abstract class ViewModelFactoryModule {
#Binds
public abstract ViewModelProvider.Factory mBindViewModelFactory(ViewModelProviderFactory mModelProviderFactory);
}
When I try to inject the viewmodel inside the BottomSheetDialogFragment it shows null exception
public class BottomSheetMoreOptions extends BottomSheetDialogFragment {
#Inject
FeedViewModel ViewModel;
}
The ViewModel constructor is:
#Inject
public FeedViewModel(#Named("notificationsRef") DatabaseReference mRef) {
Log.d(TAG, "HomeViewModel: is ready...");
this.mRef = mRef;
}
Actually to load the bottom sheet I'm passing the view model in its constructor:
BottomSheetMoreOptions bottomSheetMoreOptions = new BottomSheetMoreOptions(model.getFeed().getId(), viewModel);
bottomSheetMoreOptions.show(requireActivity().getSupportFragmentManager(),
"ModalBottomSheet");
Any help please in Java?
Related
So i'm trying to inject an object with the #Inject annotation in a class that is not related to:
Application (mediante #HiltAndroidApp)
Activity
Fragment
View
Service
BroadcastReceiver
But i can't do it. Maybe i'am doing something wrong because i can do the injection via constructor.
As you can see. The #Inject annotated object is null but the object received from the constructor is created.
Here is my code:
ApplicationModule
#Module
#InstallIn(ApplicationComponent.class)
public class ApplicationModule {
#Provides
public Context providesApplicationContext(#ApplicationContext Context context) {
return context;
}
#Provides
public CompositeDisposable providesCompositeDisposable() {
return new CompositeDisposable();
}
...
}
HomeViewModel
public class HomeViewModel extends ViewModel {
private static final String TAG = "HomeViewModel";
private CatalogInteractor catalogInteractor;
#Inject
public CompositeDisposable compositeDisposable;
#Inject
public HomeViewModel(CatalogInteractor catalogInteractor, CompositeDisposable compositeDisposable) {
this.catalogInteractor = catalogInteractor;
}
...
}
HomeFragment
#AndroidEntryPoint
public class HomeFragment extends BaseFragment {
private static final String TAG = "HomeFragment";
private DrawerLayout drawerLayout;
HomeViewModel viewModel;
LoginViewModel loginViewModel;
ViewModelProvider.Factory factory;
#Inject
HomeViewModelFactory homeViewModelFactory;
#Inject
CompositeDisposable compositeDisposable; // Injected here just to validate the correct creation
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
OnBackPressedCallback callback = new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
closeDrawer();
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}
...
}
So i don't know if i'am doing something wrong or if it is a Hilt issue.
TLDR: I can inject fields with the #Inject annotation inside a fragment but i can't do it outside an android component class.
I create android ViewModel in Activity:
EventViewModel model = ViewModelProviders.of(this).get(EventViewModel.class);
For this I create EventViewModel :
public class EventViewModel extends ViewModel {
private final EventDao eventDao;
public EventViewModel(EventDao eventDao) {
this.eventDao = eventDao;
}
public void createEvent(final Event event) {
new Thread(new Runnable() {
#Override
public void run() {
eventDao.insert(event);
}
}).start();
}
}
I learned that I must create custom factory in order to inject my EventDao to EventViewModel. Ok, let's say i did it.
public class ViewModelFactory implements ViewModelProvider.Factory {
private final EventDao eventDao;
#Inject
public ViewModelFactory(EventDao eventDao) {
this.eventDao = eventDao;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
if (modelClass == EventViewModel.class) {
return (T) new EventViewModel(eventDao);
}
return null;
}
}
but what to do next? I see several ways. If belive to examples from google I must do next in MyActivity:
EventViewModel model = ViewModelProviders.of(this, new ModelFactory(eventDao)).get(EventViewModel.class);
1) But where do i get eventDao in MyActivity?
2) Do I need create custom ModelFactory for each ViewModel if it use dao class in?
I use Dagger 2 and I just want understand how can I create ViewModel with DAO and use this ViewModel in MyActivity?
You inject ViewModelFactory in the activity. ViewModelFactory will get EventDao through constructor injection. You pass the injected ViewModelFactory instance say viewModelFactory to ViewModelProviders
EventViewModel model = ViewModelProviders.of(this, viewModelFactory).get(EventViewModel.class);
I'm trying to learn dagger 2 but I'm confused in injecting of constructor with interface. This is my below code :
MainActivity.java
public class MainActivity extends AppCompatActivity implements MainView {
// this keyword of request dependency . At compiling process, dagger will look at all of these annotations
//to create the exact dependency
#Inject MainPresenter mainPresenter ;
TextView textView ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textview) ;
DaggerPresenterComponent.create().inject(this);
textView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
mainPresenter.doThings(8555) ;
}
});
}
/**********************************/
#Override
public void invokeRandomViewMethod(String msg) {
textView.setText(msg);
}
}
MainPresenter.java
public class MainPresenter {
private MainView mainView ;
#Inject
public MainPresenter(MainView mainView) {
this.mainView = mainView;
}
public void doThings(int value){
Random random = new Random();
int rand= random.nextInt(value) ;
if(mainView != null){
mainView.invokeRandomViewMethod("You random number is "+rand);
}
}
public interface MainView {
void invokeRandomViewMethod(String msg) ;
}
}
This is the Module :
#Module
public class PresenterModule {
#Provides
// this is the method that will provide the dependancy
MainPresenter provideMainPresenter(MainView mainView){
return new MainPresenter(mainView);
}
}
And this is the Component
#Component (modules = PresenterModule.class)
public interface PresenterComponent {
void inject(MainActivity activity) ;
}
When I run the code it shows me this error :
Error:(15, 10) error: com.imennmn.hellodagger2example.MainView cannot
be provided without an #Provides-annotated method.
com.imennmn.hellodagger2example.MainView is injected at
com.imennmn.hellodagger2example.presenterInjection.PresenterModule.provideMainPresenter(mainView)
com.imennmn.hellodagger2example.MainPresenter is injected at
com.imennmn.hellodagger2example.MainActivity.mainPresenter
com.imennmn.hellodagger2example.MainActivity is injected at
com.imennmn.hellodagger2example.simpleInjection.DataComponent.inject(activity)
My Question is how I can provide the interface MainView by inject it with dagger and bind the MainPresenter and MainActivity ?
Any help would be appreciated !
By following code:
MainPresenter provideMainPresenter(MainView mainView) {
return new MainPresenter(mainView);
}
You are telling dagger: "hey, whenever I ask you to inject MainPresenter, construct it using MainView".
But dagger complaints, because you haven't specified how exactly he should build/acquire MainView.
So, in your PresenterModule do this:
#Module
public class PresenterModule {
MainView mainView;
public PresenterModule(MainView mainView) {
this.mainView = mainView;
}
#Provides
MainPresenter provideMainPresenter() {
return new MainPresenter(mainView);
}
}
Then when building the component:
DaggerPresenterComponent.builder()
.presenterModule(new PresenterModule(this))
.build();
Your provideMainPresenter implicitly depends on a MainView. Dagger has no way to get it. You need to add a method to provide it:
#Module
public class PresenterModule {
#Provides
MainView provideMainView(){
// Provide MainView here somehow so Dagger can use this to create a MainPresenter
}
#Provides
// this is the method that will provide the dependancy
MainPresenter provideMainPresenter(MainView mainView){
return new MainPresenter(mainView);
}
}
Add abstract module with #Binds annotation, look at my impl of : AbstractTestSettingsFragmentModule.java
TestFragment.java
public class TestFragment extends Fragment{
#Inject TestFragmentContract.Presenter mPresenter;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidSupportInjection.inject(this);
}
}
TestFragmentPresenterImpl.java
public class TestFragmentPresenterImpl implements TestFragmentContract.Presenter {
#Inject
public TestFragmentPresenterImpl(){
}
}
AbstractTestSettingsFragmentModule.java
#Module
public abstract class AbstractTestSettingsFragmentModule {
#Binds
#NonNull
public abstract TestSettingsFragmentContract.Presenter testSettingsFragmentPresenterImpl(TestSettingsFragmentImpl presenter);
}
ContributesModule.java
#Module
public abstract class ContributesModule {
#ContributesAndroidInjector(modules = {AbstractTestSettingsFragmentModule.class})
abstract TestSettingsFragment testSettingsFragment();
}
AppComponent.java
#Singleton
#Component(
modules = {
AndroidSupportInjectionModule.class,
ContributesModule.class,
AppModule.class,
})
public interface AppComponent extends AndroidInjector<DaggerApplication> {
void inject(TheApplication theApplication);
#Override
void inject(DaggerApplication instance);
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
}
You can use Assisted Injection with Dagger: https://dagger.dev/dev-guide/assisted-injection.html
Assisted injection is a dependency injection (DI) pattern that is used to construct an object where some parameters may be provided by the DI framework and others must be passed in at creation time (a.k.a “assisted”) by the user.
I try to create sample application with dagger 2 using mvp & RXAndroid, every thing work correctly but I cannot able to inject Activity the following is my AppComponent
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
void inject(App app);
void inject(MainActivity activity);
void inject(ResponseService service);
void inject(MainPresenter presenter);
}
and the following is my Module
#Module
public class AppModule {
private App app;
public AppModule(App app) {
this.app = app;
}
private static final String API_ENDPOINT = "url here";
#Provides
#Singleton
public ApiService apiService() {
OkHttpClient client = new OkHttpClient();
Gson gson = new GsonBuilder().registerTypeAdapterFactory(new ClassTypeAdapterFactory())
.registerTypeAdapter(Class.class, new ClassTypeAdapter()).create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_ENDPOINT)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
return retrofit.create(ApiService.class);
}
#Provides
#Singleton
ResponseService responseService() {
return new ResponseService(app.getComponent());
}
#Provides
#Singleton
MainPresenter mainPresenter() {
return new MainPresenter(app.getComponent());
}
#Provides
#Singleton
EventBus eventBus() {
return EventBus.getDefault();
}
}
I inject all things correctly and can work with them except ManiActivity when try to use it give me null pointer the following how i inject it
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((App) getApplicationContext())
.getComponent()
.inject(this);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initRecyclerView();
presenter.setView(this);
}
but when try to use it as Context in My adapter and pass it to Picasso library give me the following exception java.lang.IllegalArgumentException: Context must not be null.
the following is how i use it
#Inject
MainActivity activity;
and use it in onBindViewHolder as following
Picasso.with(activity).load(response).fit().into(holder.ivCover);
My adapter code
public class ReAdapter extends RecyclerView.Adapter<ReAdapter.RViewHolder> {
private List<Response> responseList;
#Inject
MainActivity appContext;
public ReAdapter() {
}
public void setResponseList(List<Response> responseList) {
this.responseList = responseList;
notifyDataSetChanged();
}
#Override
public RViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.response_item, parent, false);
return new RViewHolder(view);
}
#Override
public void onBindViewHolder(RViewHolder holder, int position) {
final Response response = responseList.get(position);
Picasso.with(appContext).load(response.getValue().toLowerCase()).fit().into(holder.ivd);
}
#Override
public int getItemCount() {
return responseList != null ? responseList.size() : 0;
}
public class RViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.ivd)
ImageView ivd;
public RViewHolder(View view) {
super(view);
ButterKnife.bind(this, itemView);
}
}
}
Can anyone help me to solve this issue ?
Your activity: MainActivity doesn't have an #Inject annotated constructor (and can't have, because it's created by the system).
In your AppModule there is no #Provides annotated method that returns MainActivity.
You are injecting the fields in your activity not in your adapter.
The result is Dagger has no idea how to create an object of type MainActivity.
The solution for this specific problem is to use the application context for Picasso or even better create a Picasso object with Dagger.
Update your AppModule with:
#Provides
#Singleton
Picasso providePicasso(App app) {
return Picasso.with(app);
}
In MainActivity add a field:
#Inject
ReAdapter adapter;
In ReAdapter modify the constructor and add a field for picasso:
private final Picasso picasso;
#Inject
public ReAdapter(Picasso picasso) {
this.picasso = picasso;
}
This way Dagger can create a singleton Picasso instance. The ReAdapter is annotated with #Inject (using constructor injection) so Dagger knows how to create it. By adding a ReAdapter field in MainActivity when you call component.inject(this) in MainActivity the adapter field will be initialized.
First time using Dagger2.
In my android application I have a MyApplication class that extends Application.
I also have an ImageAssistant class that is a collection of related image-processing methods.
In my MyApplicaiton class I used to instantiate an ImageAssistant for all the activities to use.
Now I am trying to make it work with Dagger2, but I dont know how to pass a context in the module that provides ImageAssistant
This is how my code looked:
public class ImageAssistant {
Context context;
public ImageAssistant(Context context){
this.context = context;
}
// A bunch of methods...
}
public class MyApplication extends Application {
public ImageAssistant imageAssistant;
public void onCreate() {
imageAssistant = new ImageAssistant(this);
}
}
Now, enter Dagger 2, here is what I have
public class ImageAssistant {
Context context;
#Inject
public ImageAssistant(Context context){
this.context = context;
}
// A bunch of methods...
}
public class MyApplication extends Application {
#Inject
public ImageAssistant imageAssistant;
public void onCreate() {
}
}
in package .modules:
AppModule.java
#Module
public class AppModule {
#Provides
ImageAssistant provideImageAssistant() {
return new ImageAssistant(); // HERE A CONTEXT IS NEEDED. WHERE TO GET IT FROM?
}
}
EDIT: This is how my module looks now, but I still dont know how to tie everything together:
#Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
#Provides
Context provideApplicationContext() {
return this.application;
}
#Provides
ImageAssistant provideImageAssistant(ImageAssistant imageAssistant) {
return imageAssistant;
}
}
And this is the AppComponent:
#Singleton
#Component(modules = {AppModule.class})
public interface AppComponent {
ImageAssistant provideImageAssistant();
Context context();
}
Your module should look like this:
#Module(injects = {MainActivity.class})
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
#Provides
public ImageAssistant provideImageAssistant() {
return new ImageAssistantImpl(application); // implementation of ImageAssistant
}
}
Usage in Activity:
public class MainActivity extends Activity{
#Inject
ImageAssistant imageAssistant;
#Override
protected void onCreate(Bundle savedInstanceState) {
SharedObjectGraph.inject(this);
imageAssistant.doSomething();
}
}
Don't forget to init the ObjectGraph in Application class. I use SharedObjectGraph static class to share ObjectGraph created from AppModule across the whole app.
Read building the graph section.
I did something like this:
#Module
public class ApplicationModule {
private final SpendApplication app;
public ApplicationModule(SpendApplication app) {
this.app = app;
}
#Provides
#Singleton
public Context providesContext() {
return app;
}
}
and:
#Module
public class GattManagerModule {
#Provides
#Singleton
GattManager providesGattManager(Context context) {
return new GattManager(context);
}
}
And declare all places where it's used:
#Component(modules = { ApplicationModule.class, GattManagerModule.class, ...})
#Singleton
public interface ApplicationComponent {
void inject(MainScreenActivity activity);
...
}
In my app application class:
#Override
public void onCreate() {
super.onCreate();
component = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
And in my MainActivity:
#Inject
GattManager gattManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getAppComponent().inject(this);
}