Roku with You.i Roku Cloud apps are almost inactive while the Roku client is playing a video, or when the end user isn’t using the Roku remote. During this time, Cloud server instances can be made available to other Roku clients by saving their state, which can be used by the new server instances to resume the application. This allows a greater number of users to be serviced by the Cloud infrastructure while reducing costs and the number of server instances running at a given time. We call this feature “Disconnect on Inactivity”.
To support this feature in your app, you need to add app logic to save and restore the app’s state at the appropriate times.
When a Roku client is inactive for more than the noActivitySessionTimeoutSec
time, the You.i Roku Cloud infrastructure disconnects the Roku client from the Cloud server by providing it with the saved state information from the previous connection.
The client reconnects again with the server after the end-user’s interaction with the app.
The You.i Roku Cloud server saves its state with the CYICloud::GetInterface().SaveInstanceState(savedState)
API for a You.i C++ app, in response to the screenSaverWillStart
event.
The Cloud server instance is destroyed once the saveInstanceState
is complete.
For more information on the APIs, see saveInstanceState() and getSavedInstanceState().
The saved state is a deep link to the screen at the point when the client became inactive. A stack of screen data could also be stored to restore the history. The app state is stored by the Roku client as a JSON stringified blob.
The following sequence diagram illustrates the sequence of events between the Roku client, Cloud infrastructure, and Cloud server when the screensaver is active for longer than noActivitySessionTimeoutSec
.
The following sequence diagram illustrates the sequence of events between the Roku client, Cloud infrastructure, and Cloud server when the screensaver is active for less than noActivitySessionTimeoutSec
, but the server exceeds two ping durations (which is every 10 seconds) from the client.
The following diagram explains the You.i Roku Cloud server activity cycle for Disconnect on Inactivity:
To enable the Disconnect on Inactivity feature, you need to update the client configuration file and subscribe to events from the client.
Add the following to clientConfig.json
, located at ../youi/build/<platform>/<configuration>/assets/json/default/
:
{
"inactivityDisconnect": true
}
Setting inactivityDisconnect
to true
causes the application to save its state when the client is inactive.
The client sends the screenSaverWillStart
event 30 seconds before the screensaver actually starts, which enables the server to send the app state data to the client.
If the screensaver active time is longer than noActivitySessionTimeoutSec
, which is configured by the Cloud infrastructure, the connection to the server instance is terminated.
On an end-user’s interaction with the app, the connection between client and new server instance is established, and the client starts from the saved state, instead of the beginning.
When running this feature locally, the server terminates when disconnect on inactivity occurs. You must restart the server manually again.
Subscribe to events from the client on UserStart
as shown in the following example.
bool VideoPlayerApp::UserStart()
{
// ...
CYICloud::GetInterface().ClientBackgrounded.Connect(*this, &VideoPlayerApp::OnClientBackgrounded, EYIConnectionType::Async);
CYICloud::GetInterface().ScreenSaverWillStart.Connect(*this, &VideoPlayerApp::OnScreenSaverWillStart, EYIConnectionType::Async);
// ...
}
void VideoPlayerApp::OnClientBackgrounded()
{
YI_LOGI(LOG_TAG, "OnClientBackgrounded.");
CYICloud::GetInterface().SaveInstanceState(Transition::GetStringifiedNavigationHistory(m_transition.GetScreenHistory()));
}
void VideoPlayerApp::OnScreenSaverWillStart()
{
YI_LOGI(LOG_TAG, "OnScreenSaverWillStart.");
CYICloud::GetInterface().SaveInstanceState(Transition::GetStringifiedNavigationHistory(m_transition.GetScreenHistory()));
}
This feature is supported for C++ apps only.
When a Roku client is reconnecting to a server with the Disconnect On Inactivity feature, to reduce the chance of a re-export of the client’s current scene, the client node IDs generated on the server must be assigned deterministically. For this reason, Roku client node IDs are constructed using a relative path, as a sequence of child indices, of the target node starting from its root scene.
Using the relative path of scene nodes as the client node ID may fail, however, if sibling nodes are not ordered deterministically throughout the scene tree. For example, for a list of buttons that are generated dynamically based on video content information, the static ID provided might be a content-specific string such as the video media ID. As another example, optional nodes within a staged scene could be assigned a static ID to identify these nodes uniquely within the top-level scene.
Once the client reconnects to a server, the server scene tree synchronization monitor performs an export if it determines that the scene tree does not match the client. This match is based on a hash of scene tree client node IDs.
To assign static node IDs:
"useStaticNodeID" : true
to serverConfig.json
.
To learn more about serverConfig.json
, see Customizing the Cloud Solution Application Configuration File.Specify a static ID using the CloudInterface SetExportHint
API with HINT::STATIC_ID
.
CYICloud::GetInterface().SetExportHint(pSceneView, HINT::STATIC_ID, "Option1");
This feature is supported for C++ apps only.
There are times when you may want your application to temporarily disable the Disconnect on Inactivity system at runtime - for example, when performing an in-app purchase (IAP).
A Cloud module API allows you to turn the feature on and off programmatically.
Call SetDisconnectOnInactivityEnabled(false)
to disable the system; call SetDisconnectOnInactivityEnabled(false)
to re-enable it once the operation is complete.
If an application turns off the Disconnect on Inactivity feature, it’s very important to re-enable it again afterwards. Otherwise, the system will be disabled for the entire application lifecycle.
CYICloud::GetInterface().SetDisconnectOnInactivityEnabled(false);
const auto *pProduct = dynamic_cast<const RokuProduct *>(m_products[id].get());
CYICloud::GetInterface().RequestPurchase(pProduct->code, [this](const CloudIAPRequestStatus &status, const CloudIAPReceipt &receipt) {
this->OnRequestPurchaseResponse(status, receipt);
CYICloud::GetInterface().SetDisconnectOnInactivityEnabled(true);
});