PageSourceSearch

https://bambuser.com/docs/assets/js/4ed60963.68f35569.js

js bambuser.com collected 2026-09-24 08:31:20 UTC 10,245 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkbambuser_docs||=[]).push([[5104],{55491(e,t,n){n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>s,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"mobileSDK/android/vc-android-multi-activity-retention","title":"Android SDK \u2014 Multi-Activity retention","description":"Keep a live call running when the shopper navigates between Activities. Opt-in retainWebViewAcrossActivities flag plus an Application-scoped singleton controller.","source":"@site/video-consultation/mobileSDK/android/multi-activity-retention.mdx","sourceDirName":"mobileSDK/android","slug":"/mobileSDK/android/mobile-sdk/android/multi-activity-retention","permalink":"/docs/video-consultation/mobileSDK/android/mobile-sdk/android/multi-activity-retention","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"vc-android-multi-activity-retention","title":"Android SDK \u2014 Multi-Activity retention","description":"Keep a live call running when the shopper navigates between Activities. Opt-in retainWebViewAcrossActivities flag plus an Application-scoped singleton controller.","sidebar_label":"Multi-Activity retention","slug":"mobile-sdk/android/multi-activity-retention"},"sidebar":"someSidebars","previous":{"title":"Co-browse","permalink":"/docs/video-consultation/mobileSDK/android/mobile-sdk/android/co-browse"},"next":{"title":"Connect Booking System","permalink":"/docs/video-consultation/how-to-integrate-your-booking-system"}}');var r=n(74848),o=n(28453);const s={id:"vc-android-multi-activity-retention",title:"Android SDK \u2014 Multi-Activity retention",description:"Keep a live call running when the shopper navigates between Activities. Opt-in retainWebViewAcrossActivities flag plus an Application-scoped singleton controller.",sidebar_label:"Multi-Activity retention",slug:"mobile-sdk/android/multi-activity-retention"},a="Multi-Activity retention",l={},c=[{value:"Enable retention",id:"enable-retention",level:2},{value:"Requirement: share one controller across Activities",id:"requirement-share-one-controller-across-activities",level:2},{value:"What happens on background",id:"what-happens-on-background",level:2},{value:"Overlay behaviour when retention is on",id:"overlay-behaviour-when-retention-is-on",level:2},{value:"Starting a different call after retention",id:"starting-a-different-call-after-retention",level:2},{value:"Handling renderer crashes",id:"handling-renderer-crashes",level:2},{value:"Trade-offs",id:"trade-offs",level:2}];function d(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(t.header,{children:(0,r.jsx)(t.h1,{id:"multi-activity-retention",children:"Multi-Activity retention"})}),"\n",(0,r.jsxs)(t.p,{children:["By default ",(0,r.jsx)(t.code,{children:"BambuserCallOverlay"})," ends the call when its hosting Activity is destroyed \u2014 the ",(0,r.jsx)(t.code,{children:"WebView"}),", the WebRTC media session, and the widget state all go with it. Single-Activity Compose hosts see no issue."]}),"\n",(0,r.jsxs)(t.p,{children:["Apps that navigate between multiple Activities (e.g. a shopper starts a call on ",(0,r.jsx)(t.code,{children:"MainActivity"})," and taps into ",(0,r.jsx)(t.code,{children:"ProductDetailActivity"})," mid-call) need the SDK to keep the same ",(0,r.jsx)(t.code,{children:"WebView"})," alive across that transition. Enable it with a single flag."]}),"\n",(0,r.jsx)(t.h2,{id:"enable-retention",children:"Enable retention"}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-kotlin",children:'BambuserCallController(\n    configuration = BambuserCallConfiguration(\n        orgId = "YOUR_ORG_ID",\n        environment = BambuserEnvironment.US,\n        retainWebViewAcrossActivities = true,\n    )\n)\n'})}),"\n",(0,r.jsxs)(t.p,{children:["When retention is on, the SDK reparents the same ",(0,r.jsx)(t.code,{children:"WebView"})," between the two Activities' ",(0,r.jsx)(t.code,{children:"BambuserCallOverlay"})," mounts. The audio/video session, chat, and cart state all survive the navigation."]}),"\n",(0,r.jsx)(t.h2,{id:"requirement-share-one-controller-across-activities",children:"Requirement: share one controller across Activities"}),"\n",(0,r.jsxs)(t.p,{children:["Retention only works if both Activities host the ",(0,r.jsx)(t.strong,{children:"same controller instance"}),". The Application is the natural scope. Hilt users can do this with ",(0,r.jsx)(t.code,{children:"@Singleton"}),"; a plain Application field is fine too:"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-kotlin",children:'class DemoApplication : Application() {\
1n    val bambuserCall: BambuserCallController by lazy {\n        BambuserCallController(\n            configuration = BambuserCallConfiguration(\n                orgId = "YOUR_ORG_ID",\n                environment = BambuserEnvironment.US,\n                retainWebViewAcrossActivities = true,\n            )\n        )\n    }\n}\n'})}),"\n",(0,r.jsx)(t.p,{children:"Every Activity that mounts the overlay reads that same controller:"}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-kotlin",children:"class MainActivity : ComponentActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        val bambuserCall = (application as DemoApplication).bambuserCall\n        setContent {\n            BambuserCallOverlay(controller = bambuserCall)\n            // \u2026your screen\u2026\n        }\n    }\n}\n"})}),"\n",(0,r.jsxs)(t.p,{children:["Ship the SDK with ",(0,r.jsx)(t.code,{children:"retainWebViewAcrossActivities = true"})," but with a controller that lives on an Activity (rather than Application) and retention is a no-op \u2014 the controller dies with the Activity and the retained state goes with it. The SDK logs a warning at controller construction to flag this."]}),"\n",(0,r.jsx)(t.h2,{id:"what-happens-on-background",children:"What happens on background"}),"\n",(0,r.jsxs)(t.p,{children:["When the whole app goes into the background with no overlay composed ",(0,r.jsx)(t.strong,{children:"and"})," retention is on, the SDK starts a 30-second grace timer. If any Activity resumes in time, the timer cancels and the call resumes. If the app stays in the background past 30 seconds, the retained ",(0,r.jsx)(t.code,{children:"WebView"})," is torn down to release the Chromium renderer \u2014 otherwise a warm-but-hidden app would hold the renderer indefinitely."]}),"\n",(0,r.jsx)(t.p,{children:"The grace period is fixed at 30 seconds and not currently configurable."}),"\n",(0,r.jsx)(t.h2,{id:"overlay-behaviour-when-retention-is-on",children:"Overlay behaviour when retention is on"}),"\n",(0,r.jsxs)(t.p,{children:["While the shopper is transitioning between two Activities, both Activities briefly hold a ",(0,r.jsx)(t.code,{children:"BambuserCallOverlay"}),". To avoid a flash of grey placeholder in the paused Activity's slot, the overlay only composes when its host is at least ",(0,r.jsx)(t.code,{children:"RESUMED"}),":"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-kotlin",children:"if (configuration.retainWebViewAcrossActivities) {\n    val state by LocalLifecycleOwner.current.lifecycle.currentStateAsState()\n    if (!state.isAtLeast(Lifecycle.State.RESUMED)) return\n}\n"})}),"\n",(0,r.jsx)(t.p,{children:"You don't need to write anything for this \u2014 the SDK does it internally. It only affects builds with retention enabled; unaffected apps keep their original composition behaviour."}),"\n",(0,r.jsx)(t.h2,{id:"starting-a-different-call-after-retention",children:"Starting a different call after retention"}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.code,{children:"show(orgId, connectId, queue)"})," on a controller with a retained ",(0,r.jsx)(t.code,{children:"WebView"}),' tears down the retained one if any of the three arguments differ from the previous call. Prevents "shopper sees the previous call\'s widget" when the host reuses the singleton for a different call target.']}),"\n",(0,r.jsx)(t.h2,{id:"handling-renderer-crashes",children:"Handling renderer crashes"}),"\n",(0,r.jsxs)(t.p,{children:["Even with retention on, Android may reclaim the Chromium renderer under memory pressure. Handle ",(0,r.jsx)(t.code,{children:"BambuserCallEvent.RenderProcessGone(didCrash)"})," in your delegate \u2014 see ",(0,r.jsx)(t.a,{href:"/docs/video-consultation/mobileSDK/android/mobile-sdk/android/integration",children:"Integration \u203a Wire delegate + handlers"}),"."]}),"\n",(0,r.jsx)(t.h2,{id:"trade-offs",children:"Trade-offs"}),"\n",(0,r.jsxs)(t.ul,{children:["\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.strong,{children:"Memory:"})," the retained ",(0,r.jsx)(t.code,{children:"WebView"})," holds a Chromium renderer and a WebRTC session across the entire app foreground lifetime. Only enable if your app genuinely navigates between Activities during a call \u2014 single-Activity Compose hosts don't need this."]}),"\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.strong,{children:"Debug builds:"})," hot reload / process restart tears down the retained state along with everything else. That's expected."]}),"\n",(0,r.jsxs)(t.li,{children:[(0,r.jsxs)(t.strong,{children:["Not compatible with ",(0,r.jsx)(t.code,{children:"Activity.finish()"})," from the call host:"]})," if the Activity holding the currently-mounted overlay finishes and no other overlay is composed within the same frame, the retained ",(0,r.jsx)(t.code,{children:"WebView"})," has nowhere to live and the call ends."]}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,o.R)(),...e.components};return t?(0,r.jsx)(t,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},28453(e,t,n){n.d(t,{R:()=>s,x:()=>a});var i=n(96540);const r={},o=i.createContext(r);function s(e){const t=i.useContext(o);return i.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:s(e.components),i.createElement(o.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.