series

Back to Jetpack: Dependencies and transitive relationships of Jetpack’s major components

Changes to using activities and fragments under AdroidX

Can you really use a Fragment? Fragments FAQ and new postures for using fragments on androidx

AndroidX Fragment1.2.2 Source code analysis

Fragment returns the stack preparation section

Jetpack’s Fragment return stack is a Fragment return stack demo

Never lose the state of AndroidX SaveState ViewModel-SaveState analysis

Even if you don’t use MVVM, understand the ViewModel — the functional boundaries of the ViewModel

preface

There is an important concept in Android: “life cycle.” When you go to an interview right out of college, you’re often asked the “lifecycle of the four components” question. At IO 2017, Google launched Lifecycle Aware Components to help developers organize better, lighter, and easier to maintain code

This article will give you an insight into the responsibilities of Lifecycle and a brief analysis of how Lifecycle feels about activities and fragments

Everything is based on Lifecycle

You don’t understand the pain of managing the life cycle manually

Lu Xun once said: Everything is based on Lifecycle

Oh no

View controllers in Android have this many life cycles, so it’s important to get the life cycle right, otherwise it can lead to memory leaks and even crashes. Here is an example of official documentation

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start(a) {
        // Connect to the system location service
    }

    void stop(a) {
        // The system is disconnected from the location service}}class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...). {
        myLocationListener = new MyLocationListener(this, (location) -> {
            / / update the UI
        });
    }

    @Override
    public void onStart(a) {
        super.onStart();
        myLocationListener.start();
        // Manage other components that need to respond to the activity lifecycle
    }

    @Override
    public void onStop(a) {
        super.onStop();
        myLocationListener.stop();
        // Manage other components that need to respond to the activity lifecycle}}Copy the code

This example looks good; in a real application, you would still make too many calls to manage the UI and other components in response to the current state of the life cycle. Managing multiple components puts a lot of code in lifecycle methods, such as onStart() and onStop(), which makes them difficult to maintain

Also, there is no guarantee that the component will start before the activity or fragment stops. If we need an operation that takes a long time to run (such as some configuration check in onStart()), we may result in a race situation where the onStop() method completes before onStart(), making the component live longer than it needs to.

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...). {
        myLocationListener = new MyLocationListener(this, location -> {
            / / update the UI
        });
    }

    @Override
    public void onStart(a) {
        super.onStart();
        Util.checkUserStatus(result -> {
            // What if this callback is called after the activity has stopped?
            if(result) { myLocationListener.start(); }}); }@Override
    public void onStop(a) {
        super.onStop(); myLocationListener.stop(); }}Copy the code

It would be nice to have all the components aware of the external life cycle, able to release resources at the appropriate time, and stop asynchronous tasks when the life cycle is missed.

Let’s start by thinking about how to implement such an idea

Think conventionally

First, let’s sort out our requirements

  • Internal components are aware of external life cycles
  • Can be unified management, so that a modification, everywhere effective
  • Be able to call off missed tasks

For requirement 1, the observer pattern can be used so that internal components can respond as the external lifecycle changes

For requirement 2, component-dependent code can be moved out of the lifecycle methods and into the components themselves, so that only the internal logic of the components can be modified

For requirement 3, the observer can be removed at the appropriate time

Observer model

The first time I looked at the observer mode in detail was in the Drop line RxJava tutorial for Android developers.

The observer mode is oriented to the requirement that object A (observer) is highly sensitive to A certain change of object B (observed) and needs to respond at the moment when B changes. For example, in the news, the police catch a thief. The police need to catch the thief when he reaches for his hand. In this example, the policeman is the observer and the thief is the observed. The policeman needs to keep an eye on the thief’s every move to ensure that no moment is missed. The observer mode of the program is slightly different from this kind of real “observation”. The observer does not need to stare at the observed all the time (for example, A does not need to check the status of B every 2ms), but registers, or subscribes, to tell the observed: I need your status, and you need to let me know when it changes. A typical example of this in Android development is the OnClickListener. For OnClickListener, the View is the observed and OnClickListener is the observer, and the subscription relationship is achieved through the setOnClickListener() method. The moment the user clicks a button after subscribes, the Android Framework sends the click event to the registered OnClickListener. Adopting this passive observation mode not only saves the resource consumption of repeatedly retrieving the state, but also can get the highest feedback speed. Of course, this also benefits from the fact that we can customize the observer and observed in our own program, while the police obviously cannot ask the thief to “inform me when you commit a crime”.

The OnClickListener mode looks like this:

The above description and images are from the RxJava detail for Android developers

Therefore, the internal component is aware of the external lifecycle by telling the observer when the lifecycle of the lifecycle component changes

After introducing the Lifecycle

public class MyObserver implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void connectListener(a) {... }@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void disconnectListener(a) {... } } myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
Copy the code

Source structure

Lifecycle this is the structure of Lifecycle, an abstract class with two enumerations inside representing ‘events’ and’ states’ and three methods to add/remove observers and get the current state

Note that the enumeration order in State here makes sense, as described later

The implementation class is LifecycleRegistry and can handle multiple observers

It holds the current state, mState, LifecycleOwner, and a custom list of observers internally, overwriting the parent class’s methods for adding/removing observers

LifecycleOwner, which has the Android lifecycle, custom components can use these events to handle lifecycle changes without implementing any code in an Activity or Fragment

LifecycleObserver, which marks a class as LifecycleObserver. It doesn’t have any methods and instead relies on the methods of the OnLifecycleEvent annotation

LifecycleEventObserver, which can receive any lifecycle changes and dispatch them to recipients.

If a class implements this interface and also uses OnLifecycleEvent, annotations are ignored

DefaultLifecycleObserver, a callback interface for listening for LifecycleOwner state changes.

If a class implements both this interface and LifecycleEventObserver, the DefaultLifecycleObserver method is first called, Then call LifecycleEventObserver. OnStateChanged (LifecycleOwner, Lifecycle. The Event)

Note: Using DefaultLifecycleObserver needs to be introduced

implementation “androidx.lifecycle:lifecycle-common-java8:$lifecycle_version”

Simple source code analysis

Activity lifecycle processing

First of all, let’s look at androidx.activity.Com ponentActivity, this class we mentioned many times in this series of articles, The first mention is in [back Jetpack] never lost state androidX SaveState ViewModel-SaveState analysis, interested friends can look at.

Most of the interfaces implemented have already been covered, but today we’ll take a look at LifecycleOwner

ActivityResultCaller was introduced for Activity 1.2.0-Alpha02 to unify onActivityResult. It is not discussed here

Now that the LifecycleOwner interface is implemented, the getLifecycle() method must be overridden

// androidx.activity.ComponentActivity.java
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

@Override
public Lifecycle getLifecycle(a) {
    return mLifecycleRegistry;
}
Copy the code

Lifecycle is returned as an instance of the implementation class LifecycleRegistry

The Activity operation lifecycle is handled via ReportFragment

// androidx.activity.ComponentActivity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ReportFragment.injectIfNeededIn(this);
    / /...
}

// ReportFragment
public static void injectIfNeededIn(Activity activity) {
    if (Build.VERSION.SDK_INT >= 29) {
        // API 29 and above directly registers the correct lifecycle callbacks
        activity.registerActivityLifecycleCallbacks(
                new LifecycleCallbacks());
    }
    android.app.FragmentManager manager = activity.getFragmentManager();
    if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
        manager.beginTransaction().add(newReportFragment(), REPORT_FRAGMENT_TAG).commit(); manager.executePendingTransactions(); }}Copy the code

// ReportFragment.java
static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
    if (activity instanceof LifecycleRegistryOwner) {
        ((LifecycleRegistryOwner) activity).getLifecycle().handleLifecycleEvent(event);
        return;
    }
    if (activity instanceof LifecycleOwner) {
        Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
        if (lifecycle instanceofLifecycleRegistry) { ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event); }}}private void dispatch(@NonNull Lifecycle.Event event) {
    if (Build.VERSION.SDK_INT < 29) { dispatch(getActivity(), event); }}@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    dispatch(Lifecycle.Event.ON_CREATE);
}
@Override
public void onStart(a) {
    super.onStart();
    dispatch(Lifecycle.Event.ON_START);
}
@Override
public void onResume(a) {
    super.onResume();
    dispatch(Lifecycle.Event.ON_RESUME);
}
@Override
public void onPause(a) {
    super.onPause();
    dispatch(Lifecycle.Event.ON_PAUSE);
}
@Override
public void onStop(a) {
    super.onStop();
    dispatch(Lifecycle.Event.ON_STOP);
}
@Override
public void onDestroy(a) {
    super.onDestroy();
    dispatch(Lifecycle.Event.ON_DESTROY);
}
Copy the code
// LifecycleCallbacks
static class LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
    @Override
    public void onActivityPostCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        dispatch(activity, Lifecycle.Event.ON_CREATE);
    }

    @Override
    public void onActivityPostStarted(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_START);
    }

    @Override
    public void onActivityPostResumed(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_RESUME);
    }
    @Override
    public void onActivityPrePaused(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_PAUSE);
    }

    @Override
    public void onActivityPreStopped(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_STOP);
    }

    @Override
    public void onActivityPreDestroyed(@NonNull Activity activity) {
        dispatch(activity, Lifecycle.Event.ON_DESTROY);
    }
	/ /...
}
Copy the code

In the activity’s onCreate method, the static injectIfNeededIn() ReportFragment method is called. Internally, if the correct lifecycle callbacks are registered directly on devices with API 29 and above, the lower version handles the lifecycle callbacks by starting ReportFragment with the fragment lifecycle

Fragment life cycle processing

Inside the Fragment, each lifecycle node calls the handleLifecycleEvent method

// Fragment.java
public Fragment(a) {
    initLifecycle();
}

private void initLifecycle(a) {
    mLifecycleRegistry = new LifecycleRegistry(this);
}

@Override
public Lifecycle getLifecycle(a) {
    return mLifecycleRegistry;
}

void performCreate(Bundle savedInstanceState) {
    onCreate(savedInstanceState);
	mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);    
}

void performStart(a) {
    onStart();
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}

void performResume(a) {
    onResume();
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);    
}

void performPause(a) {
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
    onPause();
}

void performStop(a) {
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
    onStop();
}

void performDestroy(a) {
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    onDestroy();
}
Copy the code

Lifecycle State size comparison

Lifecycle.State has an isAtLeast method that determines whether the current State is not less than the incoming State

// Lifecycle.State
public boolean isAtLeast(@NonNull State state) {
    return compareTo(state) >= 0;
}
Copy the code

The compareTo method of enumerations actually compares the order of enumeration declarations

The order of State is DESTROYED -> INITIALIZED -> CREATED -> STARTED -> RESUMED

If the state passed in is STARTED, return true if the current state is STARTED or RESUMED, or false otherwise

The LiveData article will use this knowledge

About me

I am a Fly_with24

  • The Denver nuggets
  • Jane’s book
  • Github