目录
-
最简单的方式:使用
VideoView(适合快速开发)
(图片来源网络,侵删)- 简介
- 布局文件 (
activity_main.xml) - Activity 代码 (
MainActivity.kt) - 重要:网络权限配置
- 优缺点分析
-
最专业的方式:使用
ExoPlayer(Google 官方推荐)- 简介
- 添加依赖
- 布局文件 (
activity_main.xml) - Activity 代码 (
MainActivity.kt) - ExoPlayer 核心概念
- 优缺点分析
-
进阶与最佳实践
- 横竖屏切换与全屏
- 处理网络错误和加载状态
- 自定义 UI 控件
- 硬解码与性能优化
- 使用缓存库 (如
AndroidVideoCache)
最简单的方式:使用 VideoView
VideoView 是 Android SDK 提供的一个高级组件,它封装了 MediaPlayer,使用起来非常简单,适合播放简单的、不需要复杂控制的视频。
简介
VideoView 负责显示视频,而 MediaController 是一个标准的控制器,提供了播放/暂停、进度条、快进快退等控件。

布局文件 (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<VideoView
android:id="@+id/videoView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</RelativeLayout>
Activity 代码 (MainActivity.kt)
import android.net.Uri
import android.os.Bundle
import android.widget.MediaController
import android.widget.VideoView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val videoView = findViewById<VideoView>(R.id.videoView)
// 1. 设置视频源的 URL
// 注意:请替换为你的实际视频 URL
val videoUrl = "https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4"
// 2. 创建 MediaController 并附加到 VideoView
val mediaController = MediaController(this)
mediaController.setAnchorView(videoView)
videoView.setMediaController(mediaController)
// 3. 设置视频 URI
videoView.setVideoURI(Uri.parse(videoUrl))
// 4. 开始播放
videoView.start()
// 可选:监听播放完成事件
videoView.setOnCompletionListener {
// 播放完成后的操作
}
}
}
重要:网络权限配置
在 AndroidManifest.xml 中必须添加网络访问权限,否则无法播放网络视频。
<manifest ...>
<!-- 必须添加的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<application ...>
...
</application>
</manifest>
优缺点分析
- 优点:
- 简单易用: 几行代码就能实现播放功能。
- 自带控制器:
MediaController提供了基本的播放控制。
- 缺点:
- 功能有限: 无法实现高度自定义的 UI。
- 性能一般: 在处理高分辨率或复杂格式的视频时,性能可能不如
ExoPlayer。 - 扩展性差: 难以添加高级功能,如画中画、多清晰度切换等。
最专业的方式:使用 ExoPlayer
ExoPlayer 是 Google 官方推出的媒体播放器,它是基于 MediaPlayer 和 MediaCodec 构建的,它功能强大、性能优异、高度可定制,是现代 Android 应用的首选。
简介
ExoPlayer 采用了组件化的设计,核心播放器只负责播放逻辑,而渲染、控制、数据加载等功能都由不同的组件提供,开发者可以根据需要自由组合。
添加依赖
在 app/build.gradle 文件的 dependencies 代码块中添加 ExoPlayer 依赖。

dependencies {
// ExoPlayer 核心库
implementation "androidx.media3:media3-exoplayer:1.3.1"
// ExoPlayer 媒体数据加载器
implementation "androidx.media3:media3-exoplayer-hls:1.3.1" // 如果播放 HLS (m3u8) 格式视频,需要这个
// ExoPlayer UI 组件
implementation "androidx.media3:media3-ui:1.3.1"
}
注意: HLS 是一种常见的网络视频流协议(常用于直播和点播),如果你的视频是
.m3u8结尾的,必须添加media3-exoplayer-hls依赖。
布局文件 (activity_main.xml)
ExoPlayer 提供了一个 PlayerView 组件,它集成了 Surface(用于渲染视频)和一套标准的播放控制 UI。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:use_controller="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Activity 代码 (MainActivity.kt)
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.ui.PlayerView
class MainActivity : AppCompatActivity() {
private lateinit var player: ExoPlayer
private lateinit var playerView: PlayerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
playerView = findViewById(R.id.player_view)
// 1. 创建 ExoPlayer 实例
player = ExoPlayer.Builder(this).build().also { exoPlayer ->
// 2. 将 PlayerView 的播放器设置为我们创建的 ExoPlayer
playerView.player = exoPlayer
// 3. 创建 MediaItem 并设置给播放器
// 注意:请替换为你的实际视频 URL
val mediaItem = MediaItem.fromUri("https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4")
exoPlayer.setMediaItem(mediaItem)
// 4. 准备并播放
exoPlayer.prepare()
exoPlayer.play()
}
// 可选:监听播放状态变化
player.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
when (playbackState) {
Player.STATE_BUFFERING -> {
// 正在缓冲,可以显示加载动画
findViewById<View>(R.id.loading_indicator).visibility = View.VISIBLE
}
Player.STATE_READY -> {
// 准备就绪,可以隐藏加载动画
findViewById<View>(R.id.loading_indicator).visibility = View.GONE
}
Player.STATE_ENDED -> {
// 播放完成
}
}
}
})
}
override fun onStart() {
super.onStart()
// 当 Activity 可见时,开始播放
if (playerView.player != null) {
playerView.player!!.playWhenReady = true
}
}
override fun onStop() {
super.onStop()
// 当 Activity 不可见时,暂停播放以节省资源
if (playerView.player != null) {
playerView.player!!.playWhenReady = false
}
}
override fun onDestroy() {
super.onDestroy()
// 释放 