Merge "Fix memory leak with RenderNodeAnimator" into tm-dev

This commit is contained in:
Nader Jawad
2022-05-11 16:49:16 +00:00
committed by Android (Google) Code Review
6 changed files with 90 additions and 5 deletions

View File

@@ -21170,6 +21170,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId());
if (mBackgroundRenderNode != null) {
mBackgroundRenderNode.forceEndAnimators();
}
mRenderNode.forceEndAnimators();
}
private void cleanupDraw() {

View File

@@ -19,17 +19,24 @@ package android.view;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.widget.FrameLayout;
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@MediumTest
public class RenderNodeAnimatorTest {
@Rule
@@ -57,4 +64,46 @@ public class RenderNodeAnimatorTest {
anim.start(); // should initialize mTransformationInfo
assertNotNull(view.mTransformationInfo);
}
@Test
public void testViewDetachCancelsRenderNodeAnimator() {
// Start a RenderNodeAnimator with a long duration time, then detach the target view
// before the animation completes. Detaching of a View from a window should force cancel all
// RenderNodeAnimators
CountDownLatch latch = new CountDownLatch(1);
FrameLayout container = new FrameLayout(getContext());
View view = new View(getContext());
getActivity().runOnUiThread(() -> {
container.addView(view, new FrameLayout.LayoutParams(100, 100));
getActivity().setContentView(container);
});
getActivity().runOnUiThread(() -> {
RenderNodeAnimator anim = new RenderNodeAnimator(0, 0, 10f, 30f);
anim.setDuration(10000);
anim.setTarget(view);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
latch.countDown();
}
});
anim.start();
});
getActivity().runOnUiThread(()-> {
container.removeView(view);
});
try {
Assert.assertTrue("onAnimationEnd not invoked",
latch.await(3000, TimeUnit.MILLISECONDS));
} catch (InterruptedException excep) {
Assert.fail("Interrupted waiting for onAnimationEnd callback");
}
}
}

View File

@@ -1611,6 +1611,11 @@ public final class RenderNode {
nEndAllAnimators(mNativeRenderNode);
}
/** @hide */
public void forceEndAnimators() {
nForceEndAnimators(mNativeRenderNode);
}
///////////////////////////////////////////////////////////////////////////
// Regular JNI methods
///////////////////////////////////////////////////////////////////////////
@@ -1633,6 +1638,8 @@ public final class RenderNode {
private static native void nEndAllAnimators(long renderNode);
private static native void nForceEndAnimators(long renderNode);
///////////////////////////////////////////////////////////////////////////
// @CriticalNative methods
///////////////////////////////////////////////////////////////////////////

View File

@@ -31,7 +31,8 @@ static void detach(sp<BaseRenderNodeAnimator>& animator) {
animator->detach();
}
AnimatorManager::AnimatorManager(RenderNode& parent) : mParent(parent), mAnimationHandle(nullptr) {}
AnimatorManager::AnimatorManager(RenderNode& parent)
: mParent(parent), mAnimationHandle(nullptr), mCancelAllAnimators(false) {}
AnimatorManager::~AnimatorManager() {
for_each(mNewAnimators.begin(), mNewAnimators.end(), detach);
@@ -82,8 +83,16 @@ void AnimatorManager::pushStaging() {
}
mNewAnimators.clear();
}
for (auto& animator : mAnimators) {
animator->pushStaging(mAnimationHandle->context());
if (mCancelAllAnimators) {
for (auto& animator : mAnimators) {
animator->forceEndNow(mAnimationHandle->context());
}
mCancelAllAnimators = false;
} else {
for (auto& animator : mAnimators) {
animator->pushStaging(mAnimationHandle->context());
}
}
}
@@ -184,5 +193,9 @@ void AnimatorManager::endAllActiveAnimators() {
mAnimationHandle->release();
}
void AnimatorManager::forceEndAnimators() {
mCancelAllAnimators = true;
}
} /* namespace uirenderer */
} /* namespace android */

View File

@@ -16,11 +16,11 @@
#ifndef ANIMATORMANAGER_H
#define ANIMATORMANAGER_H
#include <vector>
#include <cutils/compiler.h>
#include <utils/StrongPointer.h>
#include <vector>
#include "utils/Macros.h"
namespace android {
@@ -56,6 +56,8 @@ public:
// Hard-ends all animators. May only be called on the UI thread.
void endAllStagingAnimators();
void forceEndAnimators();
// Hard-ends all animators that have been pushed. Used for cleanup if
// the ActivityContext is being destroyed
void endAllActiveAnimators();
@@ -71,6 +73,8 @@ private:
// To improve the efficiency of resizing & removing from the vector
std::vector<sp<BaseRenderNodeAnimator> > mNewAnimators;
std::vector<sp<BaseRenderNodeAnimator> > mAnimators;
bool mCancelAllAnimators;
};
} /* namespace uirenderer */

View File

@@ -543,6 +543,12 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
renderNode->animators().endAllStagingAnimators();
}
static void android_view_RenderNode_forceEndAnimators(JNIEnv* env, jobject clazz,
jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->animators().forceEndAnimators();
}
// ----------------------------------------------------------------------------
// SurfaceView position callback
// ----------------------------------------------------------------------------
@@ -745,6 +751,7 @@ static const JNINativeMethod gMethods[] = {
{"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
{"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
{"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
{"nForceEndAnimators", "(J)V", (void*)android_view_RenderNode_forceEndAnimators},
{"nRequestPositionUpdates", "(JLjava/lang/ref/WeakReference;)V",
(void*)android_view_RenderNode_requestPositionUpdates},