old ViewModel reused instead of building a new one - android

Some times when the activity is destroyed (not sure why, memory pressure I presume), a new activity is created, but the old view model bound to the dead activity is reused.
Activity:
[Activity(
LaunchMode = LaunchMode.SingleTask,
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class HomeView : MvxTabsFragmentActivity
{
protected override void OnCreate(Bundle bundle)
{
Log.Info("On HomeView created");
base.OnCreate(bundle);
}
protected override void OnDestroy()
{
base.OnDestroy();
Log.Info("On HomeView destroyed");
this.HomeViewModel.CleanUp();
}
}
ViewModel:
public class HomeViewModel : MvxViewModel
{
public HomeViewModel(
IMvxMessenger messenger,
IUserInteraction userInteraction,
DashboardViewModel dashboardViewModel,
AlertSettingsViewModel alertSettingsViewModel)
{
Log.Info("Building home view model");
}
public void CleanUp()
{
Log.Info("HomeViewModel => Clean-up");
}
}
App.cs:
public override void Initialize()
{
this.CreatableTypes().EndingWith("ViewModel").AsTypes().RegisterAsDynamic();
this.RegisterAppStart<HomeViewModel>();
TaskScheduler.UnobservedTaskException +=
(sender, eventArgs) =>
Log.Error("An Unobserved exception has been raised by a task", eventArgs.Exception);
}
Debug output:
On HomeView created
Building home view model
...
On HomeView destroyed
HomeViewModel => Clean-up
...
On HomeView created
[here: no "Building view model" message]
Maybe it is the SingleTask Activity ?
Is there a way (with IoC, or other) to get a fresh view model at every HomeView creation ?
EDIT:
I ran over this method on MvxActivityViewExtensions.cs
public static void OnViewCreate(this IMvxAndroidView androidView, Bundle bundle)
{
MvxActivityViewExtensions.EnsureSetupInitialized(androidView);
MvxActivityViewExtensions.OnLifetimeEvent(androidView, (Action<IMvxAndroidActivityLifetimeListener, Activity>) ((listener, activity) => listener.OnCreate(activity)));
IMvxViewModel cached = Mvx.Resolve<IMvxSingleViewModelCache>().GetAndClear(bundle);
IMvxView view = (IMvxView) androidView;
IMvxBundle savedState = MvxActivityViewExtensions.GetSavedStateFromBundle(bundle);
MvxViewExtensionMethods.OnViewCreate(view, (Func<IMvxViewModel>) (() => cached ?? MvxActivityViewExtensions.LoadViewModel(androidView, savedState)));
}
So would it mean my view model is cached ? How to disable this cache ?

This is covered in the Wiki in https://github.com/MvvmCross/MvvmCross/wiki/Customising-using-App-and-Setup#overriding-viewmodel-locationconstruction
By default, MvvmCross builds a new ViewModel every time one is requested and uses the CIRS sequence - Construction-Init-ReloadState-Start - to initialize that ViewModel.
If you want to override this behaviour for one or more ViewModel types, then you can do this in your App object by supplying your own IMvxViewModelLocator implementation.
For example, you could implement
public class MyViewModelLocator
: MvxDefaultViewModelLocator
{
private SpecialViewModel _special = new SpecialViewModel();
public override bool TryLoad(Type viewModelType, IDictionary<string, string> parameterValueLookup,
out IMvxViewModel model)
{
if (viewModelType == typeof(SpecialViewModel))
{
model = _special;
return true;
}
else if (viewModelType == typeof(FooViewModel))
{
model = new FooViewModel(_special);
return true;
}
return base.TryLoad(viewModelType, parameterValueLookup, out model);
}
}
and could then return this in App.cs:
protected override IMvxViewModelLocator CreateDefaultViewModelLocator()
{
return new MyViewModelLocator();
}

Related

Convert Android Lifecycle Callback to Reactive Observable

How do I convert an Android lifecycle callback to a RX Observable?
For example, onNewIntent. I could make my Activity itself a custom Observable, calling onNext for a bunch of Observers within onNewIntent - but that feels yucky. I would rather not implement a home-brew Observable if I can help it, so as to not need to mess with multi-threading etc.
namespace ...
{
using ...
/// <summary>
/// Main application activity.
/// </summary>
[Activity(...)]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IObservable<Intent>
{
private List<IObserver<Intent>> observers;
...
/// <inheritdoc/>
public IDisposable Subscribe(IObserver<Intent> observer)
{
this.observers.Add(observer);
return new Unsubscriber(this.observers, observer);
}
/// <inheritdoc/>
protected override void OnNewIntent(Intent intent)
{
foreach (var observer in this.observers)
{
observer.OnNext(intent);
}
}
private class Unsubscriber : IDisposable
{
private List<IObserver<Intent>> observers;
private IObserver<Intent> observer;
private bool disposedValue;
public Unsubscriber(
List<IObserver<Intent>> observers,
IObserver<Intent> observer)
{
this.observers = observers;
this.observer = observer;
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
this.Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
// dispose managed state (managed objects)
if (disposing &&
this.observer != null &&
this.observers.Contains(this.observer))
{
this.observers.Remove(this.observer);
}
// set large fields to null
this.observers = null;
this.observer = null;
this.disposedValue = true;
}
}
}
}
}
If there was something like a NewIntent event, this would be an easier question - there are creation operators for events, delegates, etc. But these all work with the event from outside the event - with something like an onNewIntent override implementation, I'm already inside the event (inside the monad?). So I don't know how to invert that.
I've come up with a solution. Not sure it's the most elegant, but it basically involves creating an additional event that I raise when handling the callback. Then it's just a quick trip to FromEventPattern and hey presto! Observable event data. Would be happy to hear if anyone else has any alternate approaches, but this seems to work.
namespace ...
{
using ...
/// <summary>
/// Main application activity.
/// </summary>
[Activity(...)]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity, IWithNewIntentObservable
{
private event EventHandler<Intent> NewIntent;
/// <inheritdoc/>
public IObservable<Intent> NewIntentObservable { get; private set; }
...
/// <inheritdoc/>
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
...
this.NewIntentObservable = Observable.FromEventPattern<Intent>(
h => this.NewIntent += h,
h => this.NewIntent -= h).Select(e => e.EventArgs);
}
/// <inheritdoc/>
protected override void OnNewIntent(Intent intent)
{
this.NewIntent?.Invoke(this, intent);
}
}
}

MvvmCross initial IMvxViewModel ViewAppeared not being called when in an MvxTabbbedPage on Android

We have an MvxTabbedPage and child MvxContentPages in our Xamarin.Forms project.
On Android, I'm finding that the ViewAppeared override on my first child page is not being called the first time the MvxTabbedPage is being shown.
When switching tabs, it subsequently is being called correctly.
I'm initialising the PageModels in the ViewAppearing for the MvxTabbedPage's PageModel as below:
public override async void ViewAppearing()
{
await ShowInitialViewModels();
base.ViewAppearing();
}
private bool viewModelsInitialised = false;
private async Task ShowInitialViewModels()
{
if (!viewModelsInitialised)
{
await _BusyManager.SetBusy();
var tasks = new List<Task>();
tasks.Add(_MvxNavigationService.Navigate<HomePageModel>());
tasks.Add(_MvxNavigationService.Navigate<MyBenefitsPageModel>());
tasks.Add(_MvxNavigationService.Navigate<ClaimsPageModel>());
tasks.Add(_MvxNavigationService.Navigate<ContactUsPageModel>());
tasks.Add(_MvxNavigationService.Navigate<SettingsPageModel>());
await Task.WhenAll(tasks);
viewModelsInitialised = true;
await _BusyManager.SetUnBusy();
}
}
Have others seen this behaviour, and/or should I be doing something differently?
Looks like it's this Forms bug:
https://github.com/xamarin/Xamarin.Forms/issues/3855
which is referenced by this MvvmCross issue
https://github.com/MvvmCross/MvvmCross/issues/2823
(thanks to Pedro for pointing me in this direction on Slack:)
Check the Playground project of mvvmcross. You should manage your tabs initializations separately in a viewmodel and the XF view code behind.
public class YourTabsViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public YourTabsViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
ShowInitialViewModelsCommand = new MvxAsyncCommand(ShowInitialViewModels);
}
public IMvxAsyncCommand ShowInitialViewModelsCommand { get; private set; }
private async Task ShowInitialViewModels()
{
var tasks = new List<Task>
{
tasks.Add(_navigationService.Navigate<HomePageModel>();
tasks.Add(_navigationService.Navigate<MyBenefitsPageModel>());
tasks.Add(_navigationService.Navigate<ClaimsPageModel>());
tasks.Add(_navigationService.Navigate<ContactUsPageModel>());
tasks.Add(_navigationService.Navigate<SettingsPageModel>());
}
await Task.WhenAll(tasks);
}
}
And then on the code behind of your XF view
[MvxTabbedPagePresentation(TabbedPosition.Root, NoHistory = true)]
public partial class YourTabsPage : MvxTabbedPage<YourTabsViewModel>
{
public YourTabsPage()
{
InitializeComponent();
}
private bool _firstTime = true;
protected override void OnAppearing()
{
base.OnAppearing();
if (_firstTime)
{
ViewModel.ShowInitialViewModelsCommand.ExecuteAsync(null);
_firstTime = false;
}
}
}

How to use 4 simple ViewModels with the same Fragment?

I have an app, which displays 4 lists of words in a Fragment (reusing the same class!):
2-letter words
3-letter words
Words containing Russian eh letter
Words containing Russian hard sign letter
Here a screenshot of the navigation drawer (please excuse the non-english language):
Currently my ViewModel stores all 4 lists as LiveData:
public class WordsViewModel extends AndroidViewModel {
private LiveData<List<Word>> mWords2;
private LiveData<List<Word>> mWords3;
private LiveData<List<Word>> mWordsHard;
private LiveData<List<Word>> mWordsEh;
public WordsViewModel(Application app) {
super(app);
mWords2 = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(2);
mWords3 = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(3);
mWordsHard = WordsDatabase.getInstance(app).wordsDao().fetchWordsContaining("Ъ");
mWordsEh = WordsDatabase.getInstance(app).wordsDao().fetchWordsContaining("Э");
}
public LiveData<List<Word>> getWords(int condition) {
switch (condition) {
case R.id.navi_drawer_letters_2:
return mWords2;
case R.id.navi_drawer_letters_3:
return mWords3;
case R.id.navi_drawer_letter_hard:
return mWordsHard;
case R.id.navi_drawer_letter_eh:
return mWordsEh;
}
return mWords2;
}
}
However I am concerned, that fetching all 4 lists at once is suboptimal and might cause a UI delay.
So I have tried splitting the view model into a base class and then 4 inheriting classes -
WordsViewModel (acting now as base class):
public class WordsViewModel extends AndroidViewModel {
protected LiveData<List<Word>> mWords;
public WordsViewModel (Application app) {
super(app);
}
public LiveData<List<Word>> getWords() {
return mWords;
}
}
And the inheriting classes only differ in the DAO method being called -
TwoViewModel (inheriting class):
public class TwoViewModel extends WordsViewModel {
public TwoViewModel(Application app) {
super(app);
mWords = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(2);
}
}
ThreeViewModel (inheriting class):
public class ThreeViewModel extends WordsViewModel {
public ThreeViewModel(Application app) {
super(app);
mWords = WordsDatabase.getInstance(app).wordsDao().fetchWordsLength(3);
}
}
Finally (and thanks for reading sofar!) here is my Fragment:
public class WordsFragment extends Fragment {
private final ItemAdapter<WordItem> mItemAdapter = new ItemAdapter<>();
private final FastAdapter<WordItem> mFastAdapter = FastAdapter.with(mItemAdapter);
private WordsViewModel mViewModel;
public static WordsFragment newInstance(int condition) {
WordsFragment f = new WordsFragment();
Bundle args = new Bundle();
args.putInt(KEY_CONDITION, condition);
f.setArguments(args);
return f;
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
int condition = (getArguments() == null ? -1 : getArguments().getInt(KEY_CONDITION));
switch (condition) {
case R.id.navi_drawer_letter_eh:
mViewModel = ViewModelProviders.of(this).get(EhViewModel.class);
case R.id.navi_drawer_letter_hard:
mViewModel = ViewModelProviders.of(this).get(HardViewModel.class);
case R.id.navi_drawer_letters_3:
mViewModel = ViewModelProviders.of(this).get(ThreeViewModel.class);
default:
mViewModel = ViewModelProviders.of(this).get(TwoViewModel.class);
}
mViewModel.getWords().observe(this, words -> {
mItemAdapter.clear();
for (Word word: words) {
WordItem item = new WordItem();
item.word = word.word;
item.expl = word.expl;
mItemAdapter.add(item);
}
});
Unfortunately, this breaks my app by always displaying the 2-letter words list.
I wonder, why does this happen (because of inheritance?) and how to solve this?
UPDATE:
Here my code for opening the fragment from the main activity using MaterialDrawer and withTag() and I have verified in debugger and logs (and in the Toast which can be seen in the above screenshot), that the condition variable differs:
private final Drawer.OnDrawerItemClickListener mFetchWordsListener = (view, position, drawerItem) -> {
setTitle(drawerItem);
WordsFragment f = WordsFragment.newInstance( (Integer)drawerItem.getTag() );
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.root, f)
.commitAllowingStateLoss();
return false;
};
mNavigationDrawer.addItems(
....
new SectionDrawerItem().withName(R.string.item_dict),
new PrimaryDrawerItem().withOnDrawerItemClickListener(mFindWordListener).withName(R.string.item_find_word).withIcon(R.drawable.magnify).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_find_word),
new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_2).withIcon(R.drawable.letters_2).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_2).withTag(R.id.navi_drawer_letters_2),
new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_3).withIcon(R.drawable.letters_3).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_3).withTag(R.id.navi_drawer_letters_3),
new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_2).withIcon(R.drawable.letters_hard).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_hard).withTag(R.id.navi_drawer_letters_hard),
new PrimaryDrawerItem().withOnDrawerItemClickListener(mFetchWordsListener).withName(R.string.item_letters_eh).withIcon(R.drawable.letters_eh).withIconTintingEnabled(true).withIdentifier(R.id.navi_drawer_letters_eh).withTag(R.id.navi_drawer_letters_eh)
);
UPDATE 2:
Here my DAO interface and BTW I've noticed that (mViewModel instanceof TwoViewModel) is always true for some reason?
#Dao
public interface WordsDao {
#Query("SELECT * FROM table_words WHERE LENGTH(word) = :length")
LiveData<List<Word>> fetchWordsLength(int length);
#Query("SELECT * FROM table_words WHERE word LIKE '%' || :letter || '%'")
LiveData<List<Word>> fetchWordsContaining(String letter);
}
You need to put a 'break' at the end of each case block to escape out of the switch when a case matching expression is found. Without the break statement, control flow will 'fall through' the different case statements after the first matching case is found. In your code, the default case will always be executed, which loads TwoViewModel.

Observing viewmodel for the second time returns null in android

In my android app,im following architecture components with mvvm pattern.
my app makes a network call to display the weather information.api call is being made from repository which returns a livedata of response to the viewmodel,which inturn is observed by my main activity.
the app works fine except for one condition,whenever i disconnect the internet to test the fail case,it inflates error view as required
in the error view i have a retry button,which makes the method call to observe the viewmodel again(this method was also called by oncreate() for the first time,which worked)
even after switching on the internet,and clicking the retry button which listens for the observable.still the data becomes null.
i dont know why.please anyone help
REPOSITORY
#Singleton public class ContentRepository {
#Inject AppUtils mAppUtils;
private RESTService mApiService;
#Inject public ContentRepository(RESTService mApiService) {
this.mApiService = mApiService;
}
public MutableLiveData<ApiResponse<WeatherModel>> getWeatherListData() {
final MutableLiveData<ApiResponse<WeatherModel>> weatherListData = new MutableLiveData<>();
mApiService.getWeatherList().enqueue(new Callback<WeatherModel>() {
#Override public void onResponse(Call<WeatherModel> call, Response<WeatherModel> response) {
weatherListData.setValue(new ApiResponse<>(response.body()));
}
#Override public void onFailure(Call<WeatherModel> call, Throwable t) {
weatherListData.setValue(new ApiResponse<>(t));
}
});
return weatherListData;
}
}
VIEWMODEL
public class HomeViewModel extends AndroidViewModel {
private final LiveData<ApiResponse<WeatherModel>> weatherListObservable;
#Inject public HomeViewModel(Application application, ContentRepository contentRepository) {
super(application);
this.weatherListObservable = contentRepository.getWeatherListData();
}
public LiveData<ApiResponse<WeatherModel>> getWeatherListObservable() {
return weatherListObservable;
}
}
OBSERVE METHOD IN ACTIVITY
private void observeViewModel() {
mHomeViewModel = ViewModelProviders.of(this, mViewModelFactory).get(HomeViewModel.class);
mHomeViewModel.getWeatherListObservable().observe(this, weatherModelApiResponse -> {
if (weatherModelApiResponse.isSuccessful()) {
mErrorView.setVisibility(View.GONE);
mBinding.ivLoading.setVisibility(View.GONE);
try {
setDataToViews(weatherModelApiResponse.getData());
} catch (ParseException e) {
e.printStackTrace();
}
} else if (!weatherModelApiResponse.isSuccessful()) {
mBinding.ivLoading.setVisibility(View.GONE);
mDialogUtils.showToast(this, weatherModelApiResponse.getError().getMessage());
mErrorView.setVisibility(View.VISIBLE);
}
});
}
RETRY BUTTON IN ACTIVITY
#Override public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_retry:
mErrorView.setVisibility(View.GONE);
observeViewModel();
break;
}
}
Updated:- 5 December 2017
I was fortunate to meet Lyla Fujiwara, during Google Developer Days, India where I asked her the same question. She suggested me to user Transformations.switchMap(). Following is the updated solution -
#Singleton
public class SplashScreenViewModel extends AndroidViewModel {
private final APIClient apiClient;
// This is the observable which listens for the changes
// Using 'Void' since the get method doesn't need any parameters. If you need to pass any String, or class
// you can add that here
private MutableLiveData<Void> networkInfoObservable;
// This LiveData contains the information required to populate the UI
private LiveData<Resource<NetworkInformation>> networkInformationLiveData;
#Inject
SplashScreenViewModel(#NonNull APIClient apiClient, #NonNull Application application) {
super(application);
this.apiClient = apiClient;
// Initializing the observable with empty data
networkInfoObservable = new MutableLiveData<Void>();
// Using the Transformation switchMap to listen when the data changes happen, whenever data
// changes happen, we update the LiveData object which we are observing in the MainActivity.
networkInformationLiveData = Transformations.switchMap(networkInfoObservable, input -> apiClient.getNetworkInformation());
}
/**
* Function to get LiveData Observable for NetworkInformation class
* #return LiveData<Resource<NetworkInformation>>
*/
public LiveData<Resource<NetworkInformation>> getNetworkInfoObservable() {
return networkInformationLiveData;
}
/**
* Whenever we want to reload the networkInformationLiveData, we update the mutable LiveData's value
* which in turn calls the `Transformations.switchMap()` function and updates the data and we get
* call back
*/
public void setNetworkInformation() {
networkInfoObservable.setValue(null);
}
}
The Activity's code will be updated as -
final SplashScreenViewModel splashScreenViewModel =
ViewModelProviders.of(this, viewModelFactory).get(SplashScreenViewModel.class);
observeViewModel(splashScreenViewModel);
// This function will ensure that Transformation.switchMap() function is called
splashScreenViewModel.setNetworkInformation();
This looks the most prominent and proper solution to me for now, I will update the answer if I better solution later.
Watch her droidCon NYC video for more information on LiveData. The official Google repository for LiveData is https://github.com/googlesamples/android-architecture-components/ look for GithubBrowserSample project.
Old Code
I have not been able find a proper solution to this, but this works so far -
Declare ViewModel outside the observeViewModel() and change the function like this -
private void observeViewModel(final HomeViewModel homeViewModel) {
homeViewModel.getWeatherListObservable().observe(this, weatherModelApiResponse -> {
if (weatherModelApiResponse.isSuccessful()) {
mErrorView.setVisibility(View.GONE);
mBinding.ivLoading.setVisibility(View.GONE);
try {
setDataToViews(weatherModelApiResponse.getData());
} catch (ParseException e) {
e.printStackTrace();
}
} else if (!weatherModelApiResponse.isSuccessful()) {
mBinding.ivLoading.setVisibility(View.GONE);
mDialogUtils.showToast(this, weatherModelApiResponse.getError().getMessage());
mErrorView.setVisibility(View.VISIBLE);
}
});
}
Update HomeViewModel to -
public class HomeViewModel extends AndroidViewModel {
private final LiveData<ApiResponse<WeatherModel>> weatherListObservable;
#Inject public HomeViewModel(Application application, ContentRepository contentRepository) {
super(application);
getWeattherListData();
}
public void getWeatherListData() {
this.weatherListObservable = contentRepository.getWeatherListData();
}
public LiveData<ApiResponse<WeatherModel>> getWeatherListObservable() {
return weatherListObservable;
}
}
Now Retry button, call the observeViewModel function again and pass mHomeViewModel to it. Now you should be able to get a response.

Dagger2 getting instance of activity presenter in fragment presenter

I'm really lost and hope you can help me. I'm programming app using MVP and dagger2. One of activities (let's say A ) contains fragments ( B ) in FragmentStatePagerAdapter. Both activity and fragments have their own presenters. I'm handling input in fragments ( B ), and transfer data from it "up" to main presenter ( B -> A ) (of activity holding fragments) and there ( A ) I'm handling network connection.
For this I need instance of main presenter ( A ) in fragment presenter ( B ). I've tried to use dependency on fragment graph but instead of getting already existing instance of presenter it's creating new one with every init. Any advises how I could get something similar to singleton but using ActivityScope?
A graph:
#ActivityScope
#Component(modules = GiftListModule.class, dependencies = AppGraph.class)
public interface GiftListGraph extends AppGraph{
void inject(GiftListActivity giftListActivity);
GiftListPresenter getGiftListPresenter();
final class Initializer {
public static GiftListGraph init(AppGraph appGraph, GiftListView giftListView) {
return DaggerGiftListGraph.builder()
.giftListModule(new GiftListModule(giftListView))
.appGraph(appGraph)
.build();
}
}
}
A module:
#Module
public class GiftListModule {
private final GiftListView giftListView;
public GiftListModule(GiftListView giftListView) {
this.giftListView = giftListView;
}
#Provides GiftListView provideGiftListView() {
return giftListView;
}
#Provides GiftListPresenter provideGiftListPresenter(GiftListView giftListView) {
return new GiftListPresenterImpl(giftListView);
}
}
B graph:
#FragmentScope
#Component(modules = GiftDetailsModule.class, dependencies = GiftListGraph.class)
public interface GiftDetailsGraph {
void inject(GiftDetailsFragment giftDetailsFragment);
GiftDetailsPresenter getGiftDetailsPresenter();
final class Initializer {
public static GiftDetailsGraph init(GiftListGraph giftListGraph, GiftDetailsView giftDetailsView) {
return DaggerGiftDetailsGraph.builder()
.giftDetailsModule(new GiftDetailsModule(giftDetailsView))
.giftListGraph(giftListGraph)
.build();
}
}
}
B module:
#Module
public class GiftDetailsModule {
private final GiftDetailsView giftDetailsView;
public GiftDetailsModule(GiftDetailsView giftDetailsView) {
this.giftDetailsView = giftDetailsView;
}
#Provides GiftDetailsView provideGiftDetailsView() {
return giftDetailsView;
}
#Provides GiftDetailsPresenter provideGiftDetailsPresenter(GiftDetailsView giftDetailsView,
GiftListPresenter giftListPresenter) {
return new GiftDetailsPresenterImpl(giftDetailsView, giftListPresenter);
}
}
Main App:
public class MainApp extends Application {
private static MainApp sInstance;
protected AppGraph appGraph;
protected GiftListGraph giftListGraph;
#Override
public void onCreate() {
super.onCreate();
sInstance = this;
appGraph = AppGraph.Initializer.init(this);
}
public static MainApp getInstance() {
return sInstance;
}
...
public GiftListGraph getGiftListGraph(GiftListView view) {
return giftListGraph = GiftListGraph.Initializer.init(appGraph, view);
}
public GiftDetailsGraph getGiftDetailsGraph(GiftDetailsView view) {
return GiftDetailsGraph.Initializer.init(giftListGraph, view);
}
...
}
Thanks for any help you can give me :)
From DI perspective you are seeing the correct behavior. When you are calling get list graph or detail graph, you are building entirely new graph. (See. new Module calls in your initializer calls). Hence you are getting new instance each time.
There are few options I would consider.
One:
Have a callback interface defined at the activity scope. Have activity implement it. In the process of creating fragment graphs pass the callback impl instance (activity or some impl class) as argument. Your fragment a/b presenter can use that as a dependency. Now both fragment presenter gets a call back.
Two:
Use event bus or broadcast receivers that run at app scope or activity scope. Use that to post message back and forth.
Hope this gives you some ideas.

Categories

Resources