• Android Fragments: Fragment Result
  • The original address: proandroiddev.com/android-fra…
  • Husayn Hakeem

Now in Android #17 will release new features for Android, including new ways for fragments to communicate with each other. Click here to check out the updates

Through this passage you will learn the following, which will be answered in the thinking section of the translator

  • Use of new methods of communication between fragments?
  • Source code analysis of new Fragment communication?
  • How to summarize communication between fragments?

The translation

Frrgament can transfer data between a number of ways, including the use of the target Fragment APIs (fragments. SetTargetFragment () and fragments getTargetFragment ()), The target Fragment APIs are obsolete. Now we encourage the use of the new Fragment Result APIs to transfer data between Frrgament. Where passing data is handled by the FragmentManager, and Fragments are set to send and receive data

Pass data between Frrgament

Using the new Fragment APIs for passing between two Frrgament without any references, you can use their common FragmentManager, which acts as a central store for passing data between Frrgament.

Receive data

If you want to receive data in your Fragment, you can register a FragmentResultListener in the FragmentManager. RequestKey filters data sent by the FragmentManager

FragmentManager.setFragmentResultListener(
    requestKey,
    lifecycleOwner,
    FragmentResultListener { requestKey: String, result: Bundle ->
        // Handle result
    })
Copy the code

The lifecycleOwner parameter observes the Fragment lifecycle and accepts data when the Fragment lifecycle is STARTED. If you listen for the Fragment life cycle, you can safely update the UI when new data is received, because the view is created (the onViewCreated() method is called before onStart()).

When the life cycle is in the LifecycleOwner STARTED state, only the latest value will be received if more than one data is passed

When life cycle in LifecycleOwner DESTROYED, it will automatically remove the listener, if you want to manually remove the listener, you need to call FragmentManager. SetFragmentResultListener () method, Pass an empty FragmentResultListener

Register a listener in the FragmentManager, depending on the data the Fragment sends back

  • If you accept data from a FragmentB in a FragmentA, and the FragmentA and FragmentB are at the same level, communicating via the parent FragmentManager, FragmentA must register the listener with parent FragmentManager
parentFragmentManager.setFragmentResultListener(...)
Copy the code
  • If you accept data sent by FragmentB in FragmentA, the FragmentA is the parent container of FragmentB, and they communicate through the Child FragmentManager
childFragmentManager.setFragmentResultListener(...)
Copy the code

Listener FragmentManager of the same Fragment that must be set

To send data

If FragmentB sends data to FragmentA, register a listener in FragmentA and send the data through the parent FragmentManager

parentFragmentManager.setFragmentResult(
    requestKey, // Same request key FragmentA used to register its listener
    bundleOf(key to value) // The data to be passed to FragmentA
)
Copy the code

Test fragments Results

To test whether the Fragment successfully receives or sends data, use the FragmentScenario API

Receive data

If you register a FragmentResultListener in FragmentA to receive data, you can emulate the parent FragmentManager to send data. If you register a Listener correctly in FragmentA, You can use it to verify that FragmentA receives data, for example, if you accept data in FragmentA and update the UI, you can use the Espresso APIs to verify that the expected data is available

@Test
fun shouldReceiveData() {
    val scenario = FragmentScenario.launchInContainer(FragmentA::class.java)

    // Pass data using the parent fragment manager
    scenario.onFragment { fragment ->
        val data = bundleOf(KEY_DATA to "value")
        fragment.parentFragmentManager.setFragmentResult("aKey", data)
    }

    // Verify data is received, for example, by verifying it's been displayed on the UI onView(withId(R.id.textView)).check(matches(withText("value"))) }Copy the code

To send data

You can register a FragmentResultListener on the Parent FragmentManager of FragmentB to test whether the FragmentB has successfully sent data. You can verify that the listener can receive data

@Test
fun shouldSendData() {
    val scenario = FragmentScenario.launchInContainer(FragmentB::class.java)

    // Register result listener
    var receivedData = ""scenario.onFragment { fragment -> fragment.parentFragmentManager.setFragmentResultListener( KEY, fragment, FragmentResultListener { key, result -> receivedData = result.getString(KEY_DATA) }) } // Send data onView(withId(R.id.send_data)).perform(click()) //  Verify data was successfully sent assertThat(receivedData).isEqualTo("value")}Copy the code

conclusion

While using the Fragment Result APIs to replace the outdated Fragment Target APIs, the new APIs have some limitations when it comes to delivering bundles as data. Only simple data types, Serializable, and Parcelable data can be passed. The Fragment Result APIs allow applications to recover data from crashes without holding references to the other party, avoiding unknown problems when the Fragment is in an unpredictable state

Translator’s Thinking

Fragment 1.3.0-Alpha04 added API for communication between Fragments

Data to accept

FragmentManager.setFragmentResultListener(
    requestKey,
    lifecycleOwner,
    FragmentResultListener { requestKey: String, result: Bundle ->
        // Handle result
    })
Copy the code

Data sent

parentFragmentManager.setFragmentResult(
    requestKey, // Same request key FragmentA used to register its listener
    bundleOf(key to value) // The data to be passed to FragmentA
)
Copy the code

So what are the benefits of the new API for Fragment communication?

  • Passing data between fragments does not hold references to each other
  • Start processing data when the life cycle is ON_START to avoid unknown problems that can occur when the Fragment is in an unpredictable state
  • When the life cycle is ON_DESTROY, remove the listener

Let’s take a look at what Google is doing from a source code perspective

Source code analysis

According to the practice according to the analysis of the method called data receive, call the FragmentManager androidx setFragmentResultListener method. The fragments/fragments / 1.3.0 – alpha04… androidx/fragment/app/FragmentManager.java

private final ConcurrentHashMap<String, LifecycleAwareResultListener> mResultListeners =
        new ConcurrentHashMap<>();

@Override
public final void setFragmentResultListener(@NonNull final String requestKey, @NonNull final LifecycleOwner lifecycleOwner, Nullable final FragmentResultListener Listener) {// mResultListeners are instances of ConcurrentHashMap If the listener passed is empty, remove the listener corresponding to the requestKeyif (listener == null) {
        mResultListeners.remove(requestKey);
        return; } / / Lifecycle is a cognitive component life cycle and are commonly used to response the Activity, the life cycle of the components such as fragments change final Lifecycle Lifecycle. = lifecycleOwner getLifecycle (); // Avoid unknown problems that may occur when the Fragment is in an unexpected stateif (lifecycle.getCurrentState() == Lifecycle.State.DESTROYED) {
        return; } // Start listening LifecycleEventObserver Observer = newLifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source, @nonnull Lifecycle.Event Event) {// Data is processed when the Lifecycle is ON_STARTif(event == Lifecycle.event.on_start) {// Start to check the received data Bundle storedResult = mResults.if(storedResult ! = null) {/ / if the result is null, the callback method listener. OnFragmentResult (requestKey storedResult); // Clear datasetFragmentResult(requestKey, null); }} // Remove the listener when the lifecycle is ON_DESTROYif(event == Lifecycle.Event.ON_DESTROY) { lifecycle.removeObserver(this); mResultListeners.remove(requestKey); }}}; lifecycle.addObserver(observer); mResultListeners.put(requestKey, new FragmentManager.LifecycleAwareResultListener(lifecycle, listener)); }Copy the code
  • Lifecycle is a life-cycle aware component that responds to changes in the Lifecycle of components such as activities and fragments
  • Lifecycle listens for changes in the Fragment’s Lifecycle
  • Start processing data when the life cycle is ON_START to avoid unknown problems that can occur when the Fragment is in an unpredictable state
  • When the life cycle is ON_DESTROY, remove the listener

Then look at the data sending method together, call the FragmentManager androidx setFragmentResult method. The fragments/fragments / 1.3.0 – alpha04… androidx/fragment/app/FragmentManager.java

private final ConcurrentHashMap<String, Bundle> mResults = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, LifecycleAwareResultListener> mResultListeners =
        new ConcurrentHashMap<>();
    
@Override
public final void setFragmentResult(@NonNull String requestKey, @Nullable Bundle result) {
    if(result == null) {// mResults is an instance of ConcurrentHashMap, the Bundle used to store data transfers. Remove Bundle mresults.remove (requestKey);return;
    }

    // Check if there is a listener waiting forA result with this key // mResultListeners are examples of ConcurrentHashMap Used to store the registered listener / / obtain corresponding listener requestKey LifecycleAwareResultListener resultListener = mResultListeners.get(requestKey);if(resultListener ! = null && resultListener. IsAtLeast (Lifecycle.state.started)) {// If the resultListener is not empty and the Lifecycle is STARTED, The callback resultListener. OnFragmentResult (requestKey, result); }else{// Otherwise save the data currently transferred mresults.put (requestKey, result); }}Copy the code
  • Gets the listener registered with requestKey
  • When the life cycle is STARTED, data is sent
  • Otherwise, the data currently transferred is saved

Source code analysis is over here, let’s think about it, before we have those data transfer methods

Summarize how fragments communicate with each other

  • This is done by sharing viewmodels or associating activities. Fragments should not communicate directly with each other. See Google: ViewModel#sharing
  • With interfaces, you can define interfaces in fragments and implement them in activities that refer to Google: communicate with other fragments
  • Get an instance of a Fragment by using the findFragmentById method, and then call the Fragment’s public method reference Google: Communicate with other fragments
  • Call fragments. SetTargetFragment () and fragments. GetTargetFragment () method, but pay attention to the target fragments need direct access to another Fragment instances, this is very dangerous, Because you don’t know what state the target fragment is in
  • Fragments of the new API, setFragmentResult () and setFragmentResultListener ()

Considering the above communication methods, what do you think is the best way to communicate between fragments?

reference

  • Now in Android #17: https://medium.com/androiddeve……
  • Pass data between fragments: https://developer.android.com/training/basi……
  • ViewModel#sharing: https://developer.android.com/topic/librari……
  • Communicate with other fragments: https://developer.android.com/training/basic…

conclusion

Committed to sharing a series of Android system source code, reverse analysis, algorithm, translation related articles, is currently translating a series of European and American selected articles, not only translation, but also behind the translation of each article thinking, if you like this article, please help me a like, thank you, looking forward to growing with you

Plan to establish a most complete and latest AndroidX Jetpack related components of the actual combat project and related components of the principle of analysis articles, currently has included App Startup, Paging3, Hilt and so on, is gradually adding other Jetpack new members, the warehouse continues to update, Check it out: AndroidX-Jetpack-Practice, please give me a thumbs up if this repository is helpful to you, and I will continue to complete more project practices for new members of Jetpack.

algorithm

Since LeetCode has a large question bank, hundreds of questions can be selected for each category. Due to the limited energy of each person, it is impossible to brush all the questions. Therefore, I sorted the questions according to the classic types and the difficulty of the questions

  • Data structures: arrays, stacks, queues, strings, linked lists, trees…
  • Algorithms: Search algorithm, search algorithm, bit operation, sorting, mathematics,…

Each problem will be implemented in Java and Kotlin, and each problem has a solution idea. If you like algorithms and LeetCode like me, you can pay attention to my Solution of LeetCode problem on GitHub: Leetcode-Solutions-with-Java-And-Kotlin, come to learn together And look forward to growing with you

Android 10 source code series

I’m writing a series of Android 10 source code analysis articles. Knowing the system source code is not only helpful in analyzing problems, but also very helpful in the interview process. If you like to study Android source code as MUCH as I do, Go to android10-source-Analysis on my GitHub and all articles will be synchronized to this repository

  • How is APK generated
  • APK installation process
  • 0xA03 Android 10 source code analysis: APK loading process of resource loading
  • Android 10 source code: APK
  • Dialog loading and drawing process and use in Kotlin, DataBinding
  • WindowManager View binding and architecture
  • More and more

Tool series

  • Shortcuts to AndroidStudio that few people know
  • Shortcuts to AndroidStudio that few people know
  • All you need to know about ADB commands
  • How to get video screenshots efficiently
  • 10 minutes introduction to Shell scripting
  • How to package Kotlin + Android Databinding in your project

The reverse series

  • Dynamically debug APP based on Smali file Android Studio
  • The Android Device Monitor tool cannot be found in Android Studio 3.2