Skip to content

Add method to purge keepAlive views that are not in the active historyStack #65

Description

@iObject

purgeStaleKeepAliveViews

Manually reclaim memory held by suspended keep-alive views that are no longer in the active history stack.


Why purgeStaleKeepAliveViews?

Roku Router's keepAlive feature allows views to remain in memory after they are no longer present in the navigation history stack. This is useful for preserving view state across navigation events and improving perceived performance when revisiting a screen.

However, over time these suspended views accumulate in memory without a deterministic way to remove them. In long-running applications, or after major navigation events (e.g. signing out, resetting to home), stale keep-alive views may hold onto scene nodes, content data, and component references unnecessarily.

purgeStaleKeepAliveViews gives developers explicit, first-class control over when this memory is reclaimed — without affecting any view that is still reachable via the history stack.


Usage

Call purgeStaleKeepAliveViews at any point during the application lifecycle:

RokuRouter.purgeStaleKeepAliveViews()

A common pattern is to call it after major navigation events where stale views are unlikely to be revisited:

' After signing out — clear all suspended views
m.global.AuthManager.signOut()
RokuRouter.purgeStaleKeepAliveViews()
' After navigating to home and resetting the stack
m.router.navigateTo("/home")
RokuRouter.purgeStaleKeepAliveViews()

Behaviour

  • Views whose route.path is not present in the current history stack are identified as stale and purged
  • Views whose route.path is present in the history stack are left completely untouched — including views that are suspended but still reachable via back()
  • For each stale view, _beforeViewClose is awaited before the node is removed, ensuring clean lifecycle teardown
  • All _beforeViewClose hooks are batched via promises.all — removal only begins once every hook has resolved
  • Safe to call when there are zero stale views — the function completes without error

How It Works

' Function to purge keepAlive views that are no longer in the history stack.
' Views whose path is still present in the history stack are left untouched.
' For each stale view, _beforeViewClose is awaited before the node is removed.
' @returns {Dynamic} - A promise that resolves when all stale keepAlive views are removed
function _purgeStaleKeepAliveViews(_ = Invalid as Dynamic) as Dynamic
    viewsToPurge = []

    for each v in rodash.getNodeChildren(m.__router_keepAliveViewTarget)
        inHistory = false
        for each path in m.__router_historyStack
            if v.route.path = path then
                inHistory = true
                exit for
            end if
        end for

        if NOT inHistory then
            viewsToPurge.push(v)
        end if
    end for

    return promises.chain(promises.resolve(Invalid), { viewsToPurge: viewsToPurge })
        .then(function(_ as Dynamic, ctx as Dynamic) as Dynamic
            closePromises = []
            for each v in ctx.viewsToPurge
                closePromises.push(v@._beforeViewClose({ route: v.route }))
            end for
            return promises.all(closePromises)
        end function)
        .then(function(_ as Dynamic, ctx as Dynamic) as Dynamic
            for each v in ctx.viewsToPurge
                rodash.removeNode(v)
            end for
        end function).toPromise()
end function

The function uses promises.chain with a context object so viewsToPurge is accessible across both .then steps without closure mutation. Node removal only occurs in the second .then, after all _beforeViewClose promises have resolved.


Notes

  • Automatic or scheduled purging is not currently supported — purgeStaleKeepAliveViews is a manual call only
  • Selective purging by route name or pattern is not currently supported and may be added as a follow-up
  • This function does not affect keep-alive eligibility logic or configuration

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions