I need help with Dagger2.13 for Android.
I am following several examples on the internet but I am now facing an error that I can not solve.
Error:(23, 14) error: @Subcomponent.Builder is missing setters for required modules or subcomponents: [com.hugothomaz.fipe.Module.DIMarcaModulo]
I thought it best to post the problem classes in GITHub and include the repository link here.
https://github.com/hugothomaz/FIPE_Test_Dagger2.11
-FipeApplication-
public class FipeApplication extends Application implements HasActivityInjector, HasFragmentInjector{
private static final String URL_SEARCH = "http://fipeapi.appspot.com/api/1/";
@Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjectorFragment;
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjectorActivity;
@Override
public void onCreate() {
super.onCreate();
initializeApplicationComponente();
}
@Override
public void onTerminate() {
super.onTerminate();
}
private void initializeApplicationComponente() {
Log.i("app", "FipeApplication initializeApplicationComponente");
//DaggerDIApplicationComponent.builder().(this).build();
}
@Override
public AndroidInjector<Fragment> fragmentInjector() {
return dispatchingAndroidInjectorFragment;
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjectorActivity;
}
}
-DIApplicationModulo-
@Module(subcomponents = {DIMarcaComponent.class})
public class DIApplicationModulo {
@Provides
@Singleton
GsonConverterFactory provideGsonConverterFactory(){
GsonConverterFactory factory = GsonConverterFactory.create();
return factory;
}
@Provides
@Singleton
OkHttpClient provideOkHttpClient(){
return new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
}
@Provides
@Singleton
RxJavaCallAdapterFactory provideRxJavaCallAdapterFactory(){
return RxJavaCallAdapterFactory.create();
}
@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient client,
GsonConverterFactory converterFactory,
RxJavaCallAdapterFactory adapterFactory, String mBaseURL){
return new Retrofit.Builder()
.baseUrl(mBaseURL)
.addConverterFactory(converterFactory)
.addCallAdapterFactory(adapterFactory)
.client(client)
.build();
}
}
-DIApplicationComponent-
@Singleton
@Component(modules = {
AndroidInjectionModule.class,
DIApplicationModulo.class,
ViewBuilderModule.class
})
public interface DIApplicationComponent extends AndroidInjector<FipeApplication>{
@Component.Builder
interface Builder{
@BindsInstance
DIApplicationComponent.Builder baseURL(String mBaseURL);
DIApplicationComponent build();
}
}
-ViewBuilderModule-
@Module(subcomponents = {DIMarcaComponent.class})
public abstract class ViewBuilderModule {
@Binds
@IntoMap
@FragmentKey(MarcaFragment.class)
abstract AndroidInjector.Factory<? extends Fragment> bindMarcaFragment(DIMarcaComponent.Builder bulder);
}
-DIMarcaModulo-
@Module
public class DIMarcaModulo {
private MarcaFragment mView;
private MarcaAdapterRecyclerImpl mAdapter;
public Context mContext;
public DIMarcaModulo(MarcaFragment view, MarcaAdapterRecyclerImpl adapter){
this.mView = view;
this.mAdapter = adapter;
this.mContext = view.getActivity().getBaseContext();
Log.i("app", "DIMarcaModulo instanciado");
if(adapter==null){
Log.i("app", "DIMarcaModulo - adapter : nao instanciado");
}else{
Log.i("app", "DIMarcaModulo - adapter : instancied");
}
}
@Provides
@PerFragment
IMarcaAPI provideMarcaApi(Retrofit retrofit){
Log.i("app", "DIMarcaModulo provideMarcaApi");
return retrofit.create(IMarcaAPI.class);
}
@Provides
@PerFragment
BaseView provideMarcaFragment(){
Log.i("app", "DIMarcaModulo provideMarcaFragment");
return mView;
}
@Provides
@PerFragment
IMarcaAdapterModel provideMarcaAdapterModel(){
Log.i("app", "DIMarcaModulo provideMarcaAdapterModel");
return mAdapter;
}
@Provides
@PerFragment
IMarcaPresenter provideMarcaPresenter(){
Log.i("app", "DIMarcaModulo provideMarcaPresenter");
return new MarcaPresenterImpl();
}
@Provides
@PerFragment
ControllerServiceAPIRest provideControllerServiceAPIRest(){
Log.i("app", "DIMarcaModulo ControllerServiceAPIRest");
return new ControllerServiceAPIRest();
}
@Provides
@PerFragment
Context exposeContext() {
return mContext;
}
}
-DIMarcaComponent-
@PerFragment
@Subcomponent(modules = {DIMarcaModulo.class})
public interface DIMarcaComponent extends AndroidInjector<MarcaFragment> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaFragment>{
}
}
-MarcaFragment-
public class MarcaFragment extends BaseFragment implements IMarcaView, HasFragmentInjector{
private MarcaAdapterRecyclerImpl mMarcaAdapter;
private LinearLayoutManager llm;
private View view = null;
@Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
@Inject
public IMarcaPresenter mMarcaPresenter;
@BindView(R.id.rv_marca)
protected RecyclerView mRecyclerMarca;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if(view==null){
view = inflater.inflate(R.layout.fragment_marca, container, false);
}
setUnBinder(ButterKnife.bind(this, view));
return view;
}
@Override
protected void onViewReady(Bundle saveInstanceState, Intent intent) {
initializeRecyclerMarca();
super.onViewReady(saveInstanceState, intent);
if(mMarcaPresenter != null){
Log.i("app", "MarcaFragment - Presenter nao esta vazio");
}else{
Log.i("app", "MarcaFragment - Presenter esta vazio");
}
mMarcaPresenter.initSerice();
}
private void initializeRecyclerMarca() {
if(mMarcaAdapter==null){
mMarcaAdapter = new MarcaAdapterRecyclerImpl();
}
mRecyclerMarca.setHasFixedSize(true);
llm = new LinearLayoutManager(getActivity().getBaseContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerMarca.setLayoutManager(llm);
mRecyclerMarca.setAdapter(mMarcaAdapter);
}
@Override
public void onOpenVehicleFragmentByMarcaClicked(Marca marca) {
// recebendo o item clicado para chamar proxima tela com a Marca clicada.
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
AndroidInjection.inject(this);
}
@Override
public AndroidInjector<Fragment> fragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
}
-MarcaPresenterImpl-
public class MarcaPresenterImpl extends BasePresenter<IMarcaView> implements IMarcaPresenter{
private static final String TAG_MARCA_PRESENTER = "MarcaPresenterImpl";
@Inject
public IMarcaAdapterModel mMarcaAdapterModel;
@Inject
public ControllerServiceAPIRest mControllerServiceAPIRest;
@Inject
public MarcaPresenterImpl(){
}
@Override
public void onShowMessage(String message) {
getView().onShowMessage(message);
}
@Override
public void onHideMessage() {
getView().onHideMessage();
}
@Override
public void onShowProgress(String message) {
getView().onShowProgress(message);
}
@Override
public void onHideProgress() {
getView().onHideProgress();
}
@Override
public void onShowToast(String message) {
getView().onShowToast(message);
}
public void refresh() {
mMarcaAdapterModel.refresh();
}
@Override
public void refreshItem(int id) {
}
@Override
public void listMarcaByServiceForView(List<Marca> listMarca) {
mMarcaAdapterModel.setListMarca(listMarca);
}
@Override
public void initSerice() {
mControllerServiceAPIRest.getMarca();
}
@Override
public void getMarcaClicked(@NonNull Marca marca) {
getView().onOpenVehicleFragmentByMarcaClicked(marca);
}
}
-ControllerServiceAPIRest-
public class ControllerServiceAPIRest implements Observer<List<Marca>> {
@Inject
public IMarcaPresenter mPresenter;
@Inject
public IMarcaAPI mMarcaAPI;
private List<Marca> listMarca;
@Inject
public ControllerServiceAPIRest() {
}
protected void getMarca(){
if(listMarca == null){
listMarca = new ArrayList<>();
}
mPresenter.onShowProgress("Carregando Marca de Veículos...");
Observable<List<Marca>> cakesResponseObservable = mMarcaAPI.getAllMarca();
subscribe(cakesResponseObservable, this);
}
private void subscribe(Observable<List<Marca>> observable, Observer<List<Marca>> observer){
observable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
@Override
public void onError(Throwable e) {
mPresenter.onHideMessage();
mPresenter.onHideProgress();
mPresenter.onShowToast("Erro ao carregar Marcas!");
Log.e("app", "Falha no carregamento de Marcas: " + e.getMessage());
new Exception(e.getMessage());
}
@Override
public void onComplete() {
mPresenter.onHideMessage();
mPresenter.onHideProgress();
Log.i("app", "ControllerServiceAPIRest - listMarca Position 0: " + listMarca.get(0));
if (listMarca==null && !listMarca.isEmpty() && listMarca.get(0)!=null){
mPresenter.listMarcaByServiceForView(listMarca);
mPresenter.onShowToast("Marcas carregadas!");
}else{
mPresenter.onShowToast("Lista não foi carregada!");
}
}
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(List<Marca> list) {
listMarca = list;
}
}
You require your Module:
@PerFragment
@Subcomponent(modules = {DIMarcaModulo.class})
public interface DIMarcaComponent extends AndroidInjector<MarcaFragment> {
And Dagger can't create it, because it doesn't have a public parameterless constructor:
@Module
public class DIMarcaModulo {
// ...
public DIMarcaModulo(MarcaFragment view, MarcaAdapterRecyclerImpl adapter){
But you bind in your Builder directly:
@Module(subcomponents = {DIMarcaComponent.class})
public abstract class ViewBuilderModule {
@Binds
@IntoMap
@FragmentKey(MarcaFragment.class)
abstract AndroidInjector.Factory<? extends Fragment>
bindMarcaFragment(DIMarcaComponent.Builder bulder);
}
So when dagger.android tries to create your object:
// AndroidInjector.Builder
abstract class Builder<T> implements AndroidInjector.Factory<T> {
@Override
public final AndroidInjector<T> create(T instance) {
seedInstance(instance);
return build();
}
It notices that you haven't provided a DIMarcaModulo instance and fails. You'll need to follow the advice in the SO question Dagger 2.10 Android subcomponents and builders, which means either giving DIMarcaModulo a public parameterless constructor that can inject MarcaFragment, or by overriding DIMarcaComponent.Builder#seedInstance:
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaFragment> {
// This method tells Dagger you need to supply your own DIMarcoModulo.
public abstract void diMarcoModulo(DIMarcaModulo modulo);
// dagger.android calls this method automatically, but only this method, so
// you'll need to call diMarcoModulo from it.
@Override public void seedInstance(MarcaFragment fragment) {
diMarcoModulo(fragment, fragment.getMMarcaAdapter());
bindMarcaFragment(fragment); // OPTIONAL: See below
}
// If you want MarcaFragment to remain injectable, you might need to call into
// a different @BindsInstance method you define, because you've prevented
// seedInstance from doing that for you.
@BindsInstance public abstract void bindMarcaFragment(MarcaFragment fragment);
}
As you can see, MarcaFragment will automatically be available in the graph that DIMarcoModulo installs, so if you can avoid the constructor parameters and instead receive the fragment as a parameter to @Provides methods, your code might be easier to read. You also will have trouble with the method I called fragment.getMMarcaAdapter()
, because you inject onAttach
and you get access to the RecyclerView onCreateView
. However, this shouldn't be a big problem if you remove the constructor parameters and if you can make sure you don't try to access the RecyclerView until after Android has a chance to create it.