In a project that is an Activity Model, Presenter and Model, the Activity Theme and Presenter and Presenter Model. When I do #Inject in Presenter to instantiate the Model it is never instantiated.
Do you need a dependency "cascade"?
FATAL EXCEPTION: main Process: fipedaggerrxjava, PID: 22258
java.lang.NullPointerException: Attempt to invoke interface method
'void
fipedaggerrxjava.mvp.SelectMarcaContractMVP$Model.getMarcas(java.lang.String)'
on a null object reference at
fipedaggerrxjava.module.marca.MarcaPresenter.initData(MarcaPresenter.java:35)
at
fipedaggerrxjava.module.marca.MarcaActivity$1.onCheckedChanged(MarcaActivity.java:63)
I already checked in Debug and really the Model that is not being instantiated by Dagger but I can not understand why.
App
public class App extends Application implements HasActivityInjector{
#Inject
public DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().build().inject(App.this);
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
ActivityBuilder
#Module
public abstract class ActivityBuilder {
#Binds
#IntoMap
#ActivityKey(MarcaActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMarcaActivity (MarcaComponent.Builder builder);
}
AppComponent
#Component(modules = {ActivityBuilder.class, AndroidInjectionModule.class, AppModule.class})
#Singleton
public interface AppComponent {
void inject(App app);
}
AppModule
#Module(subcomponents = MarcaComponent.class)
public class AppModule {
#Provides
#Singleton
#Named("URL_MARCA")
String provideStringURLBase(){
return "https://fipe.parallelum.com.br/api/v1/";
}
#Provides
#Singleton
Context provideContext(App app){
return app;
}
#Provides
#Singleton
Gson provideGsonRepositorie(){
return new GsonBuilder()
.create();
}
#Singleton
#Provides
OkHttpClient provideOkHttpCliente1(){
return new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
}
#Singleton
#Provides
RxJavaCallAdapterFactory provideRxJavaCallAdapterFactory(){
return RxJavaCallAdapterFactory.create();
}
#Provides
#Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient, Gson gson, RxJavaCallAdapterFactory rxAdapter, #Named("URL_MARCA") String stringBaseURL){
return new Retrofit.Builder()
.baseUrl(stringBaseURL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(rxAdapter)
.client(okHttpClient)
.build();
}
}
MarcaComponent
#Subcomponent(modules = MarcaModule.class)
#PerMarca
public interface MarcaComponent extends AndroidInjector<MarcaActivity>{
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MarcaActivity> {}
}
MarcaModule
#Module
public class MarcaModule{
#Provides
#PerMarca
APIFIPE provideAPIFIPE(Retrofit retrofit){
return retrofit.create(APIFIPE.class);
}
#Provides
#PerMarca
View provideViewMarca(MarcaActivity activity){
return activity;
}
#Provides
#PerMarca
Presenter providePresenterMarca(){
return new MarcaPresenter();
}
#Provides
#PerMarca
Model provideModelMarca(){
return new MarcaModel();
}
}
AdapterMarca
public class AdapterMarca extends BaseAdapter {
private List<Marca> mListMarca;
#Inject
public Context mContext;
public AdapterMarca(List<Marca> listMarca){
this.mListMarca = listMarca;
}
#Override
public int getCount() {
return mListMarca.size();
}
#Override
public Object getItem(int position) {
return mListMarca.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_list_item, parent, false);
TextView tvNome = view.findViewById(R.id.tv_marca);
tvNome.setText(mListMarca.get(position).getName().toString());
return view;
}
public void addListMarca(List<Marca> marcaList){
mListMarca.clear();
mListMarca.addAll(marcaList);
notifyDataSetChanged();
}
}
MarcaActivity
public class MarcaActivity extends BaseActivity implements HasActivityInjector, View {
private RadioGroup radioGroupMarca;
private String tipoSelect = "";
private List<Marca> mListMarca;
private AdapterMarca mAdapterMarca;
private ListView listViewMarca;
#Inject
public Presenter mMarcaPresenter;
#Inject
protected DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
#Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(MarcaActivity.this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listViewMarca = findViewById(R.id.lv_marca);
radioGroupMarca = findViewById(R.id.rg_tipo);
radioGroupMarca.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int id = group.getCheckedRadioButtonId();
switch (id){
case R.id.rb_carros : tipoSelect = "carros";
mMarcaPresenter.initData(tipoSelect);
break;
case R.id.rb_motos : tipoSelect = "motos";
mMarcaPresenter.initData(tipoSelect);
break;
case R.id.rb_caminhoes : tipoSelect = "caminhoes";
mMarcaPresenter.initData(tipoSelect);
break;
}
}
});
}
#Override
public AndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
#Override
public void onMarcaLoader(List<Marca> listMarcas) {
if(mListMarca==null && listMarcas!=null){
initListView();
}
if(mAdapterMarca!=null){
mListMarca.clear();
mListMarca = listMarcas;
mAdapterMarca.addListMarca(mListMarca);
}
}
private void initListView(){
mAdapterMarca = new AdapterMarca(mListMarca);
listViewMarca.setAdapter(mAdapterMarca);
}
}
MarcaPresenter
#PerMarca
public class MarcaPresenter implements Presenter {
#Inject
View mMarcaView;
#Inject
Model mMarcaModel;
#Inject
public MarcaPresenter(){
}
#Override
public void initData(String tipoMarca) {
mMarcaModel.getMarcas(tipoMarca);
}
#Override
public void getMarcas(List<Marca> listMarcas) {
mMarcaView.onMarcaLoader(listMarcas);
}
#Override
public void onShowDialog(String title, String msg) {
mMarcaView.onShowDialog(title, msg);
}
#Override
public void onHideShowDialog() {
mMarcaView.onHideShowDialog();
}
#Override
public void onShowToast(String s) {
mMarcaView.onShowToast(s);
}
}
MarcaModel
#PerMarca
public class MarcaModel implements Model {
#Inject
APIFIPE mApiFIPE;
#Inject
Presenter mMarcaPresenter;
#Inject
public MarcaModel(){
}
#Override
public void getMarcas(String tipoVeiculo) {
final List<Marca> marcaList = new ArrayList<>();
Observable<List<Marca>> observable = mApiFIPE.getRepositories(tipoVeiculo);
observable.subscribe(new Observer<List<Marca>>() {
#Override
public void onCompleted() {
mMarcaPresenter.getMarcas(marcaList);
}
#Override
public void onError(Throwable e) {
mMarcaPresenter.onShowDialog("Erro", "Falha ao carregar lista de marcas");
}
#Override
public void onNext(List<Marca> marcas) {
marcaList.addAll(marcas);
}
});
}
}
In below lines:
public void onCompleted() {
mMarcaPresenter.getMarcas(marcaList);
}
-You trying to use mMarcePResenter and you inject it but you still need to use the App's component get assigned to mMarcaPresenter.
Initialize the injected value in below constructor:
#Inject
public MarcaModel(){
mMarcaPresenter =
}
You have declared provider methods(in MarcaModule) for your model and presenter classes. Dagger uses those provider methods, ending up not supplying the field injected values.
Therefore, you need to supply your dependencies in your module(s), i.e. via Model and Presenter constructors.
MarcaModel
...
Presenter mMarcaPresenter;
#Inject
public MarcaModel(Presenter presenter){
mMarcaPresenter = presenter
}
...
MarcaModule
#Module
public class MarcaModule{
...
#Provides
#PerMarca
Model provideModelMarca(Presenter presenter){
return new MarcaModel(presenter);
}
}
Reading Dagger user's guide carefully might help you even more;
https://google.github.io/dagger/users-guide
Related
I'm new to Programming and to Android and got please a question, so I have to build an Android app ( App to buy cars) using MVVM,LiveData, RecyclerView and Fragments.
I can creat an instance of car and then view that in a RecyclerView, but when I want to delete it, then I receive the following error:
Logcat:
java.lang.NullPointerException: **Attempt to invoke virtual method 'void com.example.da.ViewModel.MyViewModel.deleteCar(com.example.da.Model.Car)' on a null object reference**
at com.example.da.Adapter.RecyclerAdapter$MyViewHolder$2.onClick(RecyclerAdapter.java:127)
at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:174)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
I hope that someone can help me.Thanks a lot!
Dao
<pre><code>
#Dao
public interface CarDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void addCar( Car car);
#Query("SELECT * FROM Car ORDER BY model ASC")
LiveData<List<Car>>getAllCars();
#Delete
public void deleteCar(Car car);
#Query("DELETE FROM Car")
void deleteAll();
#Update
void updateCar(Car car);
//User
#Insert
void registerUser(User user);
}
</pre></code>
Database
<pre><code>
#Database(entities = {Car.class, User.class},version =1, exportSchema = false)
public abstract class RoomDatabase extends androidx.room.RoomDatabase {
private static RoomDatabase roomDatabase;
private static final String databaseName="user";
// Dao including
public abstract CarDao carDao();
public abstract UserDao userDao();
public static synchronized RoomDatabase getUserDatabase(Context context)
{
if (roomDatabase==null)
{
roomDatabase= Room.databaseBuilder(context, RoomDatabase.class,databaseName)
.fallbackToDestructiveMigration()
.build();
}
return roomDatabase;
</pre></code>
Repository
<pre><code>
public class Repository {
private CarDao carDao;
private LiveData<List<Car>>getAllCars;
public Repository(Application application)
{
RoomDatabase db = RoomDatabase.getUserDatabase(application);
carDao=db.carDao();
getAllCars=carDao.getAllCars();
}
//CarDao methods
public void addCar( Car car)
{
new newAsyncTask(carDao).execute(car);
}
private static class newAsyncTask extends AsyncTask<Car, Void, Void>{
private CarDao myAsyncDao;
newAsyncTask(CarDao carDao){
myAsyncDao=carDao;
}
#Override
protected Void doInBackground(Car... cars) {
myAsyncDao.addCar(cars[0]);
return null;
}
}
public void updateCar( Car car)
{
new updateAsyncTask(carDao).execute(car);
}
private static class updateAsyncTask extends AsyncTask<Car, Void, Void>{
private CarDao myAsyncDao;
updateAsyncTask(CarDao carDao){
myAsyncDao=carDao;
}
#Override
protected Void doInBackground(Car... cars) {
myAsyncDao.updateCar(cars[0]);
return null;
}
}
public void deleteCar( Car car)
{
new deleteAsyncTask(carDao).execute(car);
}
private static class deleteAsyncTask extends AsyncTask<Car, Void, Void>{
private CarDao myAsyncDao;
deleteAsyncTask(CarDao carDao){
myAsyncDao=carDao;
}
#Override
protected Void doInBackground(Car... cars) {
myAsyncDao.deleteCar(cars[0]);
return null;
}
}
</pre></code>
RecyclerAdapter
<pre><code>
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> {
private List<Car>list;
Context context;
private MyViewModel myViewModel;
public RecyclerAdapter(List<Car> list) {
this.list = list;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.car_item,parent,false);
MyViewHolder myViewHolder = new MyViewHolder(view);
context=parent.getContext();
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
Car car = list.get(position);
holder.tvModel.setText(car.getCarModel());
holder.tvColor.setText(car.getCarColor());
holder.tvDpl.setText(car.getCarDpl());
holder.tvDescription.setText(car.getCarDescription());
holder.tvPrice.setText(car.getCarPrice());
}
#Override
public int getItemCount() {
if(list !=null)
{
return list.size();
}
else return 0;
}
public void setData(List<Car>list){
this.list=list;
notifyDataSetChanged();
}
// generate ViewHolder
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView tvModel;
TextView tvColor;
TextView tvDpl;
TextView tvDescription;
TextView tvPrice;
Button btnDelete;
// Constructor of ViewHolder
public MyViewHolder(#NonNull View itemView) {
super(itemView);
// Inflate the views
tvModel=itemView.findViewById(R.id.model);
tvColor=itemView.findViewById(R.id.color);
tvDescription=itemView.findViewById(R.id.description);
tvDpl=itemView.findViewById(R.id.dpl);
tvPrice=itemView.findViewById(R.id.price);
btnDelete=itemView.findViewById(R.id.btn_delete);
// Listener for Delete button
btnDelete.setOnClickListener(this);
btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
CreateAlertDialoge();
}
});
}
private void CreateAlertDialoge()
{
AlertDialog.Builder builder =new AlertDialog.Builder(context);
builder.setMessage("Are you sure to delete");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Car car = new Car();
int ID=list.get(getAdapterPosition()).getCarId();
car.setCarId(ID);
// if (car==null){
// return;
// }
//MainActivity.roomDatabase.carDao().deleteCar(car);
myViewModel.deleteCar(car);
MainActivity.fragmentManager.beginTransaction().replace(R.id.container,new BaseFragment(),null).commit();
Toast.makeText(context, "Yes", Toast.LENGTH_SHORT).show();
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(context, "no", Toast.LENGTH_SHORT).show();
}
});
builder.create();
builder.show();
}
#Override
public void onClick(View v)
{
Car car = new Car();
int ID=list.get(getAdapterPosition()).getCarId();
car.setCarId(ID);
//MainActivity.roomDatabase.carDao().deleteCar(car);
myViewModel.deleteCar(car);
MainActivity.fragmentManager.beginTransaction().replace(R.id.container,new BaseFragment(),null).commit();
}
}
}
</pre></code>
ViewModel
<pre><code>
public class MyViewModel extends AndroidViewModel {
private Repository repository;
private LiveData<List<Car>>getAllCars;
public MyViewModel(#NonNull Application application) {
super(application);
repository=new Repository(application);
getAllCars=repository.getAllCars();
}
public LiveData<List<Car>>getGetAllCars(){
return getAllCars;
}
public void addCar( Car car)
{
repository.addCar(car);
}
public void updateCar(Car car){
repository.updateCar(car);
}
public void registerUser( User user)
{
repository.registerUser(user);
}
public void deleteCar(Car car)
{
repository.deleteCar(car);
}
public void deleteAll(){
repository.deleteAll();
}
}
</pre></code>
The main problem here is the myViewModel inside your RecyclerAdapter was never set a value. You may pass the instance of your ViewModel when instantiating the RecyclerAdapter. However, this is considered a bad idea.
A better approach would be creating a callback from RecyclerAdapter back to your Fragment. See: https://stackoverflow.com/a/53466188/9656117
I have been struggling with one bug for a week now. I am a beginner android developer and I am trying to build MVVM app. The idea is to be gaming news but that does not really matter. Here are my 3 classes that are key to this bug.
My NewsFragment class:
FragmentNewsBinding binding;
NewsFragmentAdapter adapter;
Context context;
public NewsFragmentViewModel newsFragmentViewModel;
public NewsFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
newsFragmentViewModel = new ViewModelProvider(this).get(NewsFragmentViewModel.class);
// Inflating the layout for this fragment
binding = FragmentNewsBinding.inflate(inflater, container, false);
newsFragmentViewModel.init();
setAdapter(container);
observeChanges();
return binding.getRoot();
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
private void setAdapter(ViewGroup container) {
context = container.getContext();
adapter = new NewsFragmentAdapter(newsFragmentViewModel.getItems().getValue(), context);
RecyclerView.LayoutManager linearLayoutManager = new LinearLayoutManager(context);
binding.recyclerViewNews.setLayoutManager(linearLayoutManager);
binding.recyclerViewNews.setAdapter(adapter);
}
public void observeChanges() {
newsFragmentViewModel.getItems().observe(getViewLifecycleOwner(), new Observer<List<Item>>() {
#Override
public void onChanged(List<Item> items) {
adapter.notifyDataSetChanged();
}
});
}
}
NewsFragmentViewModel class:
public class NewsFragmentViewModel extends ViewModel {
private MutableLiveData<List<Item>> mItems;
private NewsRepository mRepo;
// Empty constructor
public NewsFragmentViewModel() {
}
public void init() {
if (mItems != null) {
return;
}
mRepo = NewsRepository.getInstance();
mItems = mRepo.getItems();
}
public LiveData<List<Item>> getItems() {
return mItems;
}
}
And the repository class:
public class NewsRepository {
MutableLiveData<List<Item>> mItems;
private static NewsRepository instance;
public static NewsRepository getInstance() {
if (instance == null) {
instance = new NewsRepository();
}
return instance;
}
//Empty constructor
private NewsRepository() {
}
public MutableLiveData<List<Item>> getItems() {
mItems = new MutableLiveData<>();
ServiceApi.getInstance().getGamezoneMethods().getGamezoneNews().enqueue(new Callback<Rss>() {
#Override
public void onResponse(Call<Rss> call, Response<Rss> response) {
if (response.code() == 200) {
List<Item> items = response.body().getChannel().getItems();
mItems.postValue(items);
}
}
#Override
public void onFailure(Call<Rss> call, Throwable t) {
Log.d("Failed", "Error");
mItems.postValue(null);
}
});
return mItems;
}
}
Now, the main problem with this code is that in the repository when I do my request I successfully get the data and I think, I successfully push the data to the viewmodel. But when I try to get my data from the LiveData in the viewmodel class or in the fragment class I always get null, and because of the null problem I cannot set up an adapter properly and I cannot set up my onChanged() method. I am trying to get the data in the fragment with the classic getValue() method. Please somebody help me because I am losing my mind.
If you are confused with this code, here is link to my github repo.
I've seen two approaches to MVP pattern in Android. Both are used in Android Architecture Blueprints:
public interface Contract {
interface View {
void showData(String data);
}
interface StartVersionPresenter {
void start();
}
interface DropViewVersionPresenter {
void takeView(View view);
void dropView();
}
}
1) The presenter in which the view is injected via the constructor:
public class StartVersionPresenter implements Contract.StartVersionPresenter {
private final Contract.View view;
private final Repository repository;
public StartVersionPresenter(Contract.View view, Repository repository) {
this.view = view;
this.repository = repository;
}
#Override
public void start() {
loadData();
}
private void loadData() {
repository.getData(new DataCallback() {
#Override
public void onDataLoaded(String someData) {
view.showData(someData);
}
});
}
}
start() is called in onResume() Fragment's method.
2) The presenter in which the view is injected via the method (takeView in my example):
public class DropViewVersionPresenter implements Contract.DropViewVersionPresenter {
private final Repository repository;
private Contract.View view;
public DropViewVersionPresenter(Repository repository) {
this.repository = repository;
}
#Override
public void takeView(Contract.View view) {
this.view = view;
loadData();
}
#Override
public void dropView() {
view = null;
}
private void loadData() {
repository.getData(new DataCallback() {
#Override
public void onDataLoaded(String someData) {
if (view != null)
view.showData(someData);
}
});
}
}
takeView(Contract.View view) is called in Fragment's onResume() method.
dropView() is called in Fragment's onDestroy() method.
In both cases the presenter is created in Fragment's onCreate() method.
Why does the second approach is used in more cases rather then the first one? I can see that the first one is simpler because we don't have to check that the view is not null if we want to call the method on it.
I am following the clean architecture with mvvm. When I injcet viewmodel factory in fragment and try to execute use case from fragment viewmodel it is giving NPE on use case. I have injected the use case as well.
Please look into the code and point out the mistake
MainModule.java
#Module
public class MainModule {
#Provides
LoginViewModel provideLoginViewModel() {
return new LoginViewModel();
}
#Provides
#Named("LoginActivity")
ViewModelProvider.Factory provideActivityViewModelFactory(LoginViewModel loginViewModel) {
return new ViewModelProviderFactory<>(loginViewModel);
}
#Provides
PasscodeViewModel providePasscodeViewModel(SetPasscode setPasscode) {
return new PasscodeViewModel(setPasscode);
}
#Provides
#Named("PasscodeFragment")
ViewModelProvider.Factory provideFragmentViewModelFactory(PasscodeViewModel passcodeViewModel) {
return new ViewModelProviderFactory<>(passcodeViewModel);
}
}
PasscodeFragment.java
public class PasscodeFragment extends BaseFragment<PasscodeViewModel> implements PasscodeNavigator, View.OnClickListener {
#Inject
#Named("PasscodeFragment")
ViewModelProvider.Factory viewModelFactory;
PasscodeViewModel passcodeViewModel;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
passcodeViewModel.setNavigator(this);
}
public PasscodeFragment() {
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
((MainApplication) getActivity().getApplicationContext()).getComponent().inject(this);
View view = inflater.inflate(R.layout.fragment_passcode, container, false);
initViews(view);
return view;
}
#Override
public PasscodeViewModel getViewModel() {
passcodeViewModel = ViewModelProviders.of(this, viewModelFactory).get(PasscodeViewModel.class);
return passcodeViewModel;
}
PasscodeViewModel.java (HERE THE SETPASSCODE IS COMING NULL)
public class PasscodeViewModel extends BaseViewModel<PasscodeNavigator> {
//use case
private SetPasscode setPasscode;
//data
private String tempPin;
private boolean isConfirming = false;
public PasscodeViewModel() {
}
public PasscodeViewModel(SetPasscode setPasscode) {
this.setPasscode = setPasscode;
}
public void checkForConfirmation(String passcode) {
try {
if (passcode.length() == 4) {
if (isConfirming) {
//second attempt
if (tempPin.equals(passcode)) {
//done
Log.e("ganesh", passcode);
setPasscode(passcode);
} else {
//reset
isConfirming = false;
tempPin = "";
getNavigator().onPinNotMatched();
getNavigator().clearViewsForFirstAttempt();
}
} else {
//first attempt
tempPin = passcode;
isConfirming = true;
getNavigator().clearViewsForSecondAttempt();
}
}
} catch (NumberFormatException e) {
getNavigator().wrongPinFormat();
getNavigator().clearViewsForFirstAttempt();
}
}
private void setPasscode(String passcode) {
//setPasscode IS NULL HERE
setPasscode.execute(new DisposableSingleObserver<Boolean>() {
#Override
public void onSuccess(Boolean aBoolean) {
if (aBoolean)
getNavigator().onPinSetSuccess();
else
getNavigator().onPinSetFailed();
}
#Override
public void onError(Throwable e) {
getNavigator().onPinSetFailed();
}
}, SetPasscode.Params.setPasscode(passcode));
}
#Override
protected void onCleared() {
if (setPasscode != null)
setPasscode.dispose();
}
}
SetPasscode.java
public class SetPasscode extends SingleUseCase<Boolean, SetPasscode.Params> {
private UserRepository repository;
public SetPasscode(PostExecutionThread postExecutionThread, UserRepository repository) {
super(postExecutionThread);
this.repository = repository;
}
#Override
public Single<Boolean> buildUseCaseObservable(SetPasscode.Params params) {
return repository.setPasscode(params.passcode);
}
public static final class Params {
private String passcode;
private Params(String passcode) {
this.passcode = passcode;
}
public static SetPasscode.Params setPasscode(String passcode) {
return new SetPasscode.Params(passcode);
}
}
}
LoginActivity.java
public class LoginActivity extends BaseActivity<LoginViewModel> implements LoginNavigator {
#Inject
#Named("LoginActivity")
ViewModelProvider.Factory viewModelFactory;
LoginViewModel loginViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
getSupportActionBar().hide();
((MainApplication) getApplicationContext()).getComponent().inject(this);
loginViewModel = ViewModelProviders.of(this, viewModelFactory).get(LoginViewModel.class);
loginViewModel.setNavigator(this);
initViews();
}
#Override
public LoginViewModel getViewModel() {
return loginViewModel;
}
I just learned dagger 2, then i want implement with android jetpack. but i have problem cannot inject variabel in ViewModel.
This my structure
dagger
-- AppComponent.java
-- AppInjector.java
-- AppModule.java
-- Injectable.java
factory
-- MainViewModelFactory.java
module
-- FragmentBuildersModule.java
-- MainActivityModule.java
-- ViewModelKey.java
-- ViewModelModule.java
-- ViewModelSubcomponent.java
ui
-- MainFragment.java
-- MainViewModel.java
App.java
MainActivity.java
My Code MainViewModelFactory.java
#Singleton
public class MainViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
#Inject
public MainViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
#SuppressWarnings("unchecked")
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown viewmodel class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
FragmentBuildersModule.java
#Module
public abstract class FragmentBuildersModule {
#ContributesAndroidInjector
abstract MainFragment contributeMainFragment();
}
MainActivityModule.java
#Module
public abstract class MainActivityModule {
#ContributesAndroidInjector(modules = FragmentBuildersModule.class)
abstract MainActivity cotributeMainActivity();
}
ViewModelKey.java
#Documented
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#MapKey
#interface ViewModelKey {
Class<? extends ViewModel> value();
}
ViewModelModule.java
#Module
public abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MainViewModel.class)
abstract MainViewModel bindMainViewModel(MainViewModel mainViewModel);
#Binds
abstract ViewModelProvider.Factory bindViewModelFactory(MainViewModelFactory factory);
}
ViewModelSubComponent.java
#Subcomponent
public interface ViewModelSubcomponent {
#Subcomponent.Builder
interface Builder {
ViewModelSubcomponent build();
}
MainViewModel mainViewModel();
}
ui.MainFragment.java
public class MainFragment extends Fragment {
private static final String TAG = "MainFragment";
#Inject
ViewModelProvider.Factory viewModelFactory;
private MainViewModel mViewModel;
public static MainFragment newInstance() {
return new MainFragment();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.main_fragment, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// mViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel.class);
// TODO: Use the ViewModel
TextView message = (TextView) getView().findViewById(R.id.message);
message.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// String test = mViewModel.getGlobalCommon().getNama();
GlobalCommon globalCommon = mViewModel.getGlobalCommon();
Toast.makeText(getContext(), "tst", Toast.LENGTH_LONG).show();
}
});
}
}
di.AppComponent.java
#Singleton
#Component(modules = { AndroidInjectionModule.class, AppModule.class, MainActivityModule.class})
public interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
Builder application(Application application);
AppComponent build();
}
void inject(App app);
}
di.AppInjector.java
public class AppInjector {
private AppInjector() {}
public static void init(App app) {
DaggerAppComponent.builder().application(app).build().inject(app);
app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
handleActivity(activity);
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
private static void handleActivity(Activity activity) {
if(activity instanceof HasSupportFragmentInjector){
AndroidInjection.inject(activity);
}
if(activity instanceof FragmentActivity){
FragmentManager.FragmentLifecycleCallbacks fragmentCallback = new FragmentManager.FragmentLifecycleCallbacks() {
#Override
public void onFragmentCreated(FragmentManager fm, Fragment f,
Bundle savedInstanceState) {
if (f instanceof Injectable) {
AndroidSupportInjection.inject(f);
}
}
};
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(fragmentCallback, true);
}
}
}
di.AppModule.java
#Module(includes = ViewModelModule.class )
public class AppModule {
#Provides
#Singleton
Context provideApplication(Context application) {
return application.getApplicationContext();
}
#Singleton
#Provides
GlobalCommon provideGlobalCommon(){
return new GlobalCommon();
}
}
App.java
public class App extends Application implements HasActivityInjector {
#Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
#Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
AppInjector.init(this);
}
#Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
#Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow();
}
}
#Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidInjector;
}
}
myerror:
error: [Dagger/MissingBinding] [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an #Provides-annotated method.
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
main.main.xxx.main.main.testdagger2.factory.MainViewModelFactory.<init>(creators)
main.main.xxx.main.main.testdagger2.factory.MainViewModelFactory is injected at
main.main.xxx.main.main.testdagger2.module.ViewModelModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
main.main.xxx.main.main.testdagger2.ui.main.MainFragment.viewModelFactory
main.main.xxx.main.main.testdagger2.ui.main.MainFragment is injected at
dagger.android.AndroidInjector.inject(T)
component path: main.main.xxx.main.main.testdagger2.di.AppComponent → main.main.xxx.main.main.testdagger2.module.MainActivityModule_CotributeMainActivity.MainActivitySubcomponent → main.main.xxx.main.main.testdagger2.module.FragmentBuildersModule_ContributeMainFragment.MainFragmentSubcomponent
please helpme.
i have created a Demo application to Demonstrate the Use of LiveData, RxJava2, Dagger2, Paging, and much more. Since it is too much of the code to post here, I will post the link here. If you think you can make it easier or better open an issue.
https://github.com/mtangoo/Kasuku-Muvi
I wrote a library that should make this more straightforward: https://github.com/radutopor/ViewModelFactory
#ViewModelFactory
class SampleViewModel(#Provided private val repository: Repository, private val userId: Int) : ViewModel() {
private val greeting = MutableLiveData<String>()
init {
val user = repository.getUser(userId)
greeting.value = "Hello, $user.name"
}
fun getGreeting() = greeting as LiveData<String>
}
In the view:
class SampleActivity : AppCompatActivity() {
#Inject
lateinit var sampleViewModelFactory2: SampleViewModelFactory2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sample)
appComponent.inject(this)
val userId = intent.getIntExtra("USER_ID", -1)
val viewModel = ViewModelProviders.of(this, sampleViewModelFactory2.create(userId))
.get(SampleViewModel::class.java)
viewModel.getGreeting().observe(this, Observer { greeting ->
greetingTextView.text = greeting
})
}
}