Preload Plan¶
Preload plans in advance and reuse them later without the internet during the app lifecycle.
Overview¶
- Use
ExpoFpPlan.preloaderto preload plans. - A preloaded plan is represented by
ExpoFpPreloadedPlanInfo. - You can obtain a presenter for a preloaded plan with
getPreloadedPlanPresenter(...). - Recommended: Preload plans if you expect to reuse them multiple times in one session.
- If a preloaded plan is reloaded with a new
planLink, the link info is automatically updated in the storedExpoFpPreloadedPlanInfo.
Lifecycle Management¶
Important: Preloaded plans are NOT automatically disposed when the Activity/Fragment is destroyed. This allows you to reuse the same presenter across multiple screens without reloading.
- Preloaded plans persist in memory until you explicitly call
disposePreloadedPlan()orremoveAllPreloadedPlans().- You can safely attach and detach the same preloaded presenter to different Activities/Fragments.
- All event callbacks will continue to work after reattaching a preloaded plan.
- You are responsible for disposing preloaded plans when they are no longer needed to free memory.
Listener lifecycle¶
The SDK does not retain the message listener. A preloaded presenter reused across screens keeps delivering callbacks without re-registering the listener.
Keep your own strong reference to the listener for as long as you need callbacks.
If you need to remove the listener earlier, you can manually call
removeMessageListener():// Optional: detach the listener manually when no longer needed presenter?.removeMessageListener()
Step 1. Preload from the Internet¶
Coroutine version (recommended)¶
val expoKey = "YourExpoKey"
val preloadedPlanInfo = ExpoFpPlan.preloader.preloadPlan(
planLink = ExpoFpLinkType.ExpoKey(expoKey)
)
With additional parameters¶
val additionalParams = listOf(ExpoFpPlanParameter.NoOverlay(true), ExpoFpPlanParameter.HideHeaderLogo(true))
val locationProvider: IExpoFpLocationProvider = YourLocationProvider()
val messageListener: IExpoFpPlanMessageListener = YourMessageListener()
val preloadedPlanInfo = ExpoFpPlan.preloader.preloadPlan(
planLink = ExpoFpLinkType.ExpoKey("YourExpoKey"),
additionalParams = additionalParams,
locationProvider = locationProvider,
messageListener = messageListener
)
Step 2. Preload from Downloaded Plan¶
Coroutine version (recommended)¶
val preloadedPlanInfo = ExpoFpPlan.preloader.preloadPlan(
planLink = ExpoFpLinkType.DownloadedPlanInfo(downloadedPlanInfo)
)
With additional parameters¶
val additionalParams = listOf(ExpoFpPlanParameter.NoOverlay(true), ExpoFpPlanParameter.HideHeaderLogo(true))
val locationProvider: IExpoFpLocationProvider = YourLocationProvider()
val messageListener: IExpoFpPlanMessageListener = YourMessageListener()
val preloadedPlanInfo = ExpoFpPlan.preloader.preloadPlan(
planLink = ExpoFpLinkType.DownloadedPlanInfo(downloadedPlanInfo),
additionalParams = additionalParams,
locationProvider = locationProvider,
messageListener = messageListener
)
Step 3. Manage Preloaded Plans¶
Get all preloaded plans:
val preloadedPlansInfo: List<ExpoFpPreloadedPlanInfo> =
ExpoFpPlan.preloader.getPreloadedPlansInfo()
Get a presenter from a preloaded plan:
val presenter = ExpoFpPlan.preloader.getPreloadedPlanPresenter(preloadedPlanInfo)
If only one plan is preloaded, you can get it without parameters:
val presenter = ExpoFpPlan.preloader.getPreloadedPlanPresenter()
Step 4. Show Preloaded Plan¶
View-based UI¶
container.addView(presenter.getView())
Important:
A preloaded presenter renders the plan with a single engine, so the plan is visible on one screen at a time. Each
getView()call returns a new host container; the plan content is shown in the most recently attached one. When the screen currently showing the plan is destroyed (for example, on back navigation), the content automatically returns to the previous screen that is still present — no re-attachment code is needed.Call
getView()once per screen and keep the result for that screen's lifetime. Do not call it repeatedly inonStart()/onResume(): every call creates a new container, and re-adding a new one on each lifecycle pass accumulates empty containers in your layout. Do not compare results of different calls by identity.
Jetpack Compose¶
AndroidView(
factory = { presenter.getView() },
modifier = Modifier.fillMaxSize()
)
Note:
getView()returns a self-sizing host container, so you can host it directly inAndroidViewwithout manualFrameLayoutwrapping. The factory runs once per composition, which matches the "call once per screen" rule.
Step 5. Delete Preloaded Plans¶
Delete a specific plan:
val presenter = ExpoFpPlan.preloader.disposePreloadedPlan(preloadedPlanInfo)
Delete all plans:
ExpoFpPlan.preloader.removeAllPreloadedPlans()
Best Practices¶
- Preload early: Preload plans during app initialization or screen preparation if you expect repeated access.
- Reuse presenters: Get presenters from preloaded plans instead of creating new ones to reduce loading time.
- Clear when not needed: Dispose preloaded plans when they are no longer needed to free memory.
- Combine with download: Download plans to disk first, then preload them for faster reuse.
Memory Management¶
Preloaded plans remain in memory until explicitly disposed:
class MainActivity : AppCompatActivity() {
private var preloadedPlanInfo: ExpoFpPreloadedPlanInfo? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Preload plan once
if (preloadedPlanInfo == null) {
preloadedPlanInfo = ExpoFpPlan.preloader.preloadPlan(
planLink = ExpoFpLinkType.ExpoKey("demo")
)
}
// Get presenter from preloaded plan
val presenter = ExpoFpPlan.preloader.getPreloadedPlanPresenter(preloadedPlanInfo)
// Attach to view - can be done multiple times across different Activities
presenter?.let {
val planView = it.getView()
container.addView(planView)
}
}
override fun onDestroy() {
super.onDestroy()
// Only dispose when you no longer need the plan
if (isFinishing) {
preloadedPlanInfo?.let {
ExpoFpPlan.preloader.disposePreloadedPlan(it)
}
}
}
}
Reusing Across Multiple Screens¶
Preloaded plans can be safely reused across different Activities or Fragments:
// Activity A
class MapActivityA : AppCompatActivity() {
private var presenter: IExpoFpPlanPresenter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
presenter = ExpoFpPlan.preloader.getPreloadedPlanPresenter(preloadedPlanInfo)
// Each getView() call returns a new host container for this screen
presenter?.getView()?.let { container.addView(it) }
// Set message listener for this screen
presenter?.setMessageListener(myListener)
}
override fun onDestroy() {
super.onDestroy()
// Note: the preloaded presenter survives this destroy; no manual cleanup is needed here.
}
}
// Activity B - reuse the same preloaded plan
class MapActivityB : AppCompatActivity() {
private var presenter: IExpoFpPlanPresenter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Get the same presenter - no reloading needed!
presenter = ExpoFpPlan.preloader.getPreloadedPlanPresenter(preloadedPlanInfo)
// The plan moves to this screen's host once it is attached; when this screen is
// closed, the plan automatically returns to Activity A
presenter?.getView()?.let { container.addView(it) }
// All callbacks still work - set new listener for this screen
presenter?.setMessageListener(myListener)
}
override fun onDestroy() {
super.onDestroy()
// Note: the preloaded presenter survives this destroy; no manual cleanup is needed here.
}
}
Multiple Activities in Back Stack¶
When several Activities in the back stack share the same preloaded presenter, the plan is
visible on the topmost one. Returning to a previous Activity via the back button restores the
plan on it automatically: once the closing Activity is destroyed, the content moves back to
the previous screen's host container. No onStart() re-attachment code, FLAG_ACTIVITY_CLEAR_TOP
or singleTop launch mode is required for this scenario.
If your code still contains a manual re-attachment pattern from earlier SDK versions (keeping the
view reference and re-adding it in onStart() when its parent changed), it is harmless and
degrades to a no-op — you can remove it.
Note: Navigation that brings an existing Activity to the front while the previous plan screen stays alive in the back stack (for example
reorderToFront) does not move the plan: the content follows attach order. For such flows, obtain a new view withgetView()on the screen that should show the plan and replace the previous host in your layout.