Jetpack AAC series of articles

  • Android Jetpack AAC Lifecycle
  • Android Jetpack LiveData+ViewModel(2)

Android Jetpack is a series of utility tools for Android developers released by Google I/O in 2018 to help developers build great Android apps. The Android Jetpack component covers the following four aspects: Architecture, Foundation, Behavior, and UI.

Lifecycle is one of the official Architecture components of Architecture Compinents. Lifecycle will enable developers to feel the Fragment and activity Lifecycle in non-Fragments and activities, Developers can also focus more on the logic itself, and much of the glue code embedded in activities/fragments can be stripped away to avoid memory leaks due to not being aware of the lifecycle.

The introduction of Lifecycle

Android X version introduced

Here we use the Android X import approach

 api 'androidx. Appcompat: appcompat: 1.1.0'
Copy the code

Appcompat already has a lifecycle-common component built in by default, so we don’t need to reference it separately.

To specify Java8, add the following reference

 api 'androidx. Lifecycle: lifecycle - common - java8:2.1.0'
Copy the code

Introduction of the Support version

Note: Lifecycle is also introduced by default in the Support version, so add an annotation and compile

implementation "Com. Android. Support: appcompat - v7:28.0.0"
annotationProcessor "Android. Arch. Lifecycle: the compiler: 1.1.1"
// Java8 uses import
implementation "Android. Arch. Lifecycle: common - java8:1.1.1"
Copy the code

Lifecycle callback

Java7

public interface LifecycleObserverIml extends LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.On_CREATE)
    void onCreate(a);

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart(a);

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    void onResume(a);

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    void onPause(a);

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onStop(a);

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy(a);
}
Copy the code

Java8

public interface LifecycleObserverIml8 extends DefaultLifecycleObserver {

    @Override
    default void onCreate(@NonNull LifecycleOwner owner) {}@Override
    default void onStart(@NonNull LifecycleOwner owner) {}@Override
    default void onResume(@NonNull LifecycleOwner owner) {}@Override
    default void onPause(@NonNull LifecycleOwner owner) {}@Override
    default void onStop(@NonNull LifecycleOwner owner) {}@Override
    default void onDestroy(@NonNull LifecycleOwner owner) {}}Copy the code

Used in Activity/Frament

The binding

getLifecycle().addObserver(lifecycleObserver);
Copy the code

unbundling

getLifecycle().removeObserver(lifecycleObserver)
Copy the code

Is it both simple and practical to use? So the question is, if I want to use it in both the Fragment and the Activity, can IT be Glide, like what level of context is passed in and automatically bind the corresponding context-level life cycle?

The answer is yes! Let’s hand lift a bound helper class

public class LifecycleBindHelper {


    private LifecycleOwner lifecycleOwner;
    // Lifecycle callback
    private ArrayMap<Object, LifecycleObserver> lifecyleCallbacks = new ArrayMap<>();


    public static LifecycleBindHelper with(Object object) {
        return new LifecycleBindHelper(object);
    }

    public static LifecycleBindHelper with(AppCompatActivity object) {
        return new LifecycleBindHelper(object);
    }

    public static LifecycleBindHelper with(Fragment object) {
        return new LifecycleBindHelper(object);
    }


    private AppLifecycleBindHelper(Object object) {
        if(object ! =null && object instanceofLifecycleOwner) { lifecycleOwner = (LifecycleOwner) object; }}public LifecycleBindHelper addLifecyleCallback(LifecycleObserver lifecycleObserver) {
        if(hasRefrence() && lifecycleObserver ! =null) {
            lifecyleCallbacks.put(lifecycleObserver, lifecycleObserver);
            lifecycleOwner.getLifecycle().addObserver(lifecycleObserver);
        }
        return this;
    }

    public LifecycleBindHelper removeLifecyleCallback(LifecycleObserver lifecycleObserver) {
        if(hasRefrence() && lifecycleObserver ! =null) {
            lifecyleCallbacks.remove(lifecycleObserver);
            this.lifecycleOwner.getLifecycle().removeObserver(lifecycleObserver);
        }
        return this;
    }

    private LifecycleObserver[] getLifecycleCallbacks() {
        LifecycleObserver[] callbacks = new LifecycleObserver[lifecyleCallbacks.values().size()];
        lifecyleCallbacks.values().toArray(callbacks);
        return callbacks;
    }


    public boolean hasRefrence(a) {
        return null! =this.lifecycleOwner;
    }

    /** * Clear all callbacks (only callbacks added to the current object can be cleared; callbacks added in other ways can be manually removed) */
    public void clearAll(a) {
        if(! hasRefrence()) {return;
        }
        for (LifecycleObserver lifecycleObserver : getLifecycleCallbacks()) {
            removeLifecyleCallback(lifecycleObserver);
        }
        build();
    }

    /** * When the build is complete, there is no need to call */
    public void build(a) {
        lifecyleCallbacks.clear();
        this.lifecycleOwner = null; }}Copy the code

How can Lifecycle be used

In the increasingly popular MVP/MVVM development mode, we will split the original code logic in the Activity/Fragment into each module. The advantage is that each module is responsible for managing its own responsibilities, but the disadvantage is that there is almost no awareness of the life cycle. As a result, there is a lot of glue code, and each module logic is like a patch with the original The start page is tightly linked.

How do we enhance lifecycle awareness in the MVP development model, for example? Using the LifecycleBindHelper we just wrapped, we wrapped a BasePresenter layer for presenters.

class BasePresenter implements LifecycleObserverIml {
    private LifecycleBindHelper lifecycleBindHelper;

    private static final String TAG = "BasePresenter";

    private FragmentActivity mActivity;
    private Fragment mFragment;

    public BasePresenter(FragmentActivity context) {
        bindLifecycleOwner(context);
    }

    public BasePresenter(Fragment fragment) {
        bindLifecycleOwner(fragment);
    }

    private void bindLifecycleOwner(Object lifecycleOwner) {
        if(lifecycleBindHelper ! =null && lifecycleBindHelper.hasReference()) {
            lifecycleBindHelper.clearAll();
        }
        if (lifecycleOwner instanceof FragmentActivity) {
            this.mActivity = (FragmentActivity) lifecycleOwner;
        }
        if (lifecycleOwner instanceof Fragment) {
            this.mFragment = (Fragment) lifecycleOwner;
            this.mActivity = this.mFragment.getActivity();
        }
        lifecycleBindHelper = LifecycleBindHelper.with(lifecycleOwner).
                addLifecycleCallback(this);
    }

    public FragmentActivity getActivity(a) {
        return mActivity;
    }

    @Override
    public void onStart(a) {
        Log.e(TAG, "onStart: ");
    }

    @Override
    public void onResume(a) {
        Log.e(TAG, "onResume: ");
    }

    @Override
    public void onPause(a) {
        Log.e(TAG, "onPause: ");
    }

    @Override
    public void onStop(a) {
        Log.e(TAG, "onStop: ");
    }

    @Override
    public void onDestroy(a) {
        Log.e(TAG, "onDestroy: ");
        if(lifecycleBindHelper.hasReference()) { lifecycleBindHelper.clearAll(); }}}Copy the code

This allows us to be in a good position for the Presenter to handle life cycle issues

Use case 1: Login module

Suppose we have a login module, and we interact with the P layer to initiate a login request after clicking the login button in the Activity layer

public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
    LoginPresenter loginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loginPresenter = new LoginPresenter(MainActivity.this);
    }

    @Override
    public void onClick(View v) {
        loginPresenter.login("136xxxxxxxxx"."123456", () -> {
            Toast.makeText(MainActivity.this."Login successful",Toast.LENGTH_LONG).show(); }); }}Copy the code
public class LoginPresenter extends BasePresenter {
    boolean isDestroy;

    public LoginPresenter(FragmentActivity context) {
        super(context);
    }

    public void login(String phoneNumber, String code, Runnable callbackRunnable) {
        if (TextUtils.isEmpty(phoneNumber)) {
            Toast.makeText(getActivity(),
                    "Cell phone number cannot be empty.", Toast.LENGTH_LONG).show();
            return;
        }

        if (TextUtils.isEmpty(code)) {
            Toast.makeText(getActivity(),
                    "Verification code cannot be empty.", Toast.LENGTH_LONG).show();
            return;
        }
        // Simulate network request latency
        new Handler().postDelayed(() -> {
            if (isDestroy) {
                return;
            }
            callbackRunnable.run();
        }, 200);
    }

    @Override
    public void onDestroy(a) {
        super.onDestroy();
        isDestroy = true;
        // You can perform operations such as resource release at this time}}Copy the code

In P layer can be seen, because we are a network request is asynchronous, at the moment when we send the request, the user could shut down this page, if at this time we also operate the UI layer, is certainly will cause abnormal program or collapse, but we can perceive the situation of the life cycle can check whether the target object has been destroyed, and the processing need timely release of information We can also find the order in which the corresponding functions are executed by log

2020-06-01 11:44:22.633 2432-2432/com.waylenw.adr.mvvm E/BasePresenter: onStart: 
2020-06-01 11:44:22.634 2432-2432/com.waylenw.adr.mvvm E/BasePresenter: onResume: 
2020-06-01 11:44:33.758 2432-2432/com.waylenw.adr.mvvm E/BasePresenter: onPause: 
2020-06-01 11:44:33.795 2432-2432/com.waylenw.adr.mvvm E/BasePresenter: onStop: 
2020-06-01 11:54:42.890 5306-5306/com.waylenw.adr.mvvm E/BasePresenter: onDestroy: 
Copy the code

Use case 2: Player scenario

This is how lifecycle is usually used when lifecycle is not used

public class MainPlayerActivity extends AppCompatActivity {
    MediaPlayer mediaPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initPlayer();
    }

    public void initPlayer(a) {
        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource(Environment.getExternalStorageDirectory() + "/demo.mp4");
            mediaPlayer.prepare();
            mediaPlayer.start();
        } catch(IOException e) { e.printStackTrace(); }}@Override
    protected void onResume(a) {
        super.onResume();
        mediaPlayer.start();
    }

    @Override
    protected void onPause(a) {
        super.onPause();
        mediaPlayer.pause();
    }

    @Override
    protected void onDestroy(a) {
        super.onDestroy(); mediaPlayer.stop(); }}Copy the code

When the lifecycle of the Activity changes, we also need to change the state of the player. Imagine if we had two or more pages, would we have to do this?

Let’s see what happens with LifyCycle

public class MainPlayerNewActivity extends AppCompatActivity {
    PlayerManager playerManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initPlayer();
    }

    public void initPlayer(a) {
        playerManager = new PlayerManager(this);
        playerManager.setDataSource(
        Environment.getExternalStorageDirectory() + "/demo.mp4"); }}Copy the code
public class PlayerManager extends BasePresenter {
    MediaPlayer mediaPlayer;

    public PlayerManager(FragmentActivity context) {
        super(context);
        mediaPlayer = new MediaPlayer();
    }

    public void setDataSource(String path) {

        try {
            mediaPlayer.setDataSource(path);
            mediaPlayer.prepare();
        } catch(IOException e) { e.printStackTrace(); }}@Override
    public void onResume(a) {
        super.onResume();
        mediaPlayer.start();
    }

    @Override
    public void onPause(a) {
        super.onPause();
        mediaPlayer.pause();
    }

    @Override
    public void onDestroy(a) {
        super.onDestroy(); mediaPlayer.stop(); }}Copy the code

You can see how much less code is in the Activity, and the PlayerManager can be reused across pages without worrying about life cycle changes for external use.

Use case 3: Common scenario

EventBus is an essential part of Android development. Take the P layer as an example. Once we can register and unbind internally, we can do self-processing inside the P layer

public  class Login2Presenter extends BasePresenter {
   boolean isDestroy;

   public Login2Presenter(FragmentActivity context) {
       super(context);
       EventBus.getDefault().register(this);
   }
   @Override
   public void onDestroy(a) {
       super.onDestroy();
       EventBus.getDefault().unregister(this); }}Copy the code

There are many other processing scenarios like file download, network request, image load, etc. Lifecycle will really decouple the modules and allow them to focus more on the content of the module, making the bloated code much cleaner.

The source code parsing

Source code analysis is based on Android X

Bind observer

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {

    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }

    private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
    
    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        returnmLifecycleRegistry; }}Copy the code

From the above code we can see that we call getLifecycle() externally and actually get the LifecycleRegistry class object.

Public class LifecycleRegistry extends Lifecycle {// Omit the related code..... }Copy the code

LifecycleRegistry implements the Lifecycle interface, which provides adding/removing observer interface functions

public abstract class Lifecycle {

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @NonNull
    AtomicReference<Object> mInternalScopeRef = new AtomicReference<>();
    
    @MainThread
    public abstract void addObserver(@NonNull LifecycleObserver observer);
    
    @MainThread
    public abstract void removeObserver(@NonNull LifecycleObserver observer);
    
    @MainThread
    @NonNull
    public abstract State getCurrentState();
}
Copy the code

Inform observer

public class LifecycleRegistry extends Lifecycle {
// omit the related code.....

 public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        State next = getStateAfter(event);
        moveToState(next);
    }

    private void moveToState(State next) {
        if (mState == next) {
            return;
        }
        mState = next;
        if(mHandlingEvent || mAddingObserverCounter ! =0) {
            mNewEventOccurred = true;
            // we will figure out what to do on upper level.
            return;
        }
        mHandlingEvent = true;
        sync();
        mHandlingEvent = false;
    }
     // happens only on the top of stack (never in reentrance),
    // so it doesn't have to take in account parents
    private void sync(a) {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
            throw new IllegalStateException("LifecycleOwner of this LifecycleRegistry is already"
                    + "garbage collected. It is too late to change lifecycle state.");
        }
        while(! isSynced()) { mNewEventOccurred =false;
            // no need to check eldest for nullability, because isSynced does it for us.
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                backwardPass(lifecycleOwner);
            }
            Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            if(! mNewEventOccurred && newest ! =null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }
    // omit the related code.....
    
}
Copy the code

Through the source code, we can find that all events received by the observer are driven by the handleLifecycleEvent() function, and finally distributed to the forwardPass and backwardPass functions for distribution and synchronization operations.

ForwardPass () traverses the collection of observers, distributing events when the observer life cycle state is less than the current life cycle state

private void forwardPass(LifecycleOwner lifecycleOwner) {

    Iterator<Entry<LifecycleObserver, ObserverWithState>> ascendingIterator =
            mObserverMap.iteratorWithAdditions();
    while(ascendingIterator.hasNext() && ! mNewEventOccurred) { Entry<LifecycleObserver, ObserverWithState> entry = ascendingIterator.next(); ObserverWithState observer = entry.getValue();while ((observer.mState.compareTo(mState) < 0&&! mNewEventOccurred && mObserverMap.contains(entry.getKey()))) { pushParentState(observer.mState); observer.dispatchEvent(lifecycleOwner, upEvent(observer.mState)); popParentState(); }}}Copy the code

BackwardPass () traverses the collection of observers, distributing events when the observer lifecycle state is greater than the current lifecycle state

private void backwardPass(LifecycleOwner lifecycleOwner) {
    Iterator<Entry<LifecycleObserver, ObserverWithState>> descendingIterator =
            mObserverMap.descendingIterator();
    while(descendingIterator.hasNext() && ! mNewEventOccurred) { Entry<LifecycleObserver, ObserverWithState> entry = descendingIterator.next(); ObserverWithState observer = entry.getValue();while ((observer.mState.compareTo(mState) > 0&&! mNewEventOccurred && mObserverMap.contains(entry.getKey()))) { Event event = downEvent(observer.mState); pushParentState(getStateAfter(event)); observer.dispatchEvent(lifecycleOwner, event); popParentState(); }}}Copy the code

So the question is, where does the handleLifecycleEvent() function fire? Looking at the ComponentActivity source code, it seems to provide only a getLifecycle function.

After some searching, this line of code is found in the onCreate function in ComponentActivity

public class ComponentActivity extends Activity implements
        LifecycleOwner.KeyEventDispatcher.Component {
        
    @SuppressLint("RestrictedApi")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ReportFragment.injectIfNeededIn(this); }}Copy the code

Add ReportFragment to ComponentActivity

public class ReportFragment extends Fragment {
    private static final String REPORT_FRAGMENT_TAG = "androidx.lifecycle"
            + ".LifecycleDispatcher.report_fragment_tag";

    public static void injectIfNeededIn(Activity activity) {
         fragments for activities
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            // Hopefully, we are the first to make a transaction.manager.executePendingTransactions(); }}}Copy the code

Look down at the ReportFragment code and it becomes immediately obvious

public class ReportFragment extends Fragment {
    
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        dispatchCreate(mProcessListener);
        dispatch(Lifecycle.Event.ON_CREATE);
    }
    
    @Override
    public void onStart(a) {
        super.onStart();
        dispatchStart(mProcessListener);
        dispatch(Lifecycle.Event.ON_START);
    }

    @Override
    public void onResume(a) {
        super.onResume();
        dispatchResume(mProcessListener);
        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);
        // just want to be sure that we won't leak reference to an activity
        mProcessListener = null;
    }

    private void dispatch(Lifecycle.Event event) {
        Activity activity = getActivity();
        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); }}}Copy the code

LifecycleRegistry, as an implementation class for Lifecycle, is responsible for managing the observers we add and storing them in the collection of observers. The ReportFragment exists as a Lifecycle broker that notifies and synchronies LifecycleRegis when Lifecycle changes occur Try, and finally LifecycleRegistry notifies all observers. So we implement LifecycleObserver observers externally by getting LifecycleRegistry registration, and can easily sense changes to the lifecycle.

Finally, the association among LifecycleObserver, Lifecycle, LifecycleRegistry, LifecycleOwner, ComponentActivity, ReportFragment can be summarized as the following figure.

The Jetpack AAC architectural component family will be further analyzed. Think this article is helpful to you, you can click here to follow the public account, more practical content is being updated.