Android ViewModel recreated on screen rotation - android

I found a case when architecture components ViewModel isn't retained - in short it goes as follows:
Activity is started and ViewModel instance is created
Activity is put to background
Device screen is rotated
Activity is put back to foreground
ViewModel's onCleared method is called and new object is created
Is it normal behavior of Android that my ViewModel instance is getting destroyed in this case? If so, is there any recommended solution of keeping its state?
One way I can think of is saving it once onCleared is called, however, it would also persist the state whenever activity is actually finishing. Another way could be making use of onRestoreInstanceState but it's fired on every screen rotation (not only if the app is in background).
Any silver bullet to handle such case?

Yes #tomwyr, this was a bug from an android framework. Bug details
The fix is available in 28.0.0-alpha3 and AndroidX 1.0.0-alpha3
But if you don't want to update to above versions now itself, Then you can solve like this (I know this is a bad solution but I didn't see any other good way)
In your activity override onDestroy method and save all the required fields to local variables before calling super.onDestroy. Now call super.onDestroy then Initialize your ViewModel again and assign the required fields back to your new instance of ViewModel
about isFinishing
Below code is in Kotlin:
override fun onDestroy() {
val oldViewModel = obtainViewModel()
if (!isFinishing) { //isFinishing will be false in case of orientation change
val requiredFieldValue = oldViewModel.getRequiredFieldValue()
super.onDestroy
val newViewModel = obtainViewModel()
if (newViewModel != oldViewModel) { //View Model has been destroyed
newViewModel.setRequiredFieldValue(requiredFieldValue)
}
} else {
super.onDestroy
}
}
private fun obtainViewModel(): SampleViewModel {
return ViewModelProviders.of(this).get(SampleViewModel::class.java)
}

AFAIK, ViewModel's only purpose is to survive and keep the data (i.e. "save the state") while its owner goes through different lifecycle events. So you don't have to "save the state" yourself.
We can tell from this that it's "not normal behavior". onCleared() is only called after the activity is finished (and is not getting recreated again).
Are you creating the ViewModel using the ViewModelProvider, or are you creating the instance using the constructor?
In your activity, you should have something like:
// in onCreate() - for example - of your activity
model = ViewModelProviders.of(this).get(MyViewModel.class);
// then use it anywhere in the activity like so
model.someAsyncMethod().observe(this, arg -> {
// do sth...
});
By doing this, you should get the expected effect.

For others that may not be helped by previous answers like me, the problem could be that you haven't set up your ViewModelProvider properly with a factory.
After digging around I solved my similiar problem by adding the following method to my Activities:
protected final <T extends ViewModel> T obtainViewModel(#NonNull AppCompatActivity activity, #NonNull Class<T> modelClass) {
ViewModelProvider.AndroidViewModelFactory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(activity.getApplication());
return new ViewModelProvider(activity, factory).get(modelClass);
}
And then I did this in my Fragments:
protected final <T extends ViewModel> T obtainFragmentViewModel(#NonNull FragmentActivity fragment, #NonNull Class<T> modelClass) {
ViewModelProvider.AndroidViewModelFactory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(fragment.getApplication());
return new ViewModelProvider(fragment, factory).get(modelClass);
}
I already had some abstract super classes for menu purposes so I hid the methods away there so I don't have to repeat it in every activity. That's why they are protected. I believe they could be private if you put them in every activity or fragment that you need them in.
To be as clear as possible I would then call the methods to assign my view model in onCreate() in my activity and it would look something like this
private MyViewModel myViewModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myViewModel = obtainViewModel(this, MyViewModel.class);
}
or in fragment
private MyViewModel myViewModel;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getActivity() != null) {
myViewModel = obtainFragmentViewModel(getActivity(), MyViewModel.class);
}
}

Change support library/compileSDK/targetSDK to 28.
I had similar issue with multi-window. When switching to split screen, my viewModel is recreated. Support library 28 fixed my problem. (My lifecycle version is 1.1.1)

Related

Where to declare functions which are used in every activity? [duplicate]

This question already has answers here:
How do I share common functions and data across many activities in a single android application
(4 answers)
Closed 2 years ago.
I call a function in onCreate function of an Activity to refactor my code. I wonder where to declare this function that is potentially used in every Activity of my app.
What I have done (it works fine) is to create a function class with a companion object where all my global functions are declared. But my question is: Is it a good way to do like that?
I call a function in onCreate function of an activity to factor my
code. I wonder where to declare this function that is potentially used
in every activity of my app.
I would create a BaseActivity and let all your Activities inherit from it.
abstract class BaseActivity : AppCompatActivity() {
private fun init() {
// your code
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
init()
}
}
In case your init function does not depend on anything which comes from the subclass, you can just invoke it in onCreate each time (as shown above), otherwise make it protected and call it from the subclass (with parameters).
What I have done (it works fine) is to create a Function class with a
companion object where all my global functions are declared. But my
question is : is it a good way to do like that ?
It depends on if you need global shared state or not. In the first case using an object or a companion object would not be a bad idea.
If you don't need global state, or what to pass in whatever state to the utility function itself, a top level function would be sufficient.
Utils.kt
fun someUtilityFunction(foo: Int) {
// ...
}
You can create some BaseActivity or YourAppNameActivity and call your function inside its onCreate. Then, every activity that extends BaseActivity as usually will call super.onCreate() and therefore the code you need
As long you do not have shared (mutable) state (as it can lead to side effects, there is nothing wrong in placing common code into companion object.
You can have a BaseActivity you extend your Activities from, but I would try to avoid inheritance in favor of composition.
If your method is touching the activity's view then BaseActivity approach would be fine. But if it doesn't move it to some singleton ActivityHelper class.
Like said, BaseActivity approach (inheritance) comes with a cost. You should be able to make good design choices by not putting everything inside it which will eventually makes it more coupled.
Follow composition pattern only if you find your code is interfering with its lifecycle. There are a few registerLifecycle callbacks for activity or fragment that can help you.
It's a good practice to move all that common code to a parent class and make each activiy heredate that parent class, by the way creating a companion object its a good option only if you want to create a singleton, a singleton it's needed when you want to instance that object only once.
For example a function in baseActivity (parent class) to create an intent filter or add code to onCreate function
public class BaseActivity extends Activity {
public static final String FINISH_ALL_ACTIVITIES = "somecode";
public final IntentFilter INTENT_FILTER = createIntentFilter();
private boolean _started;
private IntentFilter createIntentFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(FINISH_ALL_ACTIVITIES_ACTIVITY);
return filter;
}
// region Blindaje de eventos ciclo de vida
#Override
protected void onCreate(Bundle savedInstanceState) {
// inside your activity (if you did not enable transitions in your theme)
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
super.onCreate(savedInstanceState);
try {
doOnPostCreate(savedInstanceState);
} catch (Throwable t) {
doOnErrorNoControlado(t);
}
}

How to pass complex, non serializable object to android fragments

Hello fellow Android developers,
I wanna know how do you guys pass complex non serializable (& non parcelable) object to fragments. (such as Listener, Api client, ...)
Let me explain my use case:
The use case
I'm building an Android application composed of one "host" activity and 3 fragments.
Currently I'm passing the object using a custom constructor on the fragment (bad practice I know).
The fragments constructors looks like the following:
/**
* Do not remove ever or you'll face RuntimeException
*/
public FirstFragment() {
}
public FirstFragment(Session session,
ApiClient apiClient,
FirebaseAnalytics firebaseAnalytics) {
mSession = session;
mApiClient = apiClient;
mFirebaseAnalytics = firebaseAnalytics;
}
And I'm using them in the host activity like this
private FirstFragment getFirstFragment() {
if (mFirstFragment == null) {
mFirstFragment = new FirstFragment(mSession, mApiClient, mFirebaseAnalytics);
}
return mHomeFragment;
}
[...]
private void loadFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container, fragment, tag);
transaction.commit();
}
[...]
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case FIRST_FRAGMENT_RES_ID:
toolbar.setTitle(R.string.first_fragment_title);
loadFragment(getFirstFragment(), "first_fragment");
return true;
[...]
}
return false;
}
};
This solution works well almost all the time. But sometimes (and I don't know when exactly) the default constructor is invoked and therefore all local members are null.
Possible solutions
To solve the problem I'm thinking about the following solutions:
Singletons, singletons everywhere
Most of the objects I'm passing are singletons therefore I can access them in the default constructor of the fragments:
public FirstFragment() {
mSession = Session.getInstance(getContext());
mApiClient = ApiClient.getInstance(getContext());
mFirebaseAnalytics = FirebaseAnalytics.getInstance(getContext());
}
Problems
However the above solution wouldn't work if I need to pass a callback or something. How can it be done like this then?
Access the objects using parent activity
I think it's one of the ugliest possible solutions because it will couple the Fragments to the parent activity. The idea is something like this
public FirstFragment() {
mSession = Session.getInstance(getContext());
mApiClient = ApiClient.getInstance(getContext());
mFirebaseAnalytics = FirebaseAnalytics.getInstance(getContext());
mListener = (Listener) getActivity(); // <- will works because parent activity implement the interface
}
Using broadcast & receiver
The idea is to keep passing singleton everywhere and use broadcast & receiver instead of listener.
How do you guys managed this scenario?
Thanks in advance !
You probably want to look into dependency injection (using a tool like Dagger or alternatives), especially for objects like an Api Client. Post the setup, you'd define, just once, how an Api Client instance could be constructed. And later you can use it pretty much everywhere with a one-line statement. The instance is guaranteed to be available upon the fragment instantiation. Further reading: https://dagger.dev/tutorial/
According to your use case, it might be easier to use a ViewModel and store your objects there. Your ViewModel will be shared across your fragments and your host
activity.
See https://developer.android.com/topic/libraries/architecture/viewmodel
Have you considered using "Shared" ViewModel?
Essentially, a sub-class of ViewModel (which is class designed to store and manage UI-related data in a lifecycle conscious way for activities and fragments) can be created like below,
class SharedViewModel : ViewModel()
Inside this class you can have your custom objects with their correct state
Next, in your 1st Fragment you can obtain a handle to this SharedViewmodel like below,
class MasterFragment : Fragment() {
private lateinit var model: SharedViewModel
And obtain the handle to it using below code,
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
}
You can write your own logic/method/flow inside SharedViewModel to manipulate any custom object's states.
And once all this is done, In your 2nd Fragment, you can create the handle to SharedViewModel similar to above code and using SharedViewModel object you can retrieve the "modified" custom object from same SharedViewModel
It's been several months and I have now come up with a different solution.
For the UI related data
For the UI related stuff I'm now using the androidx livedata
For the complex non serializable data
My use case was to pass complex object to the fragment, such as manager, parent activity (trough a listener), etc... The approach I have taken is by injecting these data manually from the parent activity.
The first things to do was to remove the objects from the fragment constructor and use the default constructor instead, so that I won't face any instantiation errors.
Then I have created an inject() method on the fragment classes that look like this:
public void inject(BillingManager billingManager, Listener listener) {
mBillingManager = billingManager;
mListener = listener;
}
Each fragment will have their own inject method width the objects that should be injected as parameters.
In the parent activity I have override the onAttachFragment() method to handle the fragment attach process:
#Override
public void onAttachFragment(#NonNull Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass().equals(FirstFragment.class)) {
((FirstFragment) fragment).inject(mBillingManager, this);
} else if (fragment.getClass().equals(HomeFragment.class)) {
((HomeFragment) fragment).inject(this);
}
}
Simple, and now everything work great.

When is the viewmodel onCleared called

Are ViewModels independent of activity/fragment lifecycles or just their configuration changes. When will they cease to exist and the subsequent onCleared() method called.
Can the viewModel be shared with another Activity ?
A situation:
Activity1+viewModel1--->(rotation)--->Activity1+viewModel1
--->(launch Intent)--->Activity2+viewModel1
is this sharing possible and is it a good practice.
Also, since the app lifecycle callbacks, onPause->onStop->onDestroy is same for both
1.activity rotating and
2.when an Activity ends,
how is a ViewModel figuring out internally the right time to call onCleared and finally end its lifecycle.
Findings:
the ViewModel uses a holderFragment internally to hold an instance of the activity and uses the setRetainInstance method like fragments to account for configuration changes.
Source: dive-inside-of-androids-viewmodel-architecture-components
Are ViewModels independent of activity/fragment lifecycles or just
their configuration changes.
ViewModels (VMs) are independent of configuration changes and are cleared when activity/fragment is destroyed.
Following is the lifecycle of ViewModel from official site:
Can the viewModel be shared with another Activity ?
You shouldn't do that with Activities. However fragments can share a ViewModel using their activity scope to handle communication between them
How is a ViewModel figuring out internally the right time to call onCleared and finally end its lifecycle?
A VM's onCleared is called when the app is put into the background and the app process is killed in order to free up the system's memory.
See the Do ViewModels persist my data? section from this Android Developer's post, ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loaders
If you want the user to be able to put the app into the background and then come back three hours later to the exact same state, you should also persist data. This is because as soon as your activity goes into the background, your app process can be stopped if the device is running low on memory.
If the app process and activity are stopped, then the ViewModel will be cleared as well.
Check method onDestroy() in Fragment.java
public void onDestroy() {
this.mCalled = true;
FragmentActivity activity = this.getActivity();
boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
if (this.mViewModelStore != null && !isChangingConfigurations) {
this.mViewModelStore.clear();
}
}
The variant isChangingConfigurations is true when the Activity rotates, the viewModelStore method clear() is not called.
When Activity is destroyed, isChangingConfigurations is false, the viewModelStore will be cleared.
Through the source code, we know the ViewModel binds with HolderFragment. you can from the code in class ViewModelProviders to find it.
#MainThread
public static ViewModelProvider of(#NonNull FragmentActivity activity,
#NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
next, in-class HolderFragment on it's onDestroy() you can find
#Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}
Last, open it,
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
now, maybe you have know it. just like the picture above. When the fragment finished, it cleared; when activity recreate,the fragment's onDestroy() will not be invoked, because
public HolderFragment() {
setRetainInstance(true);
}
hope it can help you.
If you follow the trail (Check super class)
AppCompatActivity --> FragmentActivity --> ComponentActivity
ComponentActivity observe the lifecycle state.
onDestory() calls at configuration change (such as screen rotation) but viewModel doesn't get destroy because of the following condition.
getLifecycle().addObserver(new GenericLifecycleObserver() {
#Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
I wanted my VM's onClear to be called when the Activity was finishing. I use onPause, because the call to onDestroy is not always immediately executed...it could be a few seconds after onPause:
class SomeActivity : AppCompatActivity() {
override fun onPause() {
super.onPause()
// viewmodel is not always cleared immediately after all views detach from it, which delays
// the vm's cleanup code being called, which lets the resources continue running
// after all UIs detach, which is weird, because I was using timers and media players.
// this makes the VM execute onCleared when its Activity detaches from it.
if (isFinishing) {
viewModelStore.clear()
}
}
}
And here is the order of execution in respect to the GenericLifecycleObserver:
onStateChanged()
onResume()/onDestroy()/etc.
Meaning the observer received the information about the pending state change before it's completed, so for exemple onDestroy() method is finished.

Instantiate ViewModels directly, without making use of ViewModelProviders.of method

I have a ViewModel called RecipesViewModel. Usually, I instantiated it this way:
RecipesViewModel viewModel = ViewModelProviders.of(this, new ViewModelProvider.Factory() {
#Override
public <T extends ViewModel> T create(Class<T> modelClass) {
return (T) new RecipesViewModel(recipesRepository);
}
}).get(RecipesViewModel.class);
But now I'm using dagger2 and so I put a #Inject annotation on the constructor of this ViewModel, so I'm able to inject it directly in my fragment, using field injector.
My question is: do I lose something starting the viewmodel this way instead of ViewModelProviders.of way? My ViewModel is already Scoped, so only one instance is create in context.
Other option is to move only the factory instantiation to a dagger2 module, but if there is no problem I prefer the first aproach.
-- EDIT --
Reading the documentation android.arch.lifecycle.ViewModel, I'm a little more afraid. Whe use ViewModelProviders.of to provide a Scope (fragment or activity). If I instantiate it directly what will be the Scope?
ViewModel is a class that is responsible for preparing and managing
the data for an Activity or a Fragment. It also handles the
communication of the Activity / Fragment with the rest of the
application (e.g. calling the business logic classes).
A ViewModel is always created in association with a scope (an fragment
or an activity) and will be retained as long as the scope is alive.
E.g. if it is an Activity, until it is finished.
In other words, this means that a ViewModel will not be destroyed if
its owner is destroyed for a configuration change (e.g. rotation). The
new instance of the owner will just re-connected to the existing
ViewModel.
-- /EDIT --
The RecipesViewModel code is showing below:
#PerActivity
public class RecipesViewModel extends ViewModel {
private static final String TAG = "RecipesViewModel";
private final RecipesRepository recipesRepository;
private LiveData<List<Recipe>> recipes = null;
#Inject
public RecipesViewModel(RecipesRepository recipesRepository) {
this.recipesRepository = recipesRepository;
}
public final void loadAll() {
recipes = recipesRepository.getRecipes();
}
public LiveData<List<Recipe>> getRecipes() {
return recipes;
}
}
For me right now (and I need to research this), but injecting a view model instead of using the ViewModelProviders functionality means you lose some easy activity-fragment communication.
For example from the docs they provide an example of an activity hosting 2 fragments. If one fragment needs to talk to another, the previous method was to maintain an interface via the activity who also had to take care of the lifecycle of that interface. Instead now you can just fetch it from the the ViewModelProviders 'repo' whenever you need.

Communicating between a fragment and an activity - best practices

This question is mostly to solicit opinions on the best way to handle my app. I have three fragments being handled by one activity. Fragment A has one clickable element the photo and Fragment B has 4 clickable elements the buttons. The other fragment just displays details when the photo is clicked. I am using ActionBarSherlock.
The forward and back buttons need to change the photo to the next or previous poses, respectively. I could keep the photo and the buttons in the same fragment, but wanted to keep them separate in case I wanted to rearrange them in a tablet.
I need some advice - should I combine Fragments A and B? If not, I will need to figure out how to implement an interface for 3 clickable items.
I considered using Roboguice, but I am already extending using SherlockFragmentActivity so that's a no go. I saw mention of Otto, but I didn't see good tutorials on how to include in a project. What do you think best design practice should be?
I also need help figuring out how to communicate between a fragment and an activity. I'd like to keep some data "global" in the application, like the pose id. Is there some example code I can see besides the stock android developer's information? That is not all that helpful.
BTW, I'm already storing all the information about each pose in a SQLite database. That's the easy part.
The easiest way to communicate between your activity and fragments is using interfaces. The idea is basically to define an interface inside a given fragment A and let the activity implement that interface.
Once it has implemented that interface, you could do anything you want in the method it overrides.
The other important part of the interface is that you have to call the abstract method from your fragment and remember to cast it to your activity. It should catch a ClassCastException if not done correctly.
There is a good tutorial on Simple Developer Blog on how to do exactly this kind of thing.
I hope this was helpful to you!
The suggested method for communicating between fragments is to use callbacks\listeners that are managed by your main Activity.
I think the code on this page is pretty clear:
http://developer.android.com/training/basics/fragments/communicating.html
You can also reference the IO 2012 Schedule app, which is designed to be a de-facto reference app. It can be found here:
http://code.google.com/p/iosched/
Also, here is a SO question with good info:
How to pass data between fragments
It is implemented by a Callback interface:
First of all, we have to make an interface:
public interface UpdateFrag {
void updatefrag();
}
In the Activity do the following code:
UpdateFrag updatfrag ;
public void updateApi(UpdateFrag listener) {
updatfrag = listener;
}
from the event from where the callback has to fire in the Activity:
updatfrag.updatefrag();
In the Fragment implement the interface in CreateView do the
following code:
((Home)getActivity()).updateApi(new UpdateFrag() {
#Override
public void updatefrag() {
.....your stuff......
}
});
To communicate between an Activity and Fragments, there are several options, but after lots of reading and many experiences, I found out that it could be resumed this way:
Activity wants to communicate with child Fragment => Simply write public methods in your Fragment class, and let the Activity call them
Fragment wants to communicate with the parent Activity => This requires a bit more of work, as the official Android link https://developer.android.com/training/basics/fragments/communicating suggests, it would be a great idea to define an interface that will be implemented by the Activity, and which will establish a contract for any Activity that wants to communicate with that Fragment. For example, if you have FragmentA, which wants to communicate with any activity that includes it, then define the FragmentAInterface which will define what method can the FragmentA call for the activities that decide to use it.
A Fragment wants to communicate with other Fragment => This is the case where you get the most 'complicated' situation. Since you could potentially need to pass data from FragmentA to FragmentB and viceversa, that could lead us to defining 2 interfaces, FragmentAInterface which will be implemented by FragmentB and FragmentAInterface which will be implemented by FragmentA. That will start making things messy. And imagine if you have a few more Fragments on place, and even the parent activity wants to communicate with them. Well, this case is a perfect moment to establish a shared ViewModel for the activity and it's fragments. More info here https://developer.android.com/topic/libraries/architecture/viewmodel . Basically, you need to define a SharedViewModel class, that has all the data you want to share between the activity and the fragments that will be in need of communicating data among them.
The ViewModel case, makes things pretty simpler at the end, since you don't have to add extra logic that makes things dirty in the code and messy. Plus it will allow you to separate the gathering (through calls to an SQLite Database or an API) of data from the Controller (activities and fragments).
I made a annotation library that can do the cast for you. check this out.
https://github.com/zeroarst/callbackfragment/
#CallbackFragment
public class MyFragment extends Fragment {
#Callback
interface FragmentCallback {
void onClickButton(MyFragment fragment);
}
private FragmentCallback mCallback;
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt1
mCallback.onClickButton(this);
break;
case R.id.bt2
// Because we give mandatory = false so this might be null if not implemented by the host.
if (mCallbackNotForce != null)
mCallbackNotForce.onClickButton(this);
break;
}
}
}
It then generates a subclass of your fragment. And just add it to FragmentManager.
public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), "MY_FRAGM")
.commit();
}
Toast mToast;
#Override
public void onClickButton(MyFragment fragment) {
if (mToast != null)
mToast.cancel();
mToast = Toast.makeText(this, "Callback from " + fragment.getTag(), Toast.LENGTH_SHORT);
mToast.show();
}
}
Google Recommended Method
If you take a look at this page you can see that Google suggests you use the ViewModel to share data between Fragment and Activity.
Add this dependency:
implementation "androidx.activity:activity-ktx:$activity_version"
First, define the ViewModel you are going to use to pass data.
class ItemViewModel : ViewModel() {
private val mutableSelectedItem = MutableLiveData<Item>()
val selectedItem: LiveData<Item> get() = mutableSelectedItem
fun selectItem(item: Item) {
mutableSelectedItem.value = item
}
}
Second, instantiate the ViewModel inside the Activity.
class MainActivity : AppCompatActivity() {
// Using the viewModels() Kotlin property delegate from the activity-ktx
// artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.selectedItem.observe(this, Observer { item ->
// Perform an action with the latest item data
})
}
}
Third, instantiate the ViewModel inside the Fragment.
class ListFragment : Fragment() {
// Using the activityViewModels() Kotlin property delegate from the
// fragment-ktx artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by activityViewModels()
// Called when the item is clicked
fun onItemClicked(item: Item) {
// Set a new item
viewModel.selectItem(item)
}
}
You can now edit this code creating new observers or settings methods.
There are severals ways to communicate between activities, fragments, services etc. The obvious one is to communicate using interfaces. However, it is not a productive way to communicate. You have to implement the listeners etc.
My suggestion is to use an event bus. Event bus is a publish/subscribe pattern implementation.
You can subscribe to events in your activity and then you can post that events in your fragments etc.
Here on my blog post you can find more detail about this pattern and also an example project to show the usage.
I'm not sure I really understood what you want to do, but the suggested way to communicate between fragments is to use callbacks with the Activity, never directly between fragments. See here http://developer.android.com/training/basics/fragments/communicating.html
You can create declare a public interface with a function declaration in the fragment and implement the interface in the activity. Then you can call the function from the fragment.
I am using Intents to communicate actions back to the main activity. The main activity is listening to these by overriding onNewIntent(Intent intent). The main activity translates these actions to the corresponding fragments for example.
So you can do something like this:
public class MainActivity extends Activity {
public static final String INTENT_ACTION_SHOW_FOO = "show_foo";
public static final String INTENT_ACTION_SHOW_BAR = "show_bar";
#Override
protected void onNewIntent(Intent intent) {
routeIntent(intent);
}
private void routeIntent(Intent intent) {
String action = intent.getAction();
if (action != null) {
switch (action) {
case INTENT_ACTION_SHOW_FOO:
// for example show the corresponding fragment
loadFragment(FooFragment);
break;
case INTENT_ACTION_SHOW_BAR:
loadFragment(BarFragment);
break;
}
}
}
Then inside any fragment to show the foo fragment:
Intent intent = new Intent(context, MainActivity.class);
intent.setAction(INTENT_ACTION_SHOW_FOO);
// Prevent activity to be re-instantiated if it is already running.
// Instead, the onNewEvent() is triggered
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(intent);
There is the latest techniques to communicate fragment to activity without any interface follow the steps
Step 1- Add the dependency in gradle
implementation 'androidx.fragment:fragment:1.3.0-rc01'

Categories

Resources