Skip to main content

Implementing the AnyClip Player in a WebView (with Preloading)

Updated over 2 weeks ago

This article explains how to implement the AnyClip Player inside a WebView for in‑app experiences, using preloading to make transitions feel instant. You’ll find platform‑specific examples for iOS and Android that you can adapt to your app structure.

Note: If you’re embedding an entire Watch experience, see the companion WebView article for Watch, which covers general embed and page requirements. AnyClip Help Center


Before You Start

  • Have your Player or Watch URL ready. You’ll preload that URL so content is cached and ready when the screen appears.

  • Use an embed‑ready page. For Watch embeds, ensure your page includes a responsive viewport meta tag to optimize mobile rendering (example shown in the Watch Embed article). AnyClip Help Center

  • Handle media policies. Allow inline playback and configure gesture requirements appropriately (both samples below demonstrate the correct flags).


Why Preloading?

Preloading creates and prepares the WebView before the user navigates to it. When the user switches tabs or opens the relevant screen, the content is already loaded, reducing perceived latency and improving UX—especially on slower networks.


iOS: WebView Preloading in a Tabbed App

Approach: Before displaying the ViewController in the UITabBarController, create and preload the WKWebView in the UITabBarController. Call webView.load with your current URL there, then pass the preloaded WebView to the tab’s ViewController that will display it.

Sample:

let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
let webView = WKWebView(frame: .zero, configuration: configuration)
if #available(iOS 16.4, *) {
webView.isInspectable = true
}
configuration.userContentController.add(watchProductVC, name: "openLink")
webView.load(NSURLRequest(url: NSURL(string: "https://demo.anyclip.com/")! as URL ) as URLRequest)
watchProductVC.webView = webView

How It Works

  • Create once, reuse across tabs. Construct the WKWebView in the tab controller so it survives tab switches.

  • Inline playback: allowsInlineMediaPlayback = true ensures video plays in place instead of forcing fullscreen.

  • Preload: webView.load(...) runs early, so the first render in your tab is instantaneous.

  • Messaging: userContentController.add(..., name: "openLink") wires native handling for in‑page actions such as link taps or deep links.

  • Debugging (iOS 16.4+): isInspectable = true helps you debug WebView content via Web Inspector.

Integration Tips

  • View lifecycle: Add the preloaded WebView to the view hierarchy in viewDidLoad / viewWillAppear, and remove it appropriately if you need to reclaim memory.

  • Navigation delegation: If you need to intercept URL changes (e.g., to open external links natively), implement WKNavigationDelegate and WKUIDelegate.

  • Memory: If your app hosts multiple heavy WebViews, consider a small shared pool and dispose of the least‑recently used instance when memory warnings occur.


Android: WebView Preloading in an Activity + Fragment

Approach: Initialize and store the preloaded WebView before the Fragment is created, then attach/detach that instance as the Fragment appears or is destroyed.

Note: While you can hold a WebView in a ViewModel, it breaks MVVM principles because ViewModel should not hold UI references. If you do it, be extremely careful with lifecycle and memory management. The implementation below stores the WebView in the Activity’s companion object and exposes getters for the Fragment.

PlayerActivity:

import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.webkit.CookieManager
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
import com.anyclip.sample.databinding.ActivityPlayerBinding
import com.anyclip.sample.webview.WebViewClient


class PlayerActivity : AppCompatActivity() {
private lateinit var binding: ActivityPlayerBinding


companion object {
private var watchProductWebView: WebView? = null
private var fullScreenWebView: WebView? = null


fun getWatchProductWebView(context: Context): WebView? {
val url = SettingsManager.resourcesUrl
if (watchProductWebView == null && url != null) {
watchProductWebView = initWebView(url, context)
}
return watchProductWebView
}


fun getFullScreenWebView(context: Context): WebView? {
val url = SettingsManager.fullScreenWebViewUrl
if (fullScreenWebView == null && url != null) {
fullScreenWebView = initWebView(url, context)
}
return fullScreenWebView
}


@SuppressLint("SetJavaScriptEnabled")
private fun initWebView(link: String, context: Context): WebView {
return WebView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
CookieManager.getInstance()
.setAcceptThirdPartyCookies(this, true)
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.mediaPlaybackRequiresUserGesture = true


webViewClient =
WebViewClient(context)
setLayerType(View.LAYER_TYPE_HARDWARE, null)
loadUrl(link)
}
}
}


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPlayerBinding.inflate(layoutInflater)
setContentView(binding.root)
}


override fun onResume() {
super.onResume()
getFullScreenWebView(this)
getWatchProductWebView(this)
}


override fun onPause() {
super.onPause()
watchProductWebView = null
fullScreenWebView = null
}


override fun onUserLeaveHint() {
super.onUserLeaveHint()
PlayerFragment.getInstance().onUserLeaveHint()
}
}

WatchProductFragment:

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.anyclip.sample.databinding.FragmentWatchProductBinding


class WatchProductFragment : Fragment(R.layout.fragment_watch_product) {
private lateinit var binding: FragmentWatchProductBinding


override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = FragmentWatchProductBinding.inflate(inflater, container, false)
return binding.root
}


override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addWebView()
}


override fun onDestroyView() {
super.onDestroyView()
removeWebView()
}


private fun addWebView() {
val webView = PlayerActivity.getWatchProductWebView(requireContext())
webView?.let {
binding.root.addView(it)
}
}


private fun removeWebView() {
val webView = PlayerActivity.getWatchProductWebView(requireContext())
webView?.let {
(it.parent as ViewGroup?)?.removeView(it)
}
}
}

How It Works

  • Initialize early in the Activity. getWatchProductWebView() and getFullScreenWebView() lazily create and load WebViews so they’re warm when the Fragment attaches them.

  • Attach/detach in the Fragment. addWebView() and removeWebView() move the preloaded instance in and out of the view hierarchy as the Fragment’s view is created/destroyed.

  • Playback & storage: JavaScript and DOM storage are enabled; mediaPlaybackRequiresUserGesture = true ensures compliance with Android’s media policies.

  • Performance: Hardware acceleration (setLayerType(View.LAYER_TYPE_HARDWARE, null)) keeps rendering smooth.

  • Cookies: Third‑party cookies are allowed to support playback and analytics scenarios that rely on them.

Integration Tips

  • Lifecycle ownership: Because the WebView lives outside the Fragment, make sure you clear or recreate it if the Activity is destroyed.

  • Multiple screens: If you maintain two WebViews (e.g., a “watch” and a dedicated fullscreen instance), preload both in onResume() so either is ready on demand.

  • Memory: On low‑end devices, consider tearing down the least‑used WebView to reduce RAM pressure.


Testing Checklist

  • The target URL renders correctly inside the WebView.

  • Videos play inline (no unexpected fullscreen jumps) and respect your gesture policy.

  • Switching tabs or opening the player screen after app launch feels instant thanks to preloading.

  • External links, deep links, and share actions behave as expected (handled by native code where needed).


Related Articles

  • Embedding the AnyClip Watch in an App WebView Environment — baseline WebView setup and Watch‑specific considerations. AnyClip Help Center

  • Watch Embed — reference embed code and mobile viewport guidance. AnyClip Help Center

Did this answer your question?