欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在Android上實現(xiàn)視頻播放的多種方案

 更新時間:2025年04月28日 08:41:30   作者:Katie。  
隨著移動互聯(lián)網(wǎng)的發(fā)展,視頻已成為流量最大的媒體形式之一,無論是社交短視頻、在線視頻播放、還是直播推流功能,Android 應用對視頻播放的需求無處不在,本教程將全面介紹在 Android 上實現(xiàn)視頻播放的多種方案,需要的朋友可以參考下

一、項目介紹

1. 背景與意義

隨著移動互聯(lián)網(wǎng)的發(fā)展,視頻已成為流量最大的媒體形式之一。無論是社交短視頻、在線視頻播放、還是直播推流功能,Android 應用對視頻播放的需求無處不在。要實現(xiàn)一個穩(wěn)定、流暢、功能豐富的視頻播放模塊,需要掌握多種底層 API 與第三方框架,才能應對不同網(wǎng)絡、格式、編碼與業(yè)務場景。

本教程將全面介紹在 Android 上實現(xiàn)視頻播放的多種方案,包括:

  1. 系統(tǒng) VideoView:最簡單的 API,快速集成

  2. 原生 MediaPlayer + SurfaceView:更靈活的底層實現(xiàn)

  3. 原生 MediaPlayer + TextureView:支持旋轉(zhuǎn)、縮放等變換

  4. ExoPlayer:Google 推薦,支持 DASH/HLS、緩存、DRM

  5. Media3(Jetpack)**:繼承 ExoPlayer,未來趨勢

  6. 第三方播放器:如 IJKPlayer(FFmpeg)、Vitamio 等

  7. 低層 MediaCodec:自定義解碼管線,適合特殊需求

  8. Compose + AndroidView:在 Jetpack Compose 中集成視頻

通過對比各方案的用法、優(yōu)缺點、適用場景,以及完整的示例代碼,你將能夠根據(jù)項目需求,快速抉擇并集成視頻播放功能。

二、相關(guān)知識

在深入代碼之前,請先了解以下核心概念:

  1. 容器類型

    • SurfaceView:獨立的渲染緩沖區(qū),性能高但不支持普通 View 層級變換。

    • TextureView:在普通 View 層中渲染,支持平移、旋轉(zhuǎn)、縮放,但性能略低。

    • PlayerView / StyledPlayerView:ExoPlayer 提供的封裝視圖。

  2. 播放器 API 層

    • VideoView:封裝了 MediaPlayer + SurfaceView,快速集成但可定制性差。

    • MediaPlayer:Android 原生媒體播放引擎,支持本地與網(wǎng)絡流媒體。

    • ExoPlayer:Google 開源,支持 DASH、HLS、SmoothStreaming、自定義數(shù)據(jù)源。

    • Media3:更高層的 Jetpack 媒體庫,未來推薦。

  3. 流媒體協(xié)議

    • HTTP Progressive:直接下載 MP4、MKV 等文件。

    • HLS (M3U8):通過 #EXTM3U 播放器邊下載邊播放。

    • DASH (MPD):動態(tài)自適應比特率。

  4. DRM 與清晰度切換

    • ExoPlayer 和 Media3 內(nèi)置支持 Widevine、PlayReady 等 DRM。

    • 動態(tài)切換分辨率、碼率,需實現(xiàn) TrackSelector 或 DefaultTrackSelector。

  5. Lifecycle 與回收

    • Activity/Fragment 的 onStart/onStop 或 onResume/onPause 中控制播放器的 play()/pause(),并在銷毀時 release()。

三、實現(xiàn)思路

我們將按以下順序?qū)崿F(xiàn)并對比各方案:

  1. 方案一:VideoView

  2. 方案二:MediaPlayer + SurfaceView

  3. 方案三:MediaPlayer + TextureView

  4. 方案四:ExoPlayer

  5. 方案五:Media3

  6. 方案六:IJKPlayer(FFmpeg)

  7. 方案七:MediaCodec 自解碼

  8. 方案八:Jetpack Compose 集成方案

每個方案都將提供:

  • 布局示例

  • Activity/Fragment 代碼

  • 生命周期管理

  • 錯誤處理與回調(diào)

最后,我們將總結(jié)各方案優(yōu)缺點,并給出不同場景的最佳實踐建議。

四、環(huán)境與依賴

// app/build.gradle
plugins {
  id 'com.android.application'
  id 'kotlin-android'
}
 
android {
  compileSdkVersion 34
  defaultConfig {
    applicationId "com.example.videoplaydemo"
    minSdkVersion 21
    targetSdkVersion 34
  }
  buildFeatures { viewBinding true }
  kotlinOptions { jvmTarget = "1.8" }
}
 
dependencies {
  // ExoPlayer
  implementation 'com.google.android.exoplayer:exoplayer:2.18.2'
  // Media3
  implementation "androidx.media3:media3-exoplayer:1.0.0"
  implementation "androidx.media3:media3-ui:1.0.0"
 
  // IJKPlayer
  implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
  implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
 
  // Compose (for Compose 方案)
  implementation "androidx.compose.ui:ui:1.4.0"
  implementation "androidx.compose.material:material:1.4.0"
  implementation "androidx.activity:activity-compose:1.7.0"
}

五、整合代碼

// =======================================================
// 文件: res/layout/activity_main.xml
// 描述: 簡單導航,選擇不同播放方案
// =======================================================
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:padding="16dp"
    android:layout_width="match_parent" android:layout_height="match_parent">
  <Button android:id="@+id/btnVideoView" android:text="VideoView 方案"/>
  <Button android:id="@+id/btnSurface"   android:text="MediaPlayer+SurfaceView"/>
  <Button android:id="@+id/btnTexture"   android:text="MediaPlayer+TextureView"/>
  <Button android:id="@+id/btnExo"       android:text="ExoPlayer 方案"/>
  <Button android:id="@+id/btnMedia3"    android:text="Media3 方案"/>
  <Button android:id="@+id/btnIJK"       android:text="IJKPlayer 方案"/>
  <Button android:id="@+id/btnCodec"     android:text="MediaCodec 自解碼"/>
  <Button android:id="@+id/btnCompose"   android:text="Compose 集成方案"/>
</LinearLayout>
 
// =======================================================
// 文件: MainActivity.kt
// 描述: 跳轉(zhuǎn)到各個示例 Activity
// =======================================================
package com.example.videoplaydemo
 
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.videoplaydemo.databinding.ActivityMainBinding
 
class MainActivity : AppCompatActivity() {
  private lateinit var binding: ActivityMainBinding
  override fun onCreate(s: Bundle?) {
    super.onCreate(s)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
 
    binding.btnVideoView  .setOnClickListener { startActivity(Intent(this, VideoViewActivity::class.java)) }
    binding.btnSurface    .setOnClickListener { startActivity(Intent(this, SurfaceActivity::class.java)) }
    binding.btnTexture    .setOnClickListener { startActivity(Intent(this, TextureActivity::class.java)) }
    binding.btnExo        .setOnClickListener { startActivity(Intent(this, ExoActivity::class.java)) }
    binding.btnMedia3     .setOnClickListener { startActivity(Intent(this, Media3Activity::class.java)) }
    binding.btnIJK        .setOnClickListener { startActivity(Intent(this, IjkActivity::class.java)) }
    binding.btnCodec      .setOnClickListener { startActivity(Intent(this, CodecActivity::class.java)) }
    binding.btnCompose    .setOnClickListener { startActivity(Intent(this, ComposeActivity::class.java)) }
  }
}
 
// =======================================================
// 方案一:VideoViewActivity.kt
// Layout: res/layout/activity_video_view.xml
// =======================================================
// activity_video_view.xml
/*
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">
  <VideoView
      android:id="@+id/videoView"
      android:layout_width="match_parent" android:layout_height="match_parent"/>
  <ProgressBar android:id="@+id/progress"
      style="?android:attr/progressBarStyleLarge"
      android:layout_gravity="center"/>
</FrameLayout>
*/
// VideoViewActivity.kt
package com.example.videoplaydemo
import android.net.Uri
import android.os.Bundle
import android.widget.MediaController
import androidx.appcompat.app.AppCompatActivity
import com.example.videoplaydemo.databinding.ActivityVideoViewBinding
class VideoViewActivity : AppCompatActivity() {
  private lateinit var binding: ActivityVideoViewBinding
  override fun onCreate(s: Bundle?) {
    super.onCreate(s)
    binding = ActivityVideoViewBinding.inflate(layoutInflater)
    setContentView(binding.root)
    val uri = Uri.parse("https://www.example.com/video.mp4")
    binding.progress.show()
    binding.videoView.setVideoURI(uri)
    binding.videoView.setMediaController(MediaController(this))
    binding.videoView.setOnPreparedListener {
      binding.progress.hide()
      it.isLooping = true
      binding.videoView.start()
    }
  }
  override fun onPause(){ super.onPause(); binding.videoView.pause() }
  override fun onResume(){ super.onResume(); binding.videoView.start() }
  override fun onDestroy(){ super.onDestroy(); binding.videoView.stopPlayback() }
}
 
// =======================================================
// 方案二:SurfaceView + MediaPlayer
// File: res/layout/activity_surface.xml
// =======================================================
/*
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout ...>
  <SurfaceView android:id="@+id/surfaceView" .../>
  <ProgressBar android:id="@+id/progress" .../>
</FrameLayout>
*/
// SurfaceActivity.kt
package com.example.videoplaydemo
import android.media.MediaPlayer
import android.os.Bundle
import android.view.SurfaceHolder
import androidx.appcompat.app.AppCompatActivity
import com.example.videoplaydemo.databinding.ActivitySurfaceBinding
class SurfaceActivity: AppCompatActivity(), SurfaceHolder.Callback {
  private lateinit var binding: ActivitySurfaceBinding
  private var player: MediaPlayer? = null
  override fun onCreate(s: Bundle?){ super.onCreate(s)
    binding = ActivitySurfaceBinding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.surfaceView.holder.addCallback(this)
  }
  override fun surfaceCreated(holder: SurfaceHolder) {
    player = MediaPlayer().apply {
      setDataSource("https://.../video.mp4")
      setDisplay(holder)
      setOnPreparedListener {
        binding.progress.hide()
        isLooping = true; start()
      }
      prepareAsync()
    }
  }
  override fun surfaceDestroyed(holder: SurfaceHolder) {
    player?.release(); player = null
  }
  override fun surfaceChanged(h: SurfaceHolder, f:Int, w:Int, h2:Int){}
}
 
// =======================================================
// 方案三:TextureView + MediaPlayer
// File: res/layout/activity_texture.xml
// =======================================================
/*
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout ...>
  <TextureView android:id="@+id/textureView" .../>
  <ProgressBar android:id="@+id/progress" .../>
</FrameLayout>
*/
// TextureActivity.kt
package com.example.videoplaydemo
import android.graphics.SurfaceTexture
import android.media.MediaPlayer
import android.os.Bundle
import android.view.TextureView
import androidx.appcompat.app.AppCompatActivity
import com.example.videoplaydemo.databinding.ActivityTextureBinding
class TextureActivity: AppCompatActivity(), TextureView.SurfaceTextureListener {
  private lateinit var binding: ActivityTextureBinding
  private var player: MediaPlayer? = null
  override fun onCreate(s: Bundle?){ super.onCreate(s)
    binding = ActivityTextureBinding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.textureView.surfaceTextureListener = this
  }
  override fun onSurfaceTextureAvailable(st: SurfaceTexture, w:Int, h:Int){
    player = MediaPlayer().apply {
      setSurface(android.view.Surface(st))
      setDataSource("https://.../video.mp4")
      setOnPreparedListener {
        binding.progress.hide()
        isLooping=true; start()
      }
      prepareAsync()
    }
  }
  override fun onSurfaceTextureSizeChanged(st:SurfaceTexture,w:Int,h:Int){}
  override fun onSurfaceTextureDestroyed(st:SurfaceTexture):Boolean{ player?.release(); player=null; return true }
  override fun onSurfaceTextureUpdated(st:SurfaceTexture){}
}
 
// =======================================================
// 方案四:ExoPlayer
// File: res/layout/activity_exo.xml
// =======================================================
/*
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.exoplayer2.ui.PlayerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/playerView" .../>
*/
// ExoActivity.kt
package com.example.videoplaydemo
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.videoplaydemo.databinding.ActivityExoBinding
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
class ExoActivity: AppCompatActivity() {
  private lateinit var binding: ActivityExoBinding
  private var player: ExoPlayer? = null
  override fun onCreate(s: Bundle?){ super.onCreate(s)
    binding = ActivityExoBinding.inflate(layoutInflater)
    setContentView(binding.root)
    player = ExoPlayer.Builder(this).build().also {
      binding.playerView.player = it
      val mediaItem = MediaItem.fromUri(Uri.parse("https://.../video.mp4"))
      it.setMediaItem(mediaItem); it.repeatMode = ExoPlayer.REPEAT_MODE_ALL
      it.prepare(); it.play()
    }
  }
  override fun onPause(){ super.onPause(); player?.pause() }
  override fun onResume(){ super.onResume(); player?.play() }
  override fun onDestroy(){ super.onDestroy(); player?.release(); player=null }
}
 
// =======================================================
// 方案五:Media3 (Jetpack)
// File: res/layout/activity_media3.xml
// =======================================================
/*
<?xml version="1.0" encoding="utf-8"?>
<androidx.media3.ui.PlayerView ... android:id="@+id/playerView"/>
*/
// Media3Activity.kt
package com.example.videoplaydemo
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import com.example.videoplaydemo.databinding.ActivityMedia3Binding
class Media3Activity: AppCompatActivity() {
  private lateinit var binding: ActivityMedia3Binding
  private var player: ExoPlayer? = null
  override fun onCreate(s: Bundle?){ super.onCreate(s)
    binding = ActivityMedia3Binding.inflate(layoutInflater)
    setContentView(binding.root)
    player = ExoPlayer.Builder(this).build().apply {
      setMediaItem(MediaItem.fromUri(Uri.parse("https://.../video.mp4")))
      repeatMode = ExoPlayer.REPEAT_MODE_ALL; prepare(); play()
    }
    binding.playerView.player = player
  }
  override fun onPause(){ super.onPause(); player?.pause() }
  override fun onResume(){ super.onResume(); player?.play() }
  override fun onDestroy(){ super.onDestroy(); player?.release(); player=null }
}
 
// =======================================================
// 方案六:IJKPlayer
// File: res/layout/activity_ijk.xml
// =======================================================
/*
<?xml version="1.0" encoding="utf-8"?>
<tv.danmaku.ijk.media.player.IjkVideoView ... android:id="@+id/ijkView"/>
*/
// IjkActivity.kt
package com.example.videoplaydemo
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import tv.danmaku.ijk.media.player.IjkMediaPlayer
import com.example.videoplaydemo.databinding.ActivityIjkBinding
class IjkActivity: AppCompatActivity() {
  private lateinit var binding: ActivityIjkBinding
  override fun onCreate(s: Bundle?){ super.onCreate(s)
    binding = ActivityIjkBinding.inflate(layoutInflater)
    setContentView(binding.root)
    IjkMediaPlayer.loadLibrariesOnce(null); IjkMediaPlayer.native_profileBegin("libijkplayer.so")
    binding.ijkView.setVideoPath("https://.../video.mp4")
    binding.ijkView.start()
  }
  override fun onDestroy(){ super.onDestroy()
    binding.ijkView.stopPlayback()
    IjkMediaPlayer.native_profileEnd()
  }
}
 
// =======================================================
// 方案七:MediaCodec 自解碼(略示意)
// File: CodecActivity.kt
// =======================================================
// 此處省略數(shù)百行自解碼代碼,僅做簡要示意:
// - 使用 MediaExtractor 分離軌道  
// - 用 MediaCodec 解碼到 Surface  
// - 用 SurfaceView / TextureView 渲染  
// 建議查閱官方文檔與 Codelab 深入實現(xiàn)。
 
// =======================================================
// 方案八:Compose 集成
// File: ComposeActivity.kt
// =======================================================
package com.example.videoplaydemo
import android.net.Uri
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
class ComposeActivity: AppCompatActivity() {
  override fun onCreate(s: Bundle?){ super.onCreate(s)
    val player = ExoPlayer.Builder(this).build().apply {
      setMediaItem(MediaItem.fromUri(Uri.parse("https://.../video.mp4")))
      prepare(); play()
    }
    setContent {
      Box(Modifier.fillMaxSize()) {
        AndroidView(factory = { ctx ->
          PlayerView(ctx).apply {
            this.player = player; useController=true
          }
        }, modifier=Modifier.fillMaxSize())
      }
    }
  }
  override fun onDestroy(){ super.onDestroy()
    player.release()
  }
}

六、代碼解讀

  1. VideoView

    • 簡單易用,封裝度高;

    • 無法控制底層緩沖或自定義渲染;

  2. MediaPlayer + SurfaceView

    • 適合大批量視頻或直播;

    • 性能高,但不支持 View 變換;

  3. MediaPlayer + TextureView

    • 支持任意 2D 變換(旋轉(zhuǎn)、縮放);

    • 性能次于 SurfaceView;

  4. ExoPlayer

    • 支持 DASH、HLS、自定義加載;

    • 擁有豐富擴展(緩存、DRM、字幕);

  5. Media3

    • Jetpack 新推薦,兼容未來更新;

    • API 與 ExoPlayer 基本一致;

  6. IJKPlayer

    • 基于 FFmpeg,支持更多格式;

    • 需部署 native 庫,包體大;

  7. MediaCodec

    • 最低層控制,適合自定義渲染或特殊解碼需求;

    • 開發(fā)成本高;

  8. Compose 集成

    • 在 Compose 中可使用 AndroidView 嵌入任意 View;

    • 未來可期待原生 Compose 視頻組件;

七、性能與優(yōu)化

  1. 硬件加速

    • SurfaceView 與 ExoPlayer 默認硬件加速;

  2. 網(wǎng)絡緩沖

    • ExoPlayer 可自定義 LoadControl;

  3. 并發(fā)與切換

    • 避免頻繁 prepare()/release();

  4. 內(nèi)存管理

    • 及時 release() 資源,避免泄漏;

  5. UI 與渲染

    • 避免在主線程做 heavy UI 操作;

八、項目總結(jié)與拓展

本文多角度、全方案地介紹了 Android 上幾乎所有主流的視頻播放實現(xiàn)方式,配以示例代碼與優(yōu)缺點對比,便于在不同業(yè)務場景中做出選擇。未來可擴展:

  • 自適應碼率:HLS/DASH 動態(tài)切換

  • DRM:Protected clearplay

  • 節(jié)省流量:集成緩存、預下載

  • UI 特效:濾鏡、彈幕、畫中畫

九、FAQ

Q1:哪種方案最簡單?

A:VideoView,但可定制性最低。

Q2:推薦使用哪個?

A:ExoPlayer/Media3,功能最全,社區(qū)活躍。

Q3:如何播放直播 HLS?

A:ExoPlayer 直接 MediaItem.fromUri("https://.../live.m3u8") 即可。

Q4:IJKPlayer 包體大怎么辦?

A:可定制 native 庫,只打包需要的 ABI。

Q5:Compose 未來會有原生視頻組件嗎?

A:已在開發(fā)中,但目前仍需 AndroidView 嵌入。

以上就是在Android上實現(xiàn)視頻播放的多種方案的詳細內(nèi)容,更多關(guān)于Android視頻播放的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • android實現(xiàn)常駐通知欄遇到的問題及解決辦法

    android實現(xiàn)常駐通知欄遇到的問題及解決辦法

    這篇文章主要介紹了android實現(xiàn)常駐通知欄遇到的問題及解決辦法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-06-06
  • Android 百度地圖Sha1獲取的方法

    Android 百度地圖Sha1獲取的方法

    這篇文章主要介紹了Android 百度地圖Sha1獲取的方法的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Android添加ButterKnife時報錯Error:(2, 0) Cannot add extension with name ''android''的解決辦法

    Android添加ButterKnife時報錯Error:(2, 0) Cannot add extension wit

    今天小編就為大家分享一篇關(guān)于Android添加ButterKnife時報錯Error:(2, 0) Cannot add extension with name 'android'的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • 解決Android MediaRecorder錄制視頻過短問題

    解決Android MediaRecorder錄制視頻過短問題

    本文主要介紹Android MediaRecorder,在使用MediaRecorder時經(jīng)常會遇到視頻錄制太短問題,這里提供解決問題的實例代碼以供大家參考
    2016-07-07
  • 最新評論