From b29e318e0337213d4e031dc5d339f9903cef78a9 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Mon, 30 Apr 2018 18:51:56 +0200 Subject: [PATCH] Schedule trimMemory after drawing the frame Fixes jank issues where trimMemory gets scheduled during an animation. trimMemory likely hits code that is not in the page cache anymore, leading to high execution time because of page misses. Scheduling it after the next frame has been drawn minimizes the risk that this page miss actually causes jank. Test: Boot, switch between a couple of apps. Bug: 78611607 Change-Id: Ia1fc411fbe6ca07861183ae484124406681118e5 --- core/java/android/app/ActivityThread.java | 23 ++++++++++++++--------- core/java/android/view/Choreographer.java | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 0ae4b7d3df663..494ea39fe2fa6 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -124,6 +124,7 @@ import android.util.Slog; import android.util.SparseIntArray; import android.util.SuperNotCalledException; import android.util.proto.ProtoOutputStream; +import android.view.Choreographer; import android.view.ContextThemeWrapper; import android.view.Display; import android.view.ThreadedRenderer; @@ -145,6 +146,7 @@ import com.android.internal.os.RuntimeInit; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.org.conscrypt.OpenSSLSocketImpl; import com.android.org.conscrypt.TrustedCertificateStore; import com.android.server.am.MemInfoDumpProto; @@ -1406,7 +1408,15 @@ public final class ActivityThread extends ClientTransactionHandler { } public void scheduleTrimMemory(int level) { - sendMessage(H.TRIM_MEMORY, null, level); + final Runnable r = PooledLambda.obtainRunnable(ActivityThread::handleTrimMemory, + ActivityThread.this, level); + // Schedule trimming memory after drawing the frame to minimize jank-risk. + Choreographer choreographer = Choreographer.getMainThreadInstance(); + if (choreographer != null) { + choreographer.postCallback(Choreographer.CALLBACK_COMMIT, r, null); + } else { + mH.post(r); + } } public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) { @@ -1561,7 +1571,6 @@ public final class ActivityThread extends ClientTransactionHandler { public static final int SLEEPING = 137; public static final int SET_CORE_SETTINGS = 138; public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139; - public static final int TRIM_MEMORY = 140; public static final int DUMP_PROVIDER = 141; public static final int UNSTABLE_PROVIDER_DIED = 142; public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143; @@ -1607,7 +1616,6 @@ public final class ActivityThread extends ClientTransactionHandler { case SLEEPING: return "SLEEPING"; case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS"; case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO"; - case TRIM_MEMORY: return "TRIM_MEMORY"; case DUMP_PROVIDER: return "DUMP_PROVIDER"; case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED"; case REQUEST_ASSIST_CONTEXT_EXTRAS: return "REQUEST_ASSIST_CONTEXT_EXTRAS"; @@ -1741,11 +1749,6 @@ public final class ActivityThread extends ClientTransactionHandler { case UPDATE_PACKAGE_COMPATIBILITY_INFO: handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj); break; - case TRIM_MEMORY: - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory"); - handleTrimMemory(msg.arg1); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - break; case UNSTABLE_PROVIDER_DIED: handleUnstableProviderDied((IBinder)msg.obj, false); break; @@ -5409,7 +5412,8 @@ public final class ActivityThread extends ClientTransactionHandler { BinderInternal.forceGc("mem"); } - final void handleTrimMemory(int level) { + private void handleTrimMemory(int level) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory"); if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level); ArrayList callbacks = collectComponentCallbacks(true, null); @@ -5420,6 +5424,7 @@ public final class ActivityThread extends ClientTransactionHandler { } WindowManagerGlobal.getInstance().trimMemory(level); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } private void setupGraphicsSupport(Context context) { diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 1caea57731609..f8cfd0da49522 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -106,10 +106,16 @@ public final class Choreographer { if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } - return new Choreographer(looper, VSYNC_SOURCE_APP); + Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP); + if (looper == Looper.getMainLooper()) { + mMainInstance = choreographer; + } + return choreographer; } }; + private static volatile Choreographer mMainInstance; + // Thread local storage for the SF choreographer. private static final ThreadLocal sSfThreadInstance = new ThreadLocal() { @@ -263,6 +269,14 @@ public final class Choreographer { return sSfThreadInstance.get(); } + /** + * @return The Choreographer of the main thread, if it exists, or {@code null} otherwise. + * @hide + */ + public static Choreographer getMainThreadInstance() { + return mMainInstance; + } + /** Destroys the calling thread's choreographer * @hide */