I'm trying to implement MVP pattern using Dagger2. While I successfully did di for application, activities and for fragments (I'm not sure I did well with fragments). Actually after reading guides I still don't understand how it works.
I created RetrofitModiule:
#Module
public class RetrofitModule {
String mBaseUrl;
...
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(mBaseUrl)
.client(okHttpClient)
.build();
}
Then I declare module in MyApplicationComponent:
#Singleton
#Component(
modules = {
MyApplicationModule.class,
RetrofitModule.class
}
)
public interface MyApplicationComponent {
void inject(MyApplication myApplication);
Retrofit provideRetrofit();
}
Actually I don't understand why I have to use Inject here; Because there nothing to actually inject into MyApplication:
public class MyApplication extends Application {
private MyApplicationComponent mMyApplicationComponent;
...
#Override
public void onCreate() {
super.onCreate();
mMyApplicationComponent = DaggerMyApplicationComponent.builder()
.retrofitModule(new RetrofitModule("https://androidtutorialpoint.com"))
.build();
mMyApplicationComponent.inject(this);
}
}
I use Retrofit only in LoaderActivityPresenterImpl which injected to LoaderActivity;
#ActivityScoped
public class LoaderActivityPresenterImpl implements LoaderActivityPresenter {
private LoaderActivityView mView;
private #ActivityContext Context mContext;
private Retrofit mRetrofit;
#Inject
public LoaderActivityPresenterImpl(LoaderActivityView view, #ActivityContext Context context, Retrofit retrofit) {
mView = view;
mContext = context;
mRetrofit = retrofit;
}
}
LoaderActivity:
public class LoaderActivity extends BaseActivity implements LoaderActivityView{
#Inject LoaderActivityPresenter mPresenter;
private LoaderActivityComponent mLoaderActivityComponent;
#Override
protected void setupActivityComponent(MyApplicationComponent myApplicationComponent) {
mLoaderActivityComponent = DaggerLoaderActivityComponent.builder()
.myApplicationComponent(myApplicationComponent)
.loaderActivityModule(new LoaderActivityModule(this, this, myApplicationComponent.provideRetrofit()))
.build();
mLoaderActivityComponent.inject(this);
}
LoaderComponent:
#ActivityScoped
#Component(
modules = LoaderActivityModule.class,
dependencies = MyApplicationComponent.class
)
public interface LoaderActivityComponent {
void inject(LoaderActivity loaderActivity);
}
LoaderActivityModule:
#Module
public class LoaderActivityModule {
private Retrofit mRetrofit;
private LoaderActivityView mLoaderActivityView;
private #ActivityContext Context mContext;
public LoaderActivityModule(LoaderActivityView loaderActivityView, #ActivityContext Context context, Retrofit retrofit) {
mLoaderActivityView = loaderActivityView;
mContext = context;
mRetrofit = retrofit;
}
#Provides
LoaderActivityView provideLoaderActivityView() {
return mLoaderActivityView;
}
#Provides
public #ActivityContext Context provideActivityContext() {
return mContext;
}
#Provides
public LoaderActivityPresenter LoaderActivityPresenterImpl() {
return new LoaderActivityPresenterImpl(mLoaderActivityView, mContext, mRetrofit);
}
}
LoaderActivityComponent:
#ActivityScoped
#Component(
modules = LoaderActivityModule.class,
dependencies = MyApplicationComponent.class
)
public interface LoaderActivityComponent {
void inject(LoaderActivity loaderActivity);
}
I get this error:
java.lang.RuntimeException: Unable to create application com.xxxxx.application.MyApplication: java.lang.IllegalStateException: com.xxxxx.di.modules.MyApplicationModule must be set;
I can probably forget to show some classes, so feel free to ask me.
As the error says, you forget to add your ApplicationModule to your component.
By the way, I highly suggest you take a look at AndroidInjector, to avoid creating this Android component hierarchy manually.
public class MyApplication extends Application {
private MyApplicationComponent mMyApplicationComponent;
...
#Override
public void onCreate() {
super.onCreate();
mMyApplicationComponent = DaggerMyApplicationComponent.builder()
.myApplicationModule()
.retrofitModule(new RetrofitModule("https://androidtutorialpoint.com"))
.build();
mMyApplicationComponent.inject(this);
Related
In my MVP architecture i have Retrofit Instance
public class RetrofitInstance {
private static Retrofit retrofit;
private static final String BASE_URL = "http://api.openweathermap.org/data/2.5/";
/**
* Create an instance of Retrofit object
* */
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
return retrofit;
}
}
And DataService for it
public interface GetNoticeDataService {
#GET("weather?appid=0194877ecdcac230396a119c01d46100")
Observable<NoticeList> getNoticeData(#Query("lat") double lat , #Query("lon") double lon );
}
Also i have DataInteractor which is using RxJava Observable service to call api
public class GetNoticeIntractorImpl implements MainContract.GetNoticeIntractor {
private LatLng getloc(){
return currentLocation;
}
#SuppressLint("CheckResult")
#Override
public void getNoticeArrayList(final OnFinishedListener onFinishedListener) {
/** Create handle for the RetrofitInstance interface*/
GetNoticeDataService service = RetrofitInstance.getRetrofitInstance().create(GetNoticeDataService.class);
/** Using RxJava Observable response to handle retrofit api*/
if(currentLocation!=null) {
service.getNoticeData(getloc().latitude, getloc().longitude)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> onFinishedListener.onFinished(items.getNoticeArrayList(), items.getMain(), items.getWind()), onFinishedListener::onFailure);
}
}
}
Here is presenter
public class MainPresenterImpl implements MainContract.presenter, MainContract.GetNoticeIntractor.OnFinishedListener {
private MainContract.MainView mainView;
private MainContract.GetNoticeIntractor getNoticeIntractor;
#Inject
public MainPresenterImpl(MainContract.MainView mainView, MainContract.GetNoticeIntractor getNoticeIntractor) {
this.mainView = mainView;
this.getNoticeIntractor = getNoticeIntractor;
}
#Override
public void onDestroy() {
mainView = null;
}
#Override
public void onRefreshButtonClick() {
if(mainView != null){
mainView.showProgress();
}
getNoticeIntractor.getNoticeArrayList(this);
}
#Override
public void requestDataFromServer() {
getNoticeIntractor.getNoticeArrayList(this);
}
#Override
public void onFinished(ArrayList<Notice> noticeArrayList, Main main, Wind wind) {
if(mainView != null){
mainView.setDataToRecyclerView(noticeArrayList,main,wind);
mainView.hideProgress();
}
}
#Override
public void onFailure(Throwable t) {
if(mainView != null){
mainView.onResponseFailure(t);
mainView.hideProgress();
}
}
}
And MainContract
public interface MainContract {
/**
* Call when user interact with the view and other when view OnDestroy()
* */
interface presenter{
void onDestroy();
void onRefreshButtonClick();
void requestDataFromServer();
}
/**
* showProgress() and hideProgress() would be used for displaying and hiding the progressBar
* while the setDataToRecyclerView and onResponseFailure is fetched from the GetNoticeInteractorImpl class
**/
interface MainView {
void showProgress();
void hideProgress();
void setDataToRecyclerView(ArrayList<Notice> noticeArrayList, Main main, Wind wind);
void onResponseFailure(Throwable throwable);
}
/**
* Intractors are classes built for fetching data from your database, web services, or any other data source.
**/
interface GetNoticeIntractor {
interface OnFinishedListener {
void onFinished(ArrayList<Notice> noticeArrayList, Main main, Wind wind);
void onFailure(Throwable t);
}
void getNoticeArrayList(OnFinishedListener onFinishedListener);
}
}
Adapter for MyActivity
public class NoticeAdapter extends RecyclerView.Adapter<NoticeAdapter.EmployeeViewHolder> {
private static Wind wind;
private static ArrayList<Notice> dataList;
private static Main main;
private Date currentTime = Calendar.getInstance().getTime();
public static String date;
private Context mContext;
private RecyclerItemClickListener recyclerItemClickListener;
public NoticeAdapter(ArrayList<Notice> dataList, Main main, Wind wind, RecyclerItemClickListener recyclerItemClickListener,Context context) {
NoticeAdapter.dataList = dataList;
NoticeAdapter.main = main;
NoticeAdapter.wind = wind;
this.recyclerItemClickListener = recyclerItemClickListener;
this.mContext=context;
}
#NonNull
#Override
public EmployeeViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.single_view_row, parent, false);
return new EmployeeViewHolder(view);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull EmployeeViewHolder holder, #SuppressLint("RecyclerView") final int position) {
setDate(currentTime.toString().substring(0,currentTime.toString().length()-18));
if(getAddressMap()!=null){holder.txtNoticeAddress.setText("Loc: "+getAddressMap());}else{holder.txtNoticeAddress.setText("Loc: Unknown location");}
holder.imageIcon.setImageURI(Uri.parse("android.resource://com.locweather/drawable/i"+dataList.get(position).getIcon()));
holder.txtNoticeWind.setText("Wind: "+roundUp(+wind.getSpeed())+"m/s, "+arrow());
holder.txtNoticeTempMain.setText(roundUp(+main.getTemp())+"°C");
holder.txtNoticeWeather.setText(dataList.get(position).getWeather()+" : "+dataList.get(position).getInfo());
holder.txtNoticeTemp.setText("Feels: "+roundUp(+main.getFeelsLike())+"°C ");
holder.txtNoticeTime.setText(date);
holder.txtNoticeHumidity.setText("Humidity: "+main.getHumidity()+"%");
holder.txtNoticePressure.setText("Pressure: "+main.getPressure()+"hPa");
holder.itemView.setOnClickListener(v -> {
recyclerItemClickListener.onItemClick();
saveNoticeList(mContext,dataList); });
holder.saveButton.setOnClickListener(v -> {
recyclerItemClickListener.onItemClick();
saveNoticeList(mContext,dataList); });
}
private static String getAddressMap() {
return MapsActivity.addressMap;
}
private static void setDate(String date) {
NoticeAdapter.date = date;
}
#Override
public int getItemCount() {
return dataList.size();
}
private static LatLng getloc(){
return currentLocation;
}
class EmployeeViewHolder extends RecyclerView.ViewHolder {
ImageView imageIcon;
Button saveButton;
TextView txtNoticeWeather, txtNoticeTempMain,txtNoticeTemp, txtNoticeHumidity,txtNoticeAddress,txtNoticePressure,txtNoticeWind,txtNoticeTime;
EmployeeViewHolder(View itemView) {
super(itemView);
saveButton=itemView.findViewById(R.id.save_button);
imageIcon=itemView.findViewById(R.id.image_icon);
txtNoticeTime= itemView.findViewById(R.id.txt_time);
txtNoticeWind= itemView.findViewById(R.id.txt_notice_wind);
txtNoticeAddress= itemView.findViewById(R.id.txt_notice_title);
txtNoticeWeather = itemView.findViewById(R.id.txt_notice_weather);
txtNoticeTemp = itemView.findViewById(R.id.txt_notice_temp);
txtNoticeHumidity = itemView.findViewById(R.id.txt_notice_humidity);
txtNoticePressure = itemView.findViewById(R.id.txt_notice_pressure);
txtNoticeTempMain = itemView.findViewById(R.id.txt_notice_temp_main);
}
}
private static void saveNoticeList(Context context, List<Notice> noticeList) {
if (context != null && noticeList != null) {
WeatherData weatherData = new WeatherData(getAddressMap(), wind.getSpeed(), wind.getDeg(), dataList.get(0).getIcon(), dataList.get(0).getInfo(), dataList.get(0).getWeather(), main.getTemp(), main.getFeelsLike(), main.getHumidity(), main.getPressure(), date, getloc().latitude, getloc().longitude);
WeatherDatabase.getInstance(context)
.weatherDao()
.save(weatherData);
}
}
How can i inject this DataService to my Interactor by using Dagger2? Should i use Singleton or Component or something else?
I'll be glad of any kind of help.
I'll assume you want to keep your RetrofitInstance singleton available for now, at least until you have migrated all Retrofit uses to Dagger 2.
In order to inject an interface, or a class whose constructor you don't control, you need to use a Module. This tells Dagger how to create instances of that class or interface given some dependencies:
#Module
public class WebServiceModule {
#Singleton
#Provides
Retrofit provideRetrofit() {
// This method tells Dagger all it needs to know about creating
// a Retrofit instance. This will be replaced by something closer
// to BWappsandmore's answer after RetrofitInstance is no longer needed.
return RetrofitInstance.getRetrofitInstance();
}
#Singleton
#Provides
GetNoticeDataService provideGetNoticeDataService(Retrofit retrofit) {
return retrofit.create(GetNoticeDataService.class);
}
This module can then be included in your Component, which will in turn create any objects it knows how to create, either from modules, #BindsInstance values passed into your component factory method, or classes with #Inject constructors. Since you control your interactor's constructor, you can simply annotate it with #Inject:
// #Reusable or #Singleton if you only need one interactor of this type.
public class GetNoticeInteractorImpl implements MainContract.GetNoticeInteractor {
private GetNoticeDataService service
#Inject
GetNoticeInteractorImpl(GetNoticeDataService service) {
this.service = service;
}
private LatLng getloc(){
return currentLocation;
}
#SuppressLint("CheckResult")
#Override
public void getNoticeArrayList(final OnFinishedListener onFinishedListener) {
// Our service was injected in the constructor, so there
// is no need to create it here.
// You might also consider injecting your schedulers in the future
// for unit testing.
if(currentLocation!=null) {
service.getNoticeData(getloc().latitude, getloc().longitude)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> onFinishedListener.onFinished(items.getNoticeArrayList(), items.getMain(), items.getWind()), onFinishedListener::onFailure);
}
}
}
#Module
abstract class AnotherModule { // or interface
#Binds
abstract MainContract.GetNoticeInteractor bindGetNoticeInteractor(GetNoticeInteractorImpl implementation);
}
Of course, this is useless if you can't ultimately access your interactor. Since you don't even create your Activity classes, you need to request injection from a Component. In Android apps, this is typically created within the Application object in onCreate().
#Component(modules = {WebServicesModule.class, AnotherModule.class})
#Singleton
public interface AppComponent {
#Component.Factory
interface Factory {
AppComponent create(#BindsInstance Application application); // or whatever
}
void bindMainActivity(MainActivity activity);
}
// Probably not a singleton, but if you create an activity scope later, this might use that scope.
class MainPresenter {
#Inject
public MainPresenter(MainContract.GetNoticeInteractor interactor) {
this.getNoticeInteractor = interactor
}
// ...
}
class MainActivity {
#Inject
MainPresenter presenter;
#Override
public void onCreate(Bundle savedInstanceState) {
somehowGetComponent().inject(this);
super(savedInstanceState);
// ...
}
// ...
}
Now, since your MainActivity requires a MainPresenter, Dagger will create one for you when you request injection. This in turn requires a GetNoticeInteractor, which requires GetNoticeInteractorImpl, and so forth, and Dagger will take care of all of this behind the scenes.
If your presenter's constructor requires other arguments (such as a View), it might be simpler for now for MainActivity to request injection directly into the presenter. Ultimately, you will want all of your inject(SomeClass target) methods to refer to Activities, Services, and such, since there is no other way to inject those until your minSdkVersion is 28 or higher.
#Module
object WebServiceModule {
#Singleton
#Provides
fun providesGetNoticeDataService(retrofit: Retrofit): GetNoticeDataService =
retrofit.create<GetNoticeDataService>(GetNoticeDataService::class.java)
#Provides
fun providesGsonConverterFactory(): GsonConverterFactory = GsonConverterFactory.create()
#Provides
fun providesOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient =
OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build()
#Provides
fun providesOkHttpLoggingInterceptor(): HttpLoggingInterceptor =
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
#Provides
fun provideRxJava2CallAdapterFactory(): RxJava2CallAdapterFactory =
RxJava2CallAdapterFactory.create()
#Provides
fun providesRetrofit(
client: OkHttpClient,
converterFactory: GsonConverterFactory,
adapterFactory: RxJava2CallAdapterFactory
): Retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(converterFactory)
.addCallAdapterFactory(adapterFactory)
.client(client)
.build()
}
After successful implementing Dagger on project i have to specify dagger to each of class i want to use and inject modules, for example RestClient of retrofit, i want to know is any way to define automatically components into classes?
for example my implementation is:
public class CoreApplication extends MultiDexApplication {
private static ProjectApplicationComponent component;
private RestClient restClient;
private Picasso picasso;
private Handler handler;
#Override
public void onCreate() {
super.onCreate();
...
component = DaggerProjectApplicationComponent.builder()
.contextModule(new ContextModule(this))
.networkServiceModule(new NetworkServiceModule(ClientSettings.SERVER_URL))
.build();
restClient= component.apiService();
picasso = component.getPicasso();
handler = component.getHandler();
}
public static ProjectApplicationComponent getComponent() {
return component;
}
}
and my ApplicationComponent which i define witch class or activity or fragment i want to inject modules:
#ActivitiesScope
#Component(dependencies = ProjectApplicationComponent.class)
public interface ApplicationComponent {
void inject(PersonsRemoteRepository personsRemoteRepository);
}
and PersonsRemoteRepository class which i want to inject RestClient to use Retrofit
public class PersonsRemoteRepository implements PersonsRepository {
#Inject
private RestClient restClient;
private final ApplicationComponent component;
public PersonsRemoteRepository() {
component = DaggerApplicationComponent.builder()
.projectApplicationComponent(CoreApplication.getComponent())
.build();
component.inject(this);
}
...
}
my RestClient class is:
public interface RestClient {
#Headers("Content-Type: application/json")
#POST("/api/v1/getPersons")
Observable<List<Person>> getPersons();
}
my mean is removing component and component.inject(this); from all of classes that i want to inject RestClient
#Inject
private RestClient restClient;
for example simplified PersonsRemoteRepository class should be:
public class PersonsRemoteRepository implements PersonsRepository {
#Inject
private RestClient restClient;
public PersonsRemoteRepository() {
}
...
}
Thanks in advance
UPDATE POST
in this my activity inject(this) is not available on this line of code:
CoreApplication.getComponent().inject(this);
My activity:
public class LoginActivity extends AppCompatActivity{
#Inject
PersonsRemoteRepository personsRemoteRepository;
#Inject
RestClient restClient;
private LoginActivityBinding mBinding;
private LoginMethodsToPageViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CoreApplication.getComponent().inject(this);
mBinding = DataBindingUtil.setContentView(this, R.layout.login_activity);
personsRemoteRepository = new PersonsRemoteRepository(restClient);
viewModel = new LoginMethodsToPageViewModel(personsRemoteRepository, this, mBinding);
mBinding.setViewModel(viewModel);
}
...
}
in this screen shot as you see i dont have inject() method
PersonsRemoteRepository class after change:
public class PersonsRemoteRepository implements PersonsRepository {
private RestClient restClient;
#Inject
PersonsRemoteRepository(RestClient restClient) {
this.restClient = restClient;
}
#SuppressWarnings("unchecked")
#Override
public Observable<List<Person>> getAllPersons() {
Observable<List<Person>> observable = restClient.getPersons();
return observable
.flatMap((Function<List<Person>, Observable<List<Person>>>) Observable::fromArray);
}
}
There are two questions, how to inject into CoreApplication and how to inject into activities. And there are two corresponding components, ProjectApplicationComponent and ApplicationComponent, connected by component dependency.
To Inject into the application, the answer from Gustavo is useful:
Annotate fields of CoreApplication as #Inject,
Replace provision methods in ProjectApplicationComponent with a members-injection method:
#ApplicationScope
#Component(
modules = {
ContextModule.class,
NetworkServiceModule.class,
...,
})
public interface ProjectApplicationComponent {
// Members-injection method
void inject(CoreApplication coreApplication);
}
Construct a ProjectApplicationComponent and call the inject method:
// CoreApplication.onCreate
component =
DaggerProjectApplicationComponent.builder()
.contextModule(new ContextModule(this))
.networkServiceModule(...)
.build();
component.inject(/* coreApplication= */ this);
To inject into LoginActivity, the depending ApplicationComponent should have a members-injection method:
#ActivitiesScope
#Component(dependencies = ProjectApplicationComponent.class)
public interface ApplicationComponent {
void inject(LoginActivity loginActivity);
}
Recall that your LoginActivity has two #Injected fields, with types RestClient and PersonsRemoteRepository.
public class LoginActivity extends AppCompatActivity {
#Inject PersonsRemoteRepository personsRemoteRepository;
#Inject RestClient restClient;
}
In order for the depending ApplicationComponent to obtain a RestClient, the depended ProjectApplicationComponent should expose a provision method:
#ApplicationScope
#Component(modules = {...})
public interface ProjectApplicationComponent {
// Members-injection method
void inject(CoreApplication coreApplication);
// Provision method
RestClient getRestClient();
}
For PersonsRemoteRepository, Dagger can construct one using constructor injection:
// May be scoped #ActivitiesScope, or not
public class PersonsRemoteRepository implements PersonsRepository {
private final RestClient restClient;
#Inject
PersonsRemoteRepository(RestClient restClient) {
this.restClient = restClient;
}
}
Then when you create your LoginActivity, build the Dagger-generated component as follows:
// LoginActivity.onCreate
ApplicationComponent component =
DaggerApplicationComponent.builder()
.projectApplicationComponent(CoreApplication.getComponent())
.build();
component.inject(/* loginActivity= */ this);
You don't need to build your component for every class that you want to inject dependencies. The dependencies can be provided via constructor annotated with #Inject:
public class PersonsRemoteRepository implements PersonsRepository {
private RestClient restClient;
#Inject
public PersonsRemoteRepository(RestClient restClient) {
this.restClient = restClient;
}
}
And any other class that needs this repository can do the same:
public class AnyOtherClass {
private PersonsRemoteRepository personsRemoteRepository;
#Inject
public AnyOtherClass(PersonsRemoteRepository personsRemoteRepository) {
this.personsRemoteRepository = personsRemoteRepository;
}
You only need to use component.inject for classes that instances are created by Android, like Application, Activities and Fragments.
public class MyActivity {
#Inject PersonsRemoteRepository personsRemoteRepository;
#Override
public void onCreate() {
super.onCreate();
CoreApplication.getComponent().inject(this);
}
}
Changes needed in your CoreApplication:
public class CoreApplication extends MultiDexApplication {
private static ProjectApplicationComponent component;
#Inject private RestClient restClient;
#Inject private Picasso picasso;
#Inject private Handler handler;
#Override
public void onCreate() {
super.onCreate();
...
component = DaggerProjectApplicationComponent.builder()
.contextModule(new ContextModule(this))
.networkServiceModule(new NetworkServiceModule(ClientSettings.SERVER_URL))
.build();
component.inject(this);
}
}
Changes needed in your ApplicationComponent:
#ActivitiesScope
#Component(dependencies = ProjectApplicationComponent.class)
public interface ApplicationComponent {
void inject(CoreApplication coreApplication);
void inject(MyActivity myActivity);
}
I have an application which fetches the type ahead search values.I m using dagger for dependency injection.However dagger is injecting the values for activity class but not injecting for other classes
Modules
#Module
public class NetModule {
String baseURL;
public NetModule(String baseURL){
this.baseURL=baseURL;
}
#Provides
#Singleton
SharedPreferences providesSharedPreferences(Application application) {
return PreferenceManager.getDefaultSharedPreferences(application);
}
#Provides
#Singleton
Cache provideOkHttpCache(Application application) {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(application.getCacheDir(), cacheSize);
return cache;
}
#Provides
#Singleton
Gson provideGson(){
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
return gsonBuilder.create();
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.cache(cache);
return client.build();
}
#Provides
#Singleton
Retrofit provideRetrofit(Gson gson, OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(baseURL)
.client(okHttpClient)
.build();
return retrofit;
}
#Provides
#Singleton
GetTypeAhead provideRGetTypeAhead( Retrofit retrofit) {
return retrofit.create(GetTypeAhead.class);
}
}
My app module
#Module
public class AppModule {
Application mApplication;
public AppModule(Application application) {
mApplication = application;
}
#Provides
#Singleton
Application providesApplication() {
return mApplication;
}
}
my component
#Singleton
#Component(modules={AppModule.class, NetModule.class})
public interface NetComponent {
void inject(MainActivity dataSource);
}
MyTopLevel application component for dagger initialization
public class MyApp extends Application {
private NetComponent netComponent;
#Override
public void onCreate() {
super.onCreate();
// Dagger%COMPONENT_NAME%
netComponent = DaggerNetComponent.builder()
// list of modules that are part of this component need to be created here too
.appModule(new AppModule(this)) // This also corresponds to the name of your module: %component_name%Module
.netModule(new NetModule("https://typeahead.random.com/"))
.build();
// If a Dagger 2 component does not have any constructor arguments for any of its modules,
// then we can use .create() as a shortcut instead:
// mNetComponent = com.codepath.dagger.components.DaggerNetComponent.create();
}
public NetComponent getNetComponent() {
return netComponent;
}
}
My activity //here di is working fine
public class MainActivity extends AppCompatActivity {
#Inject
Retrofit retrofit;
CompositeDisposable compositeDisposable = new CompositeDisposable();
private ArrayList<String> arr = new ArrayList<>();
SearchViewModel searchViewModel;
AutoCompleteTextView textView;
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
searchViewModel = ViewModelProviders.of(this).get(SearchViewModel.class);
((MyApp)getApplication()).getNetComponent().inject(this);
Observable<String> inputObservable = setUpTextObservable();
textView= (AutoCompleteTextView) findViewById(R.id.productSearchText);
adapter = new ArrayAdapter<>(this,
android.R.layout.simple_dropdown_item_1line);
Log.v("++++++++++++++++++++++",retrofit.toString());
compositeDisposable.add(searchTextObserver(inputObservable));
}
.....}
My other class //here injection is not working
public class RemoteDataSource {
#Inject
GetTypeAhead getTypeAhead;
Single<TypeAhead> getTypeAhead(String input){
return getTypeAhead.getTypeAhead(input).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
}
what am i doing wrong here. Guide me to somelink if i m lacking some concept
If you use property injection follow next step. In NetComponent add void inject(RemoteDataSource dataSource);
#Singleton
#Component(modules={AppModule.class, NetModule.class})
public interface NetComponent {
void inject(MainActivity dataSource);
void inject(RemoteDataSource dataSource);
}
In RemoteDataSource class define contructor and perform injection in it
class RemoteDataSource {
public RemoteDataSource() {
NetComponent netComponent = // TODO get your component
netComponent.inject(this)
}
}
You can find additional info in Dagger Documentation.
Note that it's not the best way to use property injection in your way. I advise you use constructor injection that decribed more in discussion
To add your RemoteDataSource class to graph, you need to add #Inject annotation to its constructor and anotate class with scope :
#Singleton
public class RemoteDataSource {
GetTypeAhead getTypeAhead;
#Inject
RemoteDataSource(GetTypeAhead getTypeAhead){
this.getTypeAhead = getTypeAhead;
}
//...
}
}
I would like to implement Repository module to handle data operations. I have JSON file in row directory and want create concrete Repository implementation to get data from file. I'm not sure if I can use Context as attribute in the constructor or method of Repository.
e.g.
public class UserRepository {
UserRepository() {}
public List<User> loadUserFromFile(Context contex) {
return parseResource(context, R.raw.users);
}
}
IMHO, you should use DI (Dependency Injection) like Dagger2, to provide you Context something like,
AppModule.class
#Module
public class AppModule {
private Context context;
public AppModule(#NonNull Context context) {
this.context = context;
}
#Singleton
#Provides
#NonNull
public Context provideContext(){
return context;
}
}
MyApplication.class
public class MyApplication extends Application {
private static AppComponent appComponent;
public static AppComponent getAppComponent() {
return appComponent;
}
#Override
public void onCreate() {
super.onCreate();
appComponent = buildComponent();
}
public AppComponent buildComponent(){
return DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
}
UserRepository.class
#Singleton
public class UserRepository {
UserRepository() {}
#Inject
public List<User> loadUserFromFile(Context contex) {
return parseResource(context, R.raw.users);
}
}
Happy Coding..!!
I don't see any harm in passing the context as an attribute. If you dislike the idea then you can retrieve the context via a convenient method : Static way to get 'Context' on Android?
I'm newbie to use Dagger2 on android. i create some class as Dagger module which they are using context, i can't merge,combine or using single context for other modules which they need that. and i get this error now:
android.content.Context is bound multiple times
SpModules:
#Module
public class SpModules {
private Context context;
public SpModules(Context context) {
this.context = context;
}
#Provides // this can be non-scoped because anyway the same instance is always returned
Context provideContext() {
return this.context;
}
#Provides
#Singleton
SP provideSharePreferences(Context context) {
return new SP(context); // use method-local Context
}
}
RealmModule:
#Module
public class RealmModule {
private Context context;
#Provides
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
#Provides
#Singleton
RealmConfiguration provideRealmConfiguration() {
final RealmConfiguration.Builder builder = new RealmConfiguration.Builder()
.schemaVersion(Migration.SCHEMA_VERSION)
.deleteRealmIfMigrationNeeded()
.migration(new Migration());
return builder.build();
}
#Provides
Realm provideDefaultRealm(RealmConfiguration config) {
return Realm.getInstance(config);
}
#Provides
Context provideContext() {
return this.context;
}
}
Component:
#Component(modules = {RealmModule.class, SpModules.class})
#Singleton
public interface ApplicationComponent {
void inject(ActivityRegister target);
void inject(ActivityMain target);
void inject(ActivityBase target);
void inject(FragmentAlachiqChannels target);
void inject(SocketServiceProvider target);
}
and then Application class to make Dagger2:
component = DaggerApplicationComponent.builder()
.appModules(new SpModules(this))
.build();
how can i resolve this problem?
That's simply dagger telling you that you're providing several times the same class to your component. In fact you do it here as well as across different modules for the same component:
#Provides
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
#Provides
Context provideContext() {
return this.context;
}
These methods are different, but they're providing the same dependency and without extra info dagger is not able to determine which Context to use.
You got a couple of options. If a single context would be enough to serve all your classes, then you could simply remove the extra provide methods.
However, let's say you need both application and some other context. You can use the annotation Named. Here's how it works, you annotate your provide methods with this annotation which basically will give a name to the dependency. Like so:
#Module
public class SpModules {
// ...
#Provides
#Named("context")
Context provideContext() {
return this.context;
}
//...
}
#Module
public class RealmModule {
//...
#Provides
#Named("application.context")
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
//...
}
Now you need to also annotated when you use these dependencies, for example say you have an object that depends on the application context:
public class Something {
public Something(#Named("application.context") Context context) {
//...
}
}
Or as a field:
public class Something {
#Named("application.context") Context context;
// ...
}
Or even in your module:
#Provides
#Singleton
SP provideSharePreferences(#Named("context") Context context) {
return new SP(context);
}
The name can be anything you want as long as they're consistent.
An alternative is to use Qualifiers. They work similar to the Named annotation, but are different. You'll be qualifying the dependency. So say you create 2 qualifiers:
#java.lang.annotation.Documented
#java.lang.annotation.Retention(RUNTIME)
#javax.inject.Qualifier
public #interface InstanceContext {
}
#java.lang.annotation.Documented
#java.lang.annotation.Retention(RUNTIME)
#javax.inject.Qualifier
public #interface ApplicationContext {
}
You can then use these to annotate the provide methods:
#Module
public class SpModules {
// ...
#Provides
#InstanceContext
Context provideContext() {
return this.context;
}
//...
}
#Module
public class RealmModule {
//...
#Provides
#ApplicationContext
Context provideApplicationContext() {
return AlachiqApplication.getInstance();
}
//...
}
You then use it the same way as with name. Here are the examples:
public class Something {
public Something(#ApplicationContext Context context) {
//...
}
}
public class Something {
#ApplicationContext Context context;
// ...
}
#Provides
#Singleton
SP provideSharePreferences(#InstanceContext Context context) {
return new SP(context);
}
Again, these names can be anything. I personally didn't put a lot of effort in thinking about them. Hope this helps.