Merge "Fix memory leak with RenderNodeAnimator" into tm-dev am: b9a8ed8b70
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18290468 Change-Id: I8aeb4d884f070fca4105957668ec07ab2d02f63b Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -21170,6 +21170,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId());
|
AccessibilityNodeIdManager.getInstance().unregisterViewWithId(getAccessibilityViewId());
|
||||||
|
|
||||||
|
if (mBackgroundRenderNode != null) {
|
||||||
|
mBackgroundRenderNode.forceEndAnimators();
|
||||||
|
}
|
||||||
|
mRenderNode.forceEndAnimators();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanupDraw() {
|
private void cleanupDraw() {
|
||||||
|
|||||||
@@ -19,17 +19,24 @@ package android.view;
|
|||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
import androidx.test.annotation.UiThreadTest;
|
import androidx.test.annotation.UiThreadTest;
|
||||||
import androidx.test.filters.MediumTest;
|
import androidx.test.filters.MediumTest;
|
||||||
import androidx.test.rule.ActivityTestRule;
|
import androidx.test.rule.ActivityTestRule;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class RenderNodeAnimatorTest {
|
public class RenderNodeAnimatorTest {
|
||||||
@Rule
|
@Rule
|
||||||
@@ -57,4 +64,46 @@ public class RenderNodeAnimatorTest {
|
|||||||
anim.start(); // should initialize mTransformationInfo
|
anim.start(); // should initialize mTransformationInfo
|
||||||
assertNotNull(view.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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1611,6 +1611,11 @@ public final class RenderNode {
|
|||||||
nEndAllAnimators(mNativeRenderNode);
|
nEndAllAnimators(mNativeRenderNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public void forceEndAnimators() {
|
||||||
|
nForceEndAnimators(mNativeRenderNode);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Regular JNI methods
|
// Regular JNI methods
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@@ -1633,6 +1638,8 @@ public final class RenderNode {
|
|||||||
|
|
||||||
private static native void nEndAllAnimators(long renderNode);
|
private static native void nEndAllAnimators(long renderNode);
|
||||||
|
|
||||||
|
private static native void nForceEndAnimators(long renderNode);
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// @CriticalNative methods
|
// @CriticalNative methods
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ static void detach(sp<BaseRenderNodeAnimator>& animator) {
|
|||||||
animator->detach();
|
animator->detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimatorManager::AnimatorManager(RenderNode& parent) : mParent(parent), mAnimationHandle(nullptr) {}
|
AnimatorManager::AnimatorManager(RenderNode& parent)
|
||||||
|
: mParent(parent), mAnimationHandle(nullptr), mCancelAllAnimators(false) {}
|
||||||
|
|
||||||
AnimatorManager::~AnimatorManager() {
|
AnimatorManager::~AnimatorManager() {
|
||||||
for_each(mNewAnimators.begin(), mNewAnimators.end(), detach);
|
for_each(mNewAnimators.begin(), mNewAnimators.end(), detach);
|
||||||
@@ -82,8 +83,16 @@ void AnimatorManager::pushStaging() {
|
|||||||
}
|
}
|
||||||
mNewAnimators.clear();
|
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();
|
mAnimationHandle->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimatorManager::forceEndAnimators() {
|
||||||
|
mCancelAllAnimators = true;
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace uirenderer */
|
} /* namespace uirenderer */
|
||||||
} /* namespace android */
|
} /* namespace android */
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
#ifndef ANIMATORMANAGER_H
|
#ifndef ANIMATORMANAGER_H
|
||||||
#define ANIMATORMANAGER_H
|
#define ANIMATORMANAGER_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <cutils/compiler.h>
|
#include <cutils/compiler.h>
|
||||||
#include <utils/StrongPointer.h>
|
#include <utils/StrongPointer.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "utils/Macros.h"
|
#include "utils/Macros.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
@@ -56,6 +56,8 @@ public:
|
|||||||
// Hard-ends all animators. May only be called on the UI thread.
|
// Hard-ends all animators. May only be called on the UI thread.
|
||||||
void endAllStagingAnimators();
|
void endAllStagingAnimators();
|
||||||
|
|
||||||
|
void forceEndAnimators();
|
||||||
|
|
||||||
// Hard-ends all animators that have been pushed. Used for cleanup if
|
// Hard-ends all animators that have been pushed. Used for cleanup if
|
||||||
// the ActivityContext is being destroyed
|
// the ActivityContext is being destroyed
|
||||||
void endAllActiveAnimators();
|
void endAllActiveAnimators();
|
||||||
@@ -71,6 +73,8 @@ private:
|
|||||||
// To improve the efficiency of resizing & removing from the vector
|
// To improve the efficiency of resizing & removing from the vector
|
||||||
std::vector<sp<BaseRenderNodeAnimator> > mNewAnimators;
|
std::vector<sp<BaseRenderNodeAnimator> > mNewAnimators;
|
||||||
std::vector<sp<BaseRenderNodeAnimator> > mAnimators;
|
std::vector<sp<BaseRenderNodeAnimator> > mAnimators;
|
||||||
|
|
||||||
|
bool mCancelAllAnimators;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace uirenderer */
|
} /* namespace uirenderer */
|
||||||
|
|||||||
@@ -543,6 +543,12 @@ static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
|
|||||||
renderNode->animators().endAllStagingAnimators();
|
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
|
// SurfaceView position callback
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@@ -745,6 +751,7 @@ static const JNINativeMethod gMethods[] = {
|
|||||||
{"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
|
{"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
|
||||||
{"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
|
{"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
|
||||||
{"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
|
{"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
|
||||||
|
{"nForceEndAnimators", "(J)V", (void*)android_view_RenderNode_forceEndAnimators},
|
||||||
{"nRequestPositionUpdates", "(JLjava/lang/ref/WeakReference;)V",
|
{"nRequestPositionUpdates", "(JLjava/lang/ref/WeakReference;)V",
|
||||||
(void*)android_view_RenderNode_requestPositionUpdates},
|
(void*)android_view_RenderNode_requestPositionUpdates},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user