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 Limitation:
A preloaded presenter has only ONE View instance. When you call
getView()from a different Activity/Fragment, the View is physically moved from its previous parent to the new one.This means: - You cannot display the same preloaded plan in multiple Activities/Fragments simultaneously - If you have multiple Activities in the back stack using the same preloaded presenter, only the top one will show the plan - Previous Activities will have blank/empty screens because their View was taken by the new Activity
Solution: Use
FLAG_ACTIVITY_CLEAR_TOPorsingleToplaunch mode if you need to repeatedly open the same screen with a preloaded plan.Note: When reusing a preloaded plan across multiple screens,
getView()detaches the view from its previous parent automatically, so you can add it to the new container directly.
Jetpack Compose¶
AndroidView(
factory = { presenter.getView() },
modifier = Modifier.fillMaxSize()
)
Note:
getView()returns a self-sizing container and detaches itself from its previous parent, so you can host it directly inAndroidViewwithout manualFrameLayoutwrapping.
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)
// getView() detaches from any previous parent, so add it to this container directly
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)
// getView() detaches from Activity A's container, so add it here directly
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.
}
}
Advanced: Multiple Activities in Back Stack¶
If you have multiple Activities in the back stack that need to share the same preloaded presenter simultaneously, note that only the topmost Activity will display the plan due to the one-View-per-presenter limitation.
For this advanced scenario where you need the plan to reappear when returning to a previous Activity via the back button, you can re-attach the View in onStart():
class MapActivity : AppCompatActivity() {
private var presenter: IExpoFpPlanPresenter? = null
private var container: FrameLayout? = null
private var planView: View? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
presenter = ExpoFpPlan.preloader.getPreloadedPlanPresenter(preloadedPlanInfo)
// Standard pattern for initial attachment in onCreate()
// (For re-attachment on back stack return, see onStart() pattern below)
// Keep the view reference so onStart() can re-attach it without calling getView() again.
planView = presenter?.getView()
container = FrameLayout(this).apply {
addView(planView)
}
rootView.addView(container)
presenter?.setMessageListener(myListener)
}
override fun onStart() {
super.onStart()
// Re-attach only if another Activity took the view while this one was in the back stack.
// Avoid calling getView() here: it would detach the view and re-trigger attach/detach.
if (planView != null && planView?.parent != container) {
(planView?.parent as? ViewGroup)?.removeView(planView)
container?.addView(planView)
}
}
override fun onDestroy() {
super.onDestroy()
// Note: the preloaded presenter survives this destroy; no manual cleanup is needed here.
}
}
Note: The re-attachment pattern addresses edge cases where multiple Activities remain in the back stack with a shared preloaded presenter. For standard workflows, use
FLAG_ACTIVITY_CLEAR_TOPorsingleToplaunch mode (see Important Limitation above). StandardonCreate()attachment suffices for typical destroy/recreate cycles.