Repository: flutter
Issue: [engine] FlutterFragmentActivity needs a fallback for the cached engine
Labels: platform-android,engine,c: proposal,P2,team-android,triaged-android

When a `FlutterFragmentActivity` is used with a cached engine, the fragment state is saved with the EXTRA_CACHED_ENGINE_ID. So then if the activity is destroyed, and the user re-opens the activity, it will remember the cached engine ID from last time (rather than re-querying `activity.getCachedEngineId()` and assume that the old engine ID is still in the cache. If it isn't, this check in `FlutterActivityAndFragmentDelegate.java` fails:

```java
    // First, check if the host wants to use a cached FlutterEngine.
    String cachedEngineId = host.getCachedEngineId();
    if (cachedEngineId != null) {
      flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
      isFlutterEngineFromHost = true;
      if (flutterEngine == null) {
        throw new IllegalStateException(
            "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '"
                + cachedEngineId
                + "'");
      }
      return;
    }
```

There should be a fallback where if the old ID isn't in the cache anymore, `getCachedEngineId()` should be called on the activity.

This has turned up in a bug report in my plugin [audio_service](https://github.com/ryanheise/audio_service/issues/964) where I'm using the cached engine ID mechanism to allow the same flutter engine to be shared between the activity and service. An audio_service application can have an entrypoint via the activity if the user launches the activity, or it can have an entrypoint via the service if the user presses the play button in the media notification to resume an old media session. But in both cases, we want to route through the same flutter engine. If the engine was already created via one entrypoint, then it should be reused from the other entrypoint.

We keep the engine around only so long as either the activity or the service are active. As soon as the last one of those is gone, we dispose of the engine, and that is where the problem arises, because at that point, if the user re-opens the activity, the stale `EXTRA_CACHED_ENGINE_ID` key is assumed to still point to a valid engine in the cache without actually querying `activity.getCachedEngineId()` to see if that is still what the activity wants. (*)

I wonder whether `EXTRA_CACHED_ENGINE_ID` should actually be saved as state at all because the cache isn't really the "actual" state, the cache is just a mechanism to optimise resources. That seems like a misuse of the cache API.

Although it would be nice to have a better solution in the engine to handle this use case, I would also appreciate some guidance on how to work around this in the current engine implementation. Are there implicit rules we are not following, such as that we are not actually permitted to ever destroy and remove an engine from the cache once it has been used in a `FlutterFragmentActivity`, even after that activity has itself been destroyed? Do we need to keep that engine around indefinitely although it will use up resources? If that is the answer, it will be helpful to know that so I can redesign the plugin accordingly.

(*) Admittedly our implementation of `activity.getCachedEngineId()` in the plugin is a bit cheeky, it is used as a lifecycle hook to lazilly create and cache the engine, but I don't know if there is a better lifecycle hook to do that.