Skip to content

Preload Plan

Preload plans in advance and reuse them later without the internet during the app lifecycle.

Overview

  • Use ExpoFpPlan.preloader to 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 stored ExpoFpPreloadedPlanInfo.

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() or removeAllPreloadedPlans().
  • 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

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

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_TOP or singleTop launch 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 in AndroidView without manual FrameLayout wrapping.


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_TOP or singleTop launch mode (see Important Limitation above). Standard onCreate() attachment suffices for typical destroy/recreate cycles.