RouterStateAdapter now properly restores routers after process death

This commit is contained in:
EricKuck
2021-11-28 11:27:47 -06:00
parent 46519c2c2c
commit 8297e0273d
3 changed files with 45 additions and 7 deletions
@@ -154,7 +154,7 @@ abstract class RouterStateAdapter(private val host: Controller) :
private fun attachRouter(holder: RouterViewHolder, position: Int) {
val itemId = getItemId(position)
val router = host.getChildRouter(holder.container, "$itemId")
val router = host.getChildRouter(holder.container, "$itemId", true, false)!!
// This should have already been handled by onViewRecycled, but it seems like this wasn't
// always reliably called
@@ -9,7 +9,6 @@ import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -215,6 +214,23 @@ public abstract class Controller {
*/
@Nullable
public final Router getChildRouter(@NonNull ViewGroup container, @Nullable String tag, boolean createIfNeeded) {
return getChildRouter(container, tag, createIfNeeded, true);
}
/**
* Retrieves the child {@link Router} for the given container/tag combination. Note that multiple
* routers should not exist in the same container unless a lot of care is taken to maintain order
* between them. Avoid using the same container unless you have a great reason to do so (ex: ViewPagers).
* The only time this method will return {@code null} is when the child router does not exist prior
* to calling this method and the createIfNeeded parameter is set to false.
*
* @param container The ViewGroup that hosts the child Router
* @param tag The router's tag or {@code null} if none is needed
* @param createIfNeeded If true, a router will be created if one does not yet exist. Else {@code null} will be returned in this case.
* @param boundToHostContainerId If true, a router will only ever rebind with a container with the same view id on state restoration. Note that this must be set to true if the tag is null.
*/
@Nullable
public final Router getChildRouter(@NonNull ViewGroup container, @Nullable String tag, boolean createIfNeeded, boolean boundToHostContainerId) {
@IdRes final int containerId = container.getId();
if (containerId == View.NO_ID) {
throw new IllegalStateException("You must set an id on your container.");
@@ -222,7 +238,7 @@ public abstract class Controller {
ControllerHostedRouter childRouter = null;
for (ControllerHostedRouter router : childRouters) {
if (router.getHostId() == containerId && TextUtils.equals(tag, router.getTag())) {
if (router.matches(containerId, tag)) {
childRouter = router;
break;
}
@@ -230,7 +246,7 @@ public abstract class Controller {
if (childRouter == null) {
if (createIfNeeded) {
childRouter = new ControllerHostedRouter(container.getId(), tag);
childRouter = new ControllerHostedRouter(container.getId(), tag, boundToHostContainerId);
childRouter.setHostContainer(this, container);
childRouters.add(childRouter);
@@ -5,6 +5,7 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.ViewGroup;
import androidx.annotation.IdRes;
@@ -22,18 +23,28 @@ class ControllerHostedRouter extends Router {
private final String KEY_HOST_ID = "ControllerHostedRouter.hostId";
private final String KEY_TAG = "ControllerHostedRouter.tag";
private final String KEY_BOUND_TO_CONTAINER = "ControllerHostedRouter.boundToContainer";
private Controller hostController;
@IdRes private int hostId;
private String tag;
private boolean isDetachFrozen;
private boolean boundToContainer;
ControllerHostedRouter() { }
ControllerHostedRouter(int hostId, @Nullable String tag) {
this(hostId, tag, false);
}
ControllerHostedRouter(int hostId, @Nullable String tag, boolean boundToContainer) {
if (!boundToContainer && tag == null) {
throw new IllegalStateException("ControllerHostedRouter can't be created without a tag if not bounded to its container");
}
this.hostId = hostId;
this.tag = tag;
this.boundToContainer = boundToContainer;
}
final void setHostController(@NonNull Controller controller) {
@@ -213,6 +224,7 @@ class ControllerHostedRouter extends Router {
super.saveInstanceState(outState);
outState.putInt(KEY_HOST_ID, hostId);
outState.putBoolean(KEY_BOUND_TO_CONTAINER, boundToContainer);
outState.putString(KEY_TAG, tag);
}
@@ -221,6 +233,7 @@ class ControllerHostedRouter extends Router {
super.restoreInstanceState(savedInstanceState);
hostId = savedInstanceState.getInt(KEY_HOST_ID);
boundToContainer = savedInstanceState.getBoolean(KEY_BOUND_TO_CONTAINER);
tag = savedInstanceState.getString(KEY_TAG);
}
@@ -234,9 +247,18 @@ class ControllerHostedRouter extends Router {
return hostId;
}
@Nullable
String getTag() {
return tag;
boolean matches(int hostId, @Nullable String tag) {
if (!boundToContainer && container == null) {
if (this.tag == null) {
throw new IllegalStateException("Host ID can't be variable with a null tag");
}
if (this.tag.equals(tag)) {
this.hostId = hostId;
return true;
}
}
return this.hostId == hostId && TextUtils.equals(tag, this.tag);
}
@Override @NonNull