From 914aa7da7f0d74f5de358daa616c50cbf5bd9c30 Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Mon, 19 Mar 2018 21:51:53 -0700 Subject: [PATCH] Use transaction for local activity relaunch To know that onPostCreate callback should be executed we should use TransactionExecutor for the entire transaction. It will fill PendingActions object during the launch and the callback will be triggered after onStart. This CL changes local activity relaunch to use Lifecycler infrastructure. We should immediately execute local recreate requests, because if we wait until the scheduled message to be handled, we may already be in a different state and final state request in the scheduled relaunch transaction will already be obsolete. Bug: 72029061 Bug: 64610483 Bug: 76088057 Bug: 73747058 Test: ActivityLifecycleTests Change-Id: Ia53ecd199c83d030932c4493064e58568805f2a5 --- core/java/android/app/ActivityThread.java | 36 ++++++++++++++----- .../android/app/ClientTransactionHandler.java | 17 +++++++++ .../TransactionExecutorHelper.java | 27 ++++++++++++-- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f01eee4709b50..1df724edeccb9 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -30,12 +30,15 @@ import android.annotation.Nullable; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.app.backup.BackupAgent; +import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; +import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ActivityResultItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.PendingTransactionActions; import android.app.servertransaction.PendingTransactionActions.StopInfo; import android.app.servertransaction.TransactionExecutor; +import android.app.servertransaction.TransactionExecutorHelper; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; @@ -520,6 +523,10 @@ public final class ActivityThread extends ClientTransactionHandler { return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS; } + public boolean isVisibleFromServer() { + return activity != null && activity.mVisibleFromServer; + } + public String toString() { ComponentName componentName = intent != null ? intent.getComponent() : null; return "ActivityRecord{" @@ -1797,6 +1804,7 @@ public final class ActivityThread extends ClientTransactionHandler { // message is handled. transaction.recycle(); } + // TODO(lifecycler): Recycle locally scheduled transactions. break; } Object obj = msg.obj; @@ -2755,6 +2763,11 @@ public final class ActivityThread extends ClientTransactionHandler { } } + @Override + TransactionExecutor getTransactionExecutor() { + return mTransactionExecutor; + } + void sendMessage(int what, Object obj) { sendMessage(what, obj, 0, 0, false); } @@ -4723,15 +4736,22 @@ public final class ActivityThread extends ClientTransactionHandler { return; } - // TODO(b/73747058): Investigate converting this to use transaction to relaunch. - handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */, - null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME, - r.overrideConfig, "handleRelaunchActivityLocally"); - // Restore back to the previous state before relaunch if needed. - if (prevState != r.getLifecycleState()) { - mTransactionExecutor.cycleToPath(r, prevState); - } + // Initialize a relaunch request. + final MergedConfiguration mergedConfiguration = new MergedConfiguration( + r.createdConfig != null ? r.createdConfig : mConfiguration, + r.overrideConfig); + final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain( + null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */, + mergedConfiguration, r.mPreserveWindow); + // Make sure to match the existing lifecycle state in the end of the transaction. + final ActivityLifecycleItem lifecycleRequest = + TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); + // Schedule the transaction. + final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token); + transaction.addCallback(activityRelaunchItem); + transaction.setLifecycleStateRequest(lifecycleRequest); + executeTransaction(transaction); } private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges, diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 961bca23c91af..925080e447789 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -17,6 +17,7 @@ package android.app; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.PendingTransactionActions; +import android.app.servertransaction.TransactionExecutor; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -43,6 +44,22 @@ public abstract class ClientTransactionHandler { sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction); } + /** + * Execute transaction immediately without scheduling it. This is used for local requests, so + * it will also recycle the transaction. + */ + void executeTransaction(ClientTransaction transaction) { + transaction.preExecute(this); + getTransactionExecutor().execute(transaction); + transaction.recycle(); + } + + /** + * Get the {@link TransactionExecutor} that will be performing lifecycle transitions and + * callbacks for activities. + */ + abstract TransactionExecutor getTransactionExecutor(); + abstract void sendMessage(int what, Object obj); diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 7e66fd7a2ead9..01b13a28aed1f 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -26,7 +26,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE; import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; -import android.app.ActivityThread; +import android.app.ActivityThread.ActivityClientRecord; import android.util.IntArray; import com.android.internal.annotations.VisibleForTesting; @@ -124,7 +124,7 @@ public class TransactionExecutorHelper { * {@link ActivityLifecycleItem#UNDEFINED} if there is not path. */ @VisibleForTesting - public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r, + public int getClosestPreExecutionState(ActivityClientRecord r, int postExecutionState) { switch (postExecutionState) { case UNDEFINED: @@ -147,7 +147,7 @@ public class TransactionExecutorHelper { * were provided or there is not path. */ @VisibleForTesting - public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) { + public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) { if (finalStates == null || finalStates.length == 0) { return UNDEFINED; } @@ -168,6 +168,27 @@ public class TransactionExecutorHelper { return closestState; } + /** Get the lifecycle state request to match the current state in the end of a transaction. */ + public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) { + final int prevState = r.getLifecycleState(); + final ActivityLifecycleItem lifecycleItem; + switch (prevState) { + // TODO(lifecycler): Extend to support all possible states. + case ON_PAUSE: + lifecycleItem = PauseActivityItem.obtain(); + break; + case ON_STOP: + lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(), + 0 /* configChanges */); + break; + default: + lifecycleItem = ResumeActivityItem.obtain(false /* isForward */); + break; + } + + return lifecycleItem; + } + /** * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence * that involves destruction and recreation if there is another path.