Historical issues

I believe that many students who have done a lot of business development have encountered the memory leakage problem of Android applications. Although most of the leakage is caused by our own food, in fact, system services may also have memory leakage. After all, the code is written by humans, and AOSP is not flawless.

Speaking of system services, we used to see leaks like this when processing text input:

InputMethodManager (IMM) instances hold views, which in turn hold references to the Activity, resulting in a Memory Leak after the Activity exits. We know that the system service life cycle is generally longer than an Activity.

Here you can check the old version of AOSP source code (branch: Android-9.0.0-r8) to obtain evidence:

public final class InputMethodManager {.../** * This is the root view of the overall window that currently has input * method focus. */
    View mCurRootView;
    /** * This is the view that should currently be served by an input method, * regardless of the state of setting that up. */
    View mServedView;
    /** * This is then next view that will be served by the input method, when * we get around to updating things. */View mNextServedView; ./**
     * When the focused window is dismissed, this method is called to finish the
     * input method started before.
     * @hide* /
    public void windowDismissed(IBinder appWindowToken) {...synchronized (mH) {
            if(mServedView ! =null&& mServedView.getWindowToken() == appWindowToken) { finishInputLocked(); }}}.../** * Disconnect any existing input connection, clearing the served view. */
    void finishInputLocked(a) {
        mNextServedView = null;
        if(mServedView ! =null) {... mServedView =null; . }}.../**
     * Call this when a view is being detached from a {@link android.view.Window}.
     * @hide* /
    public void onViewDetachedFromWindow(View view) {
        synchronized (mH) {
            ...
            if (mServedView == view) {
                mNextServedView = null; . }}}... }Copy the code

We can search the source code and find that although both mServedView and mNextServedView are empty at the right time, the most critical input focus View, namely mCurRootView, is not empty, which is also the main cause of leakage. Especially in the ListView (ListView, RecyclerView, etc.) if there is input box in itemView, it is especially easy to produce leakage problem.

The usual solution was to reflect the IMM instance and force the View objects to be empty, which I won’t go into here.

Times have changed, my Lord

I looked at the source code for large versions of AOSP in recent years and was surprised to find that the memory leak was fixed in the IMM of Android 10. To my surprise, this repair was the patch contributed by MIUI engineers.

This fix was submitted in the second half of 2018 and was finally implemented in Android 10. The following code is based on the branch Android-10.0.0_r30:

In other words, the IMM memory leak problem in Android 9 and before was not officially fixed in time, and it was the engineers from the domestic manufacturers who could not help but fix it (I also mentioned this bug to the system team when I was in MIUI).

Out of curiosity, I checked the patch submission information:

Read the description, exactly, to fix an IMM memory leak that has not been solved for years. I don’t know how long it’s been a headache for developers around the world (after all, Memory Leak is also a project quality metric, so it affects your performance/head).

InputMethodManager#sInstance#mCurRootView cause memory leak, which was finally verified by the official AOSP team:

Further optimization

Although this has been fixed by MIUI, there are still some structural issues with the IMM code, which may lead to other bugs. The official team has further optimized the IMM source code for Android 11, and this time the changes are not small:

Here I do a brief introduction, you can view the latest source code interested. We can see that in the latest IMM, the latter two views have been removed from it:

public final class InputMethodManager {
    /** * This is the root view of the overall window that currently has input * method focus. */
    @GuardedBy("mH")ViewRootImpl mCurRootView; . }Copy the code

All that is left is the ViewrotimPL object of the mCurRootView. Many of the McUrrootview-related actions in IMM are encapsulated in a new ImeFocusController class:

public final class ImeFocusController {...private final ViewRootImpl mViewRootImpl;
    private boolean mHasImeFocus = false;
    private View mServedView;
    private View mNextServedView;

    @UiThread
    ImeFocusController(@NonNull ViewRootImpl viewRootImpl) {
        mViewRootImpl = viewRootImpl;
    }

    private InputMethodManagerDelegate getImmDelegate(a) {
        return mViewRootImpl.mContext.getSystemService(InputMethodManager.class).getDelegate();
    }
    
    @UiThread
    void onViewDetachedFromWindow(View view) {...if (mServedView == view) {
            mNextServedView = null; mViewRootImpl.dispatchCheckFocus(); }}@UiThread
    void onWindowDismissed(a) {...if(mServedView ! =null) {
            getImmDelegate().finishInput();
        }
        getImmDelegate().setCurrentRootView(null);
        mHasImeFocus = false; }...public View getServedView(a) {
        return mServedView;
    }

    public View getNextServedView(a) {
        return mNextServedView;
    }

    public void setServedView(View view) {
        mServedView = view;
    }

    public void setNextServedView(View view) { mNextServedView = view; }}Copy the code

And you can see that basically all of the empty operations that we’ve done have been put into this Controller. MServedView and mNextServedView are no longer members of IMM, but members of ImeFocusController, And ImeFocusController is a member of ViewRootImpl (this Controller is instantiated in the constructor of ViewRootImpl).

The optimization of patch removes View’s dependence on IMM to a certain extent, and effectively decouples the code. The logic associated with input focus processing is moved to the View itself, further avoiding memory leaks.

The latter

In fact, every time I find some problem solving information, will pay attention to the time of the post, I found that the IMM memory leakage problem is basically before 2019, curious to see the latest source code found that it was fixed. Android system is still in the direction of more and more stable, more and more excellent performance.

AOSP’s Code Review platform can also be found that, in fact, all major mobile phone manufacturers at home and abroad have made great contributions to AOSP. They are not just immersed in their own customization, and they will always repair bugs. Thanks again open source!

If you are interested, you can browse, you will see that the owner of many changes is not Google:

  • android-review.googlesource.com/q/xiaomi
  • android-review.googlesource.com/q/huawei
  • android-review.googlesource.com/q/oppo
  • android-review.googlesource.com/q/samsung
  • android-review.googlesource.com/q/sony