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
})
}
}
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
Following android's architecture guide, I am experimenting with SavedStateHandle in my view model. So when I post any changes from a thread to the view model, it doesn't reflect on UI. However, without SavedStateHandle the UI changes reflect just fine.
public class UserViewModel extends ViewModel {
UserRepository userRepository;
private static final String USER_KEY = "user_key";
private SavedStateHandle mState;
public UserViewModel(SavedStateHandle savedStateHandle) {
mState = savedStateHandle;
this.userRepository = new UserRepository();
}
public LiveData<String> getUserId() {
return mState.getLiveData(USER_KEY, userRepository.getUserId());
}
public void setUserId(String value) {
mState.set(USER_KEY, value);
}
}
public class UserFragment extends Fragment {
private UserViewModel mViewModel;
public static UserFragment newInstance() {
return new UserFragment();
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.user_fragment, container, false);
}
#Override
public void onViewCreated(#NonNull #NotNull View view, #Nullable #org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewModel = new ViewModelProvider(requireActivity()).get(UserViewModel.class);
mViewModel.getUserId().observe(getViewLifecycleOwner(), s -> {
((TextView) view.findViewById(R.id.text_view_1)).setText(s);
});
Executors.newCachedThreadPool().submit(() -> {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
mViewModel.setUserId("111111");
});
}
public UserViewModel getmViewModel() {
return mViewModel;
}
}
Main Activity
my view only has one framelayout with id sample_fragment
public class MainActivity extends AppCompatActivity {
private final ExecutorService executorService = Executors.newCachedThreadPool();
private UserViewModel userViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
if (savedInstanceState == null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
UserFragment userFragment = new UserFragment();
transaction.replace(R.id.sample_fragment, userFragment);
transaction.commit();
executorService.submit(() -> {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
userViewModel.setUserId("1");
});
}
}
}
with this code, the changes don't reflect on UI. however when I replace the SaveStateHandle with MutableLiveData then a simple postValue changes the screen just fine. From what I have read, it seems the live data is changed only when the lifecycle is in some specific states (like CREATED).
You set a default value in your getUserId, but I don't see where you use postValue on the data in SavedStateHandle.
I would add a mUserId member and move the code in getUserId to the ViewModel constructor to make sure it's initialized before any write to it. Then make set and post methods:
LiveData<String> mUserId;
public UserViewModel(SavedStateHandle savedStateHandle) {
mState = savedStateHandle;
this.userRepository = new UserRepository();
mUserId = mState.getLiveData(USER_KEY, userRepository.getUserId());
}
public LiveData<String> getUserId() {
return mUserId;
}
public void setUserId(String value) {
mState.getLiveData(USER_KEY).setValue(value);
}
public void postUserId(String value) {
mState.getLiveData(USER_KEY).postValue(value);
}
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;
}
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
I have already looked up many answers here about this issue but for the life of me I can't seem to fix this problem on my end and I need some help.
BasePresenter:
public abstract class BasePresenter<V> {
private V mView;
public void attachView(V view) { mView = view; }
public void detachView() { mView = null; }
}
BaseFragment:
public abstract class BaseFragment<P extends BasePresenter> extends Fragment {
#Inject protected P mPresenter;
#Override
public void onResume() {
super.onResume();
mPresenter.attachView(this); // unchecked call to 'attachView(V)' as a member of raw type 'BasePresenter'
}
#Override
public void onPause() {
super.onPause();
mPresenter.detachView();
}
}
MyPresenter:
public class MyPresenter extends BasePresenter<MyPresenter.MyView> {
#Inject
public MyPresenter() {}
public interface MyView {}
}
MyFragment:
public class MyFragment extends BaseFragment implements MyPresenter.MyView {}
The problem is in the type-variable declaration:
class BaseFragment<P extends BasePresenter>
BasePresenter is a generic class, so you need to specify what its type parameter is. From this snippet:
mPresenter.attachView(this);
It would seem that you expect BaseFragment to be bound to the type-variable V in BasePresenter - so I would rewrite your BaseFragment declaration as follows:
abstract class BaseFragment<P extends BasePresenter<BaseFragment<P>>> { ... }
That should take care of the unchecked warning. That said, I strongly suspect that what you are really after is something like this:
abstract class BaseFragment<P extends BasePresenter<V>, V> { ... }
Where V is an independent type-variable modelling the 'view'.
Simple, instead of
public abstract class BaseFragment<P extends BasePresenter> extends Fragment {
It should be
public abstract class BaseFragment<V, P extends BasePresenter<V>> extends Fragment {
or
public abstract class BaseFragment<P extends BasePresenter<BaseFragment<P>>> extends Fragment {
or
public abstract class BaseFragment<V extends BaseFragment<V, P>, P extends BasePresenter<V>> extends Fragment {
Basically, make sure the BasePresenter is parametrized with something.
EDIT:
Okay, based on what you're actually trying to do, you should do it like this:
public abstract class BasePresenter<V> {
private V mView;
public void attachView(V view) { mView = view; }
public void detachView() { mView = null; }
}
public abstract class BaseFragment<V extends BaseFragment<V, P>, P extends BasePresenter<V>> extends Fragment {
protected abstract P getPresenter();
#Override
public void onResume() {
super.onResume();
getPresenter().attachView(this); // unchecked call to 'attachView(V)' as a member of raw type 'BasePresenter'
}
#Override
public void onPause() {
super.onPause();
getPresenter().detachView();
}
}
public class MyPresenter extends BasePresenter<MyPresenter.MyView> {
#Inject
public MyPresenter() {}
public interface MyView {}
}
public class MyFragment extends BaseFragment<MyFragment, MyPresenter> implements MyPresenter.MyView {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
MyFragmentComponent component = ((MainActivity)getActivity()).getComponent().myFragmentComponent();
component.inject(this);
View view = inflater.inflate(R.layout.blah, container, false);
ButterKnife.bind(this, view);
return view;
}
}
EDIT2: Based on provided example:
public class RexTester {
// MAIN CLASS
static class Rextester {
public static void main(String args[]) {
new MyFragment();
}
}
// MVP CODE
interface BaseView {}
final static class MyPresenter extends BasePresenter<MyPresenter.MyView> {
public MyPresenter() {}
public void executeAction() {
mView.onCallback();
}
interface MyView extends BaseView {
void onCallback();
}
}
abstract static class BasePresenter<V extends BaseView> {
protected V mView;
public void attachView(V view) { mView = view;}
public void detachView() { mView = null; }
}
final static class MyFragment extends BaseFragment<MyPresenter.MyView, MyPresenter> implements MyPresenter.MyView {
private MyPresenter mPresenter;
public MyFragment() {
mPresenter = new MyPresenter();
onResume(); // Mock onResume() lifecycle event!
mPresenter.executeAction();
onPause(); // Mock onPause() lifecycle event!
}
protected MyPresenter getPresenter() {
return mPresenter;
}
#Override
protected MyPresenter.MyView getThis() {
return this;
}
public void onCallback() {
System.out.println("Hello AndroidMVP!");
}
}
abstract static class BaseFragment<V extends BaseView, P extends BasePresenter<V>> extends Fragment implements BaseView {
protected abstract P getPresenter();
protected void onResume() {
super.onResume();
getPresenter().attachView(getThis());
}
protected abstract V getThis();
protected void onPause() {
super.onPause();
getPresenter().detachView();
}
}
// ANDROID FRAMEWORK MOCK
abstract static class Fragment {
protected void onResume() {}
protected void onPause() {}
}
}