在Android Kotlin应用中嵌入的YouTube播放失败,错误码为152-4,尽管onReady已触发

移动开发 2026-07-11

我正在用Kotlin构建一个Android教育应用,课程按课程、年级和章节组织,视频来自YouTube。

为了排查问题,我创建了一个全新的极简Android测试应用。没有后端、没有播放列表逻辑,我使用单个硬编码的视频ID进行测试。

设置

-

Android Studio

  • Kotlin
  • com.pierfrancescosoffritti.androidyoutubeplayer:core
  • 硬编码的视频ID
  • 不涉及后端
  • 不涉及播放列表

发生了什么
播放器成功初始化,onReady() 触发,但 cueVideo(...)loadVideo(...) 都失败。

屏幕上的YouTube错误:152-4

库回调:播放器错误:UNKNOWN

相关日志

A. onCreate entered

1. Activity started. Target Video ID: [rDo-GlvNsUc] (Raw: [rDo-GlvNsUc])

Internal WebView found and settings applied.

B. initialize called

C. initialize returned

Activity onStart

Activity onResume

D. onReady fired

Player width: 2020, height: 1080

E. calling cueVideo with [rDo-GlvNsUc]

F. onError fired: [UNKNOWN]

onVideoId: rDo-GlvNsUc

F. onError fired: [UNKNOWN]

最小代码

youtubePlayerView.addYouTubePlayerListener(object : AbstractYouTubePlayerListener() {
    override fun onReady(youTubePlayer: YouTubePlayer) {
        Log.d("YT", "onReady fired")
        youTubePlayer.cueVideo("rDo-GlvNsUc", 0f)
    }

    override fun onError(youTubePlayer: YouTubePlayer, error: PlayerConstants.PlayerError) {
        Log.d("YT", "onError fired: [$error]")
    }
})

我已经排除了哪些

  • 后端或API问题
  • 无效的视频ID解析
  • 视图大小问题
  • onReady() 之前调用播放器方法
  • 播放列表解析问题
  • 服务器或存储问题
  • 同样的故障也出现在从头创建的一个极简新应用中

问题
在这个Android嵌入式播放场景中,YouTube错误 152-4 表示什么意思?

通常是不是因为以下原因导致的:

  1. 该视频的嵌入被禁用
  2. YouTube在 Android应用或WebView嵌入中对播放的限制更严格
  3. 这个库不再与当前的YouTube嵌入行为可靠地工作

我在努力判断这是一个视频特定的限制、YouTube端的变更,还是Android WebView/库的问题。

如果你愿意,我还可以给你一个更贴合Stack Overflow的版本,听起来更专业一些,不太容易被挑剔。

解决方案

我在使用Jetpack Compose的应用中也遇到了同样的问题。

实际上这篇帖子的答案对我帮助很大 YouTube视频在WebView中在Android上给出错误代码153

我尝试实现那个库的一个包装器,处理这些头部设置,现在152-4已经解决。

@Composable
fun YoutubePlayerView(
    videoId: String,
    modifier: Modifier = Modifier
) {
    val lifecycleOwner = LocalLifecycleOwner.current
    val packageName = "com.example.myapp"  //replace with yours packageName

    AndroidView(
        modifier = modifier.clip(RoundedCornerShape(12.dp)),
        factory = { context ->
            YouTubePlayerView(context).apply {
                enableAutomaticInitialization = false
                lifecycleOwner.lifecycle.addObserver(this)


                val webView = this.getChildAt(0) as? android.webkit.WebView

                webView?.settings?.apply {
                    javaScriptEnabled = true
                    domStorageEnabled = true
                    // just a sample user agent
                    userAgentString = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36"
                }

                //the game changers :)
                val extraHeaders = mapOf(
                    "Referer" to "https://$packageName",
                    "Referrer-Policy" to "strict-origin-when-cross-origin"
                )

                val options = IFramePlayerOptions.Builder()
                    .controls(1)
                    .origin("https://$packageName") 
                    .build()


                initialize(object : AbstractYouTubePlayerListener() {
                    override fun onReady(youTubePlayer: YouTubePlayer) {
                        youTubePlayer.loadVideo(videoId, 0f)
                    }
                }, options)
            }
        },
        onRelease = { it.release() }
    )
}

备选方案

在我的应用中也遇到同样的问题,使用的是相同的依赖。问题不是依赖本身,而是YouTube针对嵌入的新策略。YouTube最近更新了安全策略,要求非常具体的 Referer策略 和有效的 Origin。解决办法?

  1. Meta标签: 在HTML头部添加一个 <meta> 标签以强制引用策略。
  2. IFrame属性: 在iframe中显式添加 referrerpolicy="strict-origin-when-cross-origin"
  3. 真实的基础URL: 不要把 youtube.com 作为基础地址,用你应用的实际网站或一个看起来像真实站点的占位域名。

``` import android.view.ViewGroup import android.webkit.WebChromeClient import android.webkit.WebView import android.webkit.WebViewClient import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView

@Composable fun YouTubeIFramePlayer( videoId: String, modifier: Modifier = Modifier ) {

   val html = """
       <!DOCTYPE html>
       <html>
       <head>
           <meta name="referrer" content="strict-origin-when-cross-origin">
           <style>
               body { margin: 0; padding: 0; background-color: black; }
               .video-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; }
               .video-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0; }
           </style>
       </head>
       <body>
           <div class="video-container">
               <iframe 
                   id="player"
                   src="https://www.youtube.com/embed/$videoId?autoplay=1&modestbranding=1&rel=0&enablejsapi=1" 
                   allow="autoplay; encrypted-media; picture-in-picture" 
                   allowfullscreen
                   /* FIX 2: Explicit Policy on the Iframe itself */
                   referrerpolicy="strict-origin-when-cross-origin">
               </iframe>
           </div>
       </body>
       </html>
   """.trimIndent()

   AndroidView(
       modifier = modifier
           .fillMaxWidth()
           .aspectRatio(16 / 9f),
       factory = { context ->
           WebView(context).apply {
               layoutParams = ViewGroup.LayoutParams(
                   ViewGroup.LayoutParams.MATCH_PARENT,
                   ViewGroup.LayoutParams.MATCH_PARENT
               )

               settings.apply {
                   javaScriptEnabled = true
                   domStorageEnabled = true
                   mediaPlaybackRequiresUserGesture = false
                   useWideViewPort = true
                   loadWithOverviewMode = true
                   // Spoofing a very specific modern Chrome agent
                   userAgentString = "Mozilla/5.0 (Linux; Android 13; Samsung-SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36"
               }

               webViewClient = WebViewClient()
               webChromeClient = WebChromeClient()

               val appBaseUrl = "https://anywebsite.com"

               loadDataWithBaseURL(appBaseUrl, html, "text/html", "UTF-8", null)
           }
       },
       onRelease = { webView ->
           webView.destroy()
       }
   )

} ```

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章