Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory leaks in BlockingBlurController #89

Closed
alaegin opened this issue Mar 6, 2019 · 13 comments
Closed

Memory leaks in BlockingBlurController #89

alaegin opened this issue Mar 6, 2019 · 13 comments

Comments

@alaegin
Copy link

alaegin commented Mar 6, 2019

Hello, @Dimezis

I'm using your librray in such way: Activity -> Fragment -> RecyclerView Item with blurred background.
So, when I replace fragment with recyclerview and blured items to another one I'm getting several leaks detected by LeakCanary.

screenshot #1
screenshot #2
screenshot #3

In my (and LeakCanary's) opinion the leak was caused by anonymous class in BlockingBlurController#deferBitmapCreation (read here about it)

I tried to remove it at all and as result no leaks detected and lower memory consumption achieved. So I can suggest to make that class static.

private static class SizeHandler implements ViewTreeObserver.OnGlobalLayoutListener {

        private WeakReference<View> blurView;
        private SizeHandlerListener listener;

        SizeHandler(View blurView, SizeHandlerListener listener) {
            this.blurView = new WeakReference<>(blurView);
            this.listener = listener;
        }

        @Override
        public void onGlobalLayout() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                blurView.get().getViewTreeObserver().removeOnGlobalLayoutListener(this);
            } else {
                legacyRemoveOnGlobalLayoutListener();
            }

            int measuredWidth = blurView.get().getMeasuredWidth();
            int measuredHeight = blurView.get().getMeasuredHeight();

            listener.onSizeClarified(measuredWidth, measuredHeight);
        }

        @SuppressWarnings("deprecation")
        void legacyRemoveOnGlobalLayoutListener() {
            blurView.get().getViewTreeObserver().removeGlobalOnLayoutListener(this);
        }
    }

    private interface SizeHandlerListener {
        void onSizeClarified(int measuredWidth, int measuredHeight);
    }

And call it from function like it:

private void deferBitmapCreation() {
     new SizeHandler(blurView, this::init);
}

This solution fixed memory leak for me.

Regards,
Alexey Egin.

@Dimezis
Copy link
Owner

Dimezis commented Mar 6, 2019

Hm, that's weird. I'm removing this listener straight away after onGlobalLayout.
There can be no leak, unless onGlobalLayout is not called at all.
Could you debug if it's called in your case?

@alaegin
Copy link
Author

alaegin commented Mar 6, 2019

@Dimezis No, that method not called at all (I could remove it w/o any negative effect)

@Dimezis
Copy link
Owner

Dimezis commented Mar 6, 2019

Weird stuff. How does BlurView initialize the bitmap in this case?
Also what's your device and OS?

@alaegin
Copy link
Author

alaegin commented Mar 6, 2019

As I can see - deferBitmapCreation called only when measuredWidth and measuredHeight give us a zero sized downscaled image, probably in my case its not getting zero sized image (Correct if i'm wrong).

if (isZeroSized(measuredWidth, measuredHeight)) {
    deferBitmapCreation();
    return;
}

OS: Android 8.1
Device: OnePlus 5

@Dimezis
Copy link
Owner

Dimezis commented Mar 6, 2019

But if it wasn't called, there would be no leak :)
Could you please debug why is this exactly happening?

Something similar to your approach would work, but I don't like using similar WeakReference-based solutions, because we have a legal way of avoiding a leak by removing a listener.
So we just need to make sure it's always properly called.

@alaegin
Copy link
Author

alaegin commented Mar 6, 2019

I'm sorry: I missed some important thing.

It turned out that the deferBitmapCreation method called only on first few view bindings in RecyclerView Adapter. After that adapter reuses previously inflated views as it should be, without initializing new ones. Following logic we've initialized that views and listeners were removed - no leak shoud occur, but it occurs, although no new BlurController were created.

So far I can not understand why there is a leak...

@Dimezis
Copy link
Owner

Dimezis commented Apr 11, 2019

Btw are you sure you don't have leaks anymore? Because essentially you're leaking it again by doing this::init.
Also, please don't ever use weak ref like this blurView.get().getViewTreeObserver().
get() can return null any time, so you have to make a check

@masc3d
Copy link
Contributor

masc3d commented May 21, 2019

the massive memory leak Im experiencing in BlockingBlurController recently is actually caused by posting in setBlurAutoUpdate, which is called when blur view is detached and essential for removing the tree observer.

since post is never really reliable, least during view destruction, it will lead to tree observer usually not unregistering, leaking the controller and entire view hierarchy of the root view with it.

I don't know why it was introduced, but removing all posts in controller works fine and resolves leakage for me.

@masc3d
Copy link
Contributor

masc3d commented May 21, 2019

In my (and LeakCanary's) opinion the leak was caused by anonymous class in BlockingBlurController#deferBitmapCreation (read here about it)

leak canary may report this as it may be GC'ed lazily but not every anonymous class is automatically an issue, despite leak canary (temporarily) reporting it.

I wouldn't see that ViewTreeObserver.OnGlobalLayoutListener is a real problem here as long as it's unregistered reliably at some point.

@masc3d
Copy link
Contributor

masc3d commented May 21, 2019

@DominuS-RU could you check if you still see leakage with patch com.github.masc3d:blurview:17c1cdc48c

it requires repositories { maven { url 'https://jitpack.io' } }

@alexandrius
Copy link

@DominuS-RU @masc3d Can confirm the patched version above doesn't have the leak.

@masc3d
Copy link
Contributor

masc3d commented Oct 8, 2019

@Dimezis do you prefer pull request for this?

@Dimezis
Copy link
Owner

Dimezis commented Oct 8, 2019

@masc3d would really appreciate it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants