关注公众号免费阅读全文,进入音视频开发技术分享群!

尝试用MediaPlayer写了一个播放demo,实现了网络流和本地流的播放。由于本人对app开发一窍不通,所以demo中很多内容是边查资料边写的,写的也比较杂乱,能够帮助理解api就行。这一节主要会记录demo开发中学到的内容,以及了解MediaPlayer Api。

1、demo效果

由于Android Studio的虚拟设备只支持API 30,所以demo的编写是基于Android R的,但是后续看的代码还是会基于Android T,这部分应该差的不是很多。

demo代码还没有完善(已发现问题还没处理),目前实现的效果如下,包含有以下几个内容:

  • 网络视频以及本地视频播放
  • 本地视频的seek,播放时间更新
  • 播放过程中窗口最大化

代码可在github下载:MediaPlayerDemo-github
有积分的小伙伴也可在CSDN下载:MediaPlayerDemo-CSDN


2、demo实现过程中学到的相关内容

2.1、layout

  • FrameLayout 中的控件默认位置都是在左上角,可以通过 layout_marginLeft/Right/Bottom/Top 来控制空间边缘的距离;
  • layout_gravity 用于控制组件在当前容器中的位置,可以设置top|right|bottom|left
  • LinearLayout 可以将组件水平或垂直摆放,用layout_weight可以动态调整组件的大小,这时候layout_width需要设置为0;
  • 同一个layout中后面的组件会覆盖前面的组件;
  • dp 转 px 的方法如下:
    public static int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}

2.2、Manifest

如果需要访问内部存储,需要添加以下权限:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />

同时application中还要添加:

android:requestLegacyExternalStorage="true"

打开app后需要给app赋予权限,否则仍不能访问存储。

访问Internet需要赋予如下权限:

<uses-permission android:name="android.permission.INTERNET" />

有了如上设定播放网络流仍会失败,还需在application添加:

android:usesCleartextTraffic="true"

播放页面横屏不能结束当前activity的生命周期,需要在activity中添加如下配置:

android:configChanges="orientation|screenSize"

android app默认的主题颜色是紫色,并且会有标题栏,我们可以修改 application 中的 theme主题,主题设置在themes.xml中。

2.3、Activity

这里要了解的是与Activity相关的方法,例如 onCreate、onStart、onPause、onResume、onDestory等,这些方法可以在Activity的基类AppCompatActivity中查找到,注意Override这些方法时需要调用它们的 super 方法。

启动MainActivity时,执行顺序如下:

2023-08-12 22:52:20.240 1790-1790/com.example.mediademo D/Demo_MainActivity: onCreate
2023-08-12 22:52:20.323 1790-1790/com.example.mediademo D/Demo_MainActivity: onPause
2023-08-12 22:52:20.324 1790-1790/com.example.mediademo D/Demo_MainActivity: onResume

启动VideoActivity时,执行顺序如下:

2023-08-12 22:59:32.992 1928-1928/com.example.mediademo D/Demo_MainActivity: onPause
2023-08-12 22:59:33.539 1928-1928/com.example.mediademo D/Demo_MainActivity: onStop

回到之前MainActivity时:

2023-08-12 22:59:41.757 1928-1928/com.example.mediademo D/Demo_MainActivity: onPause
2023-08-12 22:59:41.758 1928-1928/com.example.mediademo D/Demo_MainActivity: onResume

退出app时(按返回键或者退出后台),会多执行一个 onDestroy 销毁资源:

2023-08-12 23:06:09.275 2334-2334/com.example.mediademo D/Demo_MainActivity: dispatchKeyEvent, keycode = 4
2023-08-12 23:06:09.275 2334-2334/com.example.mediademo D/Demo_MainActivity: keyCode = 4
2023-08-12 23:06:09.383 2334-2334/com.example.mediademo D/Demo_MainActivity: dispatchKeyEvent, keycode = 4
2023-08-12 23:06:09.394 2334-2334/com.example.mediademo D/Demo_MainActivity: onPause
2023-08-12 23:06:09.940 2334-2334/com.example.mediademo D/Demo_MainActivity: onStop
2023-08-12 23:06:09.941 2334-2334/com.example.mediademo D/Demo_MainActivity: onDestroy

2.4、Handler Looper and Thread

由于我不是很熟悉java,也不是专业的android app开发工程师,所以关于这一小节的理解可能会有错误,如有小伙伴看到欢迎指出。以下是我对Thread、Handler、Looper的理解:

  • 每个 Activity 启动时都会自动开启一个线程,这个线程中应该执行了Looper.loop方法(我猜的),所有和 Activity 相关的事件均由这个 Looper 来做消息分发处理,这个线程也被称为 UI 线程;
  • 为什么一个线程 Thread 只能有一个 Looper 呢?因为 Looper.loop 是一个死循环,Thread.run 执行了这个方法当然就不能再去执行其他的内容了;
  • 为什么有的时候发消息用 Runnable,有的时候却用 Message 呢?我的理解是这样:用 Message 是让线程根据 Message.what 让 Handler 执行对应的操作,有的时候我们并不一定需要让Handler执行,工作可以直接在 Looper 中完成,这时候就用 Runnable;
  • 网上会有很多资料来讲主线程向子线程中发消息,子线程向主线程中发送消息,我觉得都没有理解到 Looper Handler 这块内容的本质。我的理解是这样,向某个线程发送消息,就是要将消息发到指定的 Looper 中,我们在创建 Handler 时会传入一个 Looper,我们利用对应线程的 Handler 就可以很轻松将 Message/Runnable 发送到指定线程中执行;当然我们也可用 Message.sendToTarget 将自身送到指定的 Looper;
  • HandlerThread 和 Thread 的区别在于前者在创建的时候会自动创建一个 Looper,而后者需要我们手动执行 Looper.prepare 以及 Looper.loop;
  • 网上会有很多资料来讲Handler的内存泄漏,这点我不是很能理解,Activity结束时为什么不去清理 Looper 中 MessageQueue 的内容呢?查看源码可以发现,我们可以在Activity结束时,在onDestory中调用Handler .removeCallbacksAndMessages 清除 MessageQueue 中由该 Handler 发送的内容;如果子线程中含有Looper,那么调用 Looper.quit 或者 Looper.quitSafely 可以安全退出子线程,同时可以调用 Thread.join 等待线程结束;如果用的是 HandlerThread ,我们可以调用 HanderThread.quitHanderThread.quitSafely 退出线程,这等同于调用 Looper.quit。按照我的理解,做到以上几点,内存泄露应该就不会发生了;
  • 以上内容的关键点在于退出 Activity 时能够打断 Run 函数,如果是UI线程我们不能主动打断,只能把 Handler 发出的消息清除;如果是子线程,我们可以通过打断 Looper 从而中断 Run 函数;

这里来看 MediaPlayer.java 中给我门提供的示例:

这里创建了一个 HandlerThread,来执行 addTrack 任务,任务执行完成后调用 Looper.quit退出线程。

        final HandlerThread thread = new HandlerThread("SubtitleReadThread",
Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE);
thread.start();
Handler handler = new Handler(thread.getLooper());
handler.post(new Runnable() {
...
public void run() {
int res = addTrack();
if (mEventHandler != null) {
Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
mEventHandler.sendMessage(m);
}
thread.getLooper().quitSafely();
}
});

还有另外一个示例,MediaPlayer的创建过程中会创建一个EventHandler,reset 时会调用 Handler.removeCallbacksAndMessages 来清除 MessageQueue 中由 EventHandler 发送的消息。如果 Handler 用的主线程 Looper,那么主线程可以安全退出;如果用的子线程 Looper,还需要调用该线程的 quit 方法打断 loop。

	public MediaPlayer() {
....
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
} public void reset() {
...
if (mEventHandler != null) {
mEventHandler.removeCallbacksAndMessages(null);
}
}

如需更深入了解 Handler Looper and Thread 机制可以查看源码,也可以查看之前的 native AHandler ALooper 机制,原理大致是相同的 。


3、MediaPlayer Api分析

这一节主要是了解 MediaPlayer有什么api,我这里将api进行了罗列分组,并且贴出了他们的功能,至于他们要怎么用,有什么注意点,与生命周期相关的内容会在下一篇内容中了解。

首先是播放控制类api,这类api用于播放参数设定,以及Player生命周期的管理:

num methods Func
1 setDataSource 设置数据源
2 prepare 准备
3 prepareAsync 准备
4 start 开始播放
5 stop 停止播放
6 pause 暂停播放
7 release 释放当前播放器持有的资源
8 reset 重置播放器
9 finalize
10 setPlaybackParams 设置播放参数,例如倍速、Audio mode
11 setSyncParams 设置Avsync mode,有4种sync模式
12 seekTo 定位,有4种seek mode
13 setDisplay 设置 SurfaceHolder
14 setSurface 设置 Surface
15 setVideoScalingMode 设置缩放模式
16 setAudioStreamType 设置音频流类型
17 invoke 调用指定函数,no public,不支持App使用
18 setParameter 设定参数,no public,不支持App使用
19 setVolume 设置音量
20 setAudioSessionId 设置AudioSessionId
21 selectTrack 指定播放的track或者是切换当前播放的track

以下是参数获取类api,用他们可以获取到播放状态、播放参数等信息:

num methods Func
1 getVideoWidth 获取宽度
2 getVideoHeight 获取高度
3 isPlaying 是否正在播放
4 getPlaybackParams 获取播放参数
5 getSyncParams 获取Avsync参数,里面包含有帧率等信息
6 getTimestamp 获取当前播放时间戳
7 getCurrentPosition 获取当前播放时间,单位是msec,getTimestamp是基于这个方法的
8 getDuration 获取视频时长
9 getMetadata 获取当前播放流的信息,no public,不支持app使用,包含 bitrate、mine、codectype等信息
10 notifyAt 设置pts更新的频率
11 getAudioSessionId 获取AudioSessionId
12 getTrackInfo 获取当前Track的媒体格式
13 getSelectedTrack 获取当前选择的track

下面两个api用于循环播放或者是快速播放:

num methods Func
1 setNextMediaPlayer 设置下一个要播放的MediaPlayer,自动播放
2 setLooping 设置循环播放

以下类和方法用于回调事件的上抛与处理:

num class/function Func
1 EventHandler framework回调事件的处理
2 postEventFromNative native callback to framework

以方法用于将回调事件进一步上抛给app层,app做相应动作:

num class Func
1 OnPreparedListener prepareAsync完成,搭配start使用
2 OnCompletionListener 当前码流播放完成
3 OnBufferingUpdateListener 缓冲进度更新
4 OnSeekCompleteListener seek完成
5 OnVideoSizeChangedListener 视频的宽高发生变化,搭配 getVideoWidth 和 getVideoHeight使用
6 OnErrorListener 错误事件回调
7 OnTimedTextListener
8 OnTimedMetaDataAvailableListener
9 OnInfoListener 播放器信息回调

Android 13 - Media框架(2)- Demo App与MediaPlayer Api了解的更多相关文章

  1. 简析Android 兼容性测试框架CTS使用

    一.什么是兼容性测试? 1)为用户提供最好的用户体验,让更多高质量的APP可以顺利的运行在此平台上 2)让程序员能为此平台写更多的高质量的应用程序 3)可以更好的利用Android应用市场 二.CTS ...

  2. android开源项目框架大全:

    android开源项目框架大全: 1.多页切换TabHost9 高仿网易云音乐客户端的Home页面切换Tabhost 高仿网易云音乐客户端的Home页面切换Tabhost,并且三角形是透明的,实现方式 ...

  3. 各种Android UI开源框架 开源库

    各种Android UI开源框架 开源库 转 https://blog.csdn.net/zhangdi_gdk2016/article/details/84643668 自己总结的Android开源 ...

  4. Android中Service的一个Demo例子

    Android中Service的一个Demo例子  Service组件是Android系统重要的一部分,网上看了代码,很简单,但要想熟练使用还是需要Coding.  本文,主要贴代码,不对Servic ...

  5. 自己动手写Android插件化框架

    自己动手写Android插件化框架 转 http://www.imooc.com/article/details/id/252238   最近在工作中接触到了Android插件内的开发,发现自己这种技 ...

  6. 25类Android常用开源框架

    1.图片加载,缓存,处理 框架名称 功能描述 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库,已过时 Picasso 一个强大的图片下载与缓存的库 F ...

  7. Android 内存缓存框架 LruCache 的实现原理,手写试试?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在之前的文章里,我们聊到了 LRU 缓存淘汰算法,并且分析 Java 标准库中支持 ...

  8. 15 个 Android 通用流行框架大全(转)

    1. 缓存 DiskLruCache    Java实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库 Picas ...

  9. Android 通用流行框架

    原文出处: http://android.jobbole.com/83028/ 1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Andro ...

  10. 经受时间沉淀的15 个 Android 通用流行框架大全

    1. 缓存 名称描述 DiskLruCache: Java实现基于LRU的磁盘缓存 2.图片加载 名称描述 Android    Universal Image Loader 一个强大的加载,缓存,展 ...

随机推荐

  1. 简单3步,OpenHarmony上跑起ArkUI分布式小游戏

    转自:OpenAtom OpenHarmony 在9月30日更新的 OpenHarmony3.0 LTS 上,标准系统新增支持了方舟开发框架(ArkUI).分布式组网和 FA 跨设备迁移能力等新特性, ...

  2. Web 在线制表工具稳定吗?和桌面报表工具对比哪个好用?

    报表工具中最成熟常用的基本都是桌面设计器,但是 web 在线制表工具也占有部分市场,这是因为它也有一些优点: 1.报表设计和发布都在 web 端,无需额外安装桌面设计器 2.web 在线制表工具可直接 ...

  3. C++对象封装后的内存布局

    在C语言中,数据和数据的处理操作(函数)是分开声明的,在语言层面并没有支持数据和函数的内在关联性,我们称之为过程式编程范式或者程序性编程范式.C++兼容了C语言,当然也支持这种编程范式.但C++更主要 ...

  4. NG 转发配置

    ng的用途就不用说了,反向代理么,都知道,不过以前一直不太理解怎没配,现在终于理解点了 1.下载ng,如图: 2.先解压,解压后的路径不建议有空格和中文,其次配置环境变量,加到系统path 3.启动n ...

  5. ORA-02303: cannot drop or replace a type with type or table dependents,即无法使用类型或表的相关性来删除或取代一个类型

    ORA-02303: cannot drop or replace a type with type or table dependents,即无法使用类型或表的相关性来删除或取代一个类型 在修改一个 ...

  6. 讲座回顾丨基于 OpenYurt 和 EdgeX 的云边端协同新可能

    简介: 为帮助参赛选手更好地了解并运用相关技术,本次大赛将在 7 月至 9 月持续开展 3 轮技术培训,涵盖初.中.高不同层级,帮助开发者系统学习智能边缘系统知识.我们邀请到来自英特尔.VMware. ...

  7. Flagger on ASM——基于Mixerless Telemetry实现渐进式灰度发布系列 2 应用级扩缩容

    简介: 应用级扩缩容是相对于运维级而言的.像监控CPU/内存的利用率就属于应用无关的纯运维指标,针对这种指标进行扩缩容的HPA配置就是运维级扩缩容.而像请求数量.请求延迟.P99分布等指标就属于应用相 ...

  8. 2021年阿里云年中钜惠攻略,注册即可抽 iPhone 12 Pro 等好礼

    简介: 七月流火,燃情盛夏!值此季节,阿里云又推出了年中钜惠,精选百款产品,助力创业新势力.从7月26日开始,每天上午10点.下午4点将会放出爆款产品,进行限量秒杀,大家不要错过.注册登陆还可抽取 i ...

  9. 一个开源轻量级的C#代码格式化工具(支持VS和VS Code)

    前言 C#代码格式化工具除了ReSharper和CodeMaid,还有一款由.NET开源.免费(MIT License).轻量级的C#语言代码格式化工具:CSharpier. 工具介绍 CSharpi ...

  10. 三、Prophecis 一站式云原生机器学习平台

    Prophecis 是微众银行自研大数据平台套件 WeDataSphere 的核心应用工具之一,为用户提供了全栈的机器学习应用开发与部署解决方案.作为WeDataSphere 功能工具应用系统,Pro ...