Data Sharing Between Fragments and Activity in Android

How to share data between fragments and activities in android

For between activities you can putExtra values such as below:

Intent intent = new Intent (this, newActivity.class);
intent.putExtra("someKey", someValue);
intent.putExtra(bundle);
startActivity(intent);

To get it inside your activity:

getIntent().getExtra("someKey");

For moving values between fragments i'd suggest using bundles:

//Where mainlayout is the top level id of your xml layout and R.id.viewProfile is the id of the action within your navigation xml.
NavController navController = Navigation.findNavController(getActivity(), R.id.mainlayout);
Bundle bundle = new Bundle();
bundle.putString("uid",snapshot.getKey());
navController.navigate(R.id.viewProfile,bundle);

to retrieve this value within your fragment:

String game = getArguments().getString("game");

Hopefully that helps.

share argument between all fragments

  • You can use shareviewmodel, it's best practice
  • Link refer: viewmodel

How to Share Data between Activity and Fragment via ViewModel Class in Android?

It really depends on how do you create your ViewModel instance. Now you are creating ViewModel by its constructor, but that is not a proper way. You should use ViewModelProvider or extension methods that were created by Google team.

If you go with ViewModelProvider you should do it like this:

TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);

It is important to pass the correct context to ViewModelProvider constructor call. If you are in fragment and you will just use getContext() instead of getActivity(), you will not get the same instance as it was created in Activity. You will create a new instance of ViewModel, that will be scoped only inside of fragment lifecycle. So it is important to use in both parts activity context to get the same instance.

Activity part:

TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);

Fragment part:

TimeTableViewModel viewModel = new ViewModelProvider(getActivity()).get(TimeTableViewModel.class);

Is important that your fragment is located inside the same activity that is using this ViewModel.

But guys at Google has make it easier for us with some extension methods. But as far as I know, they are working only in Kotlin classes. So if you have Kotlin code, you can declare your ViewModel simply like this:

private val quizViewModel: TimeTableViewModel by activityViewModels()

For Fragment scoped ViewModel you need to write something like this:

private val quizViewModel: TimeTableViewModel by viewModels()

But you have to add Kotlin ktx dependency to your project build.gradle file. For example like this:

implementation 'androidx.fragment:fragment-ktx:1.1.0'

Which is the better way to share data between Fragments?

It totally depends on your use case, what data you share between fragments and how it is used.

Even though both cases support passing custom objects you have to consider a few things.

In the case of the navigation component, regarding custom objects:

  • data must be either Parcelable (preferred) or Serializable;
  • all of the data passed through using Bundle object which has its own limitations and with large data can lead to TransactionTooLargeException (more about it). It can be easily avoided when using shared view model.

In the case of shared view models:

  • you have to define new classes to wrap your models;
  • you have to add new class members to hold shared view models;
  • a shared view model will take memory until Activity is finished or the view model store is cleared.

So what is the answer?

Such a question leads to opinionated answers, but I consider following a set of hints to use when choosing between safe argument and shared view model.

Use safe arguments when:

  1. data is small;
  2. when you do not need to return results to the previous fragment after using this data;
  3. you do not care if this data is removed from memory when you are done with it and you have to query it again each time.

Use a shared view model when:

  1. data is relatively large or you assume it could be large;
  2. you have multiple data models/sets;
  3. when you do need to return results to the previous fragment after using this data;
  4. when data is expensive to query. Shared view models typically live as long as the activity.

How to pass data that changes real-time from activity to fragment?

class MyViewModel : ViewModel() {
private val realtimedata = MutableLiveData<ByteArray>()

val sensorData: LiveData<ByteArray> = realtimedata

fun update(data: ByteArray){
realtimedata.value = data
}
}

class MainActivity: Activity() {

private val viewModel: MyViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

bluetoothSensorCallBack { data ->
// Update the realtimedata
viewModel.update(data)
}
}
}

class SensordisplayFragment : Fragment() {

// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: MyViewModel by activityViewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.sensorData.observe(viewLifecycleOwner, Observer<ByteArray> { data ->
// Update the UI
})
}
}

How can i share data across multiple fragments in application?

I'm assuming you want to share data between fragments.
One way to do that is through ViewModels

Specifically, you'll need a view model like this:

public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

public void select(Item item) {
selected.setValue(item);
}

public LiveData<Item> getSelected() {
return selected;
}
}

Then you have your fragments:

class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()

fun select(item: Item) {
selected.value = item
}
}

public class ListFragment extends Fragment {
private SharedViewModel model;

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}

public class DetailFragment extends Fragment {

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}

Notice that both fragments retrieve the activity that contains them. That way, when the fragments each get the ViewModelProvider, they receive the same SharedViewModel instance, which is scoped to this activity.

If you want to know more about this, refer to: https://developer.android.com/topic/libraries/architecture/viewmodel

Sending Data between Fragments using Shared Viewmodel and SharedFlow

Your FragmentB only collects when it is in started state or higher. This is correct because you don't want to be working with views when it's not visible and possibly doesn't currently have views.

However, since your SharedFlow has no replay history, this means there is nothing for FragmentB to collect when it is back on screen. Presumably it is off screen when FragmentA updates the flow.

So, your SharedFlow with no replay has nothing to do when it currently has no collectors, and the emitted value is thrown away. You need to have a replay of at least 1 for this to work.

private val _sharedFlow= MutableSharedFlow<String>(replay = 1)

You mentioned "without retaining its state", but this isn't possible without state if the two fragments are not on screen at the same time.

By the way, there is a simpler way to declare your shared ViewModel:

private val viewModelShared: SharedViewModel by activityViewModels()


Related Topics



Leave a reply



Submit