Android 13 - Media框架(2)- Demo App与MediaPlayer Api了解
关注公众号免费阅读全文,进入音视频开发技术分享群!
尝试用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.quit
和HanderThread.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了解的更多相关文章
- 简析Android 兼容性测试框架CTS使用
一.什么是兼容性测试? 1)为用户提供最好的用户体验,让更多高质量的APP可以顺利的运行在此平台上 2)让程序员能为此平台写更多的高质量的应用程序 3)可以更好的利用Android应用市场 二.CTS ...
- android开源项目框架大全:
android开源项目框架大全: 1.多页切换TabHost9 高仿网易云音乐客户端的Home页面切换Tabhost 高仿网易云音乐客户端的Home页面切换Tabhost,并且三角形是透明的,实现方式 ...
- 各种Android UI开源框架 开源库
各种Android UI开源框架 开源库 转 https://blog.csdn.net/zhangdi_gdk2016/article/details/84643668 自己总结的Android开源 ...
- Android中Service的一个Demo例子
Android中Service的一个Demo例子 Service组件是Android系统重要的一部分,网上看了代码,很简单,但要想熟练使用还是需要Coding. 本文,主要贴代码,不对Servic ...
- 自己动手写Android插件化框架
自己动手写Android插件化框架 转 http://www.imooc.com/article/details/id/252238 最近在工作中接触到了Android插件内的开发,发现自己这种技 ...
- 25类Android常用开源框架
1.图片加载,缓存,处理 框架名称 功能描述 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库,已过时 Picasso 一个强大的图片下载与缓存的库 F ...
- Android 内存缓存框架 LruCache 的实现原理,手写试试?
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在之前的文章里,我们聊到了 LRU 缓存淘汰算法,并且分析 Java 标准库中支持 ...
- 15 个 Android 通用流行框架大全(转)
1. 缓存 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库 Picas ...
- Android 通用流行框架
原文出处: http://android.jobbole.com/83028/ 1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Andro ...
- 经受时间沉淀的15 个 Android 通用流行框架大全
1. 缓存 名称描述 DiskLruCache: Java实现基于LRU的磁盘缓存 2.图片加载 名称描述 Android Universal Image Loader 一个强大的加载,缓存,展 ...
随机推荐
- 简单3步,OpenHarmony上跑起ArkUI分布式小游戏
转自:OpenAtom OpenHarmony 在9月30日更新的 OpenHarmony3.0 LTS 上,标准系统新增支持了方舟开发框架(ArkUI).分布式组网和 FA 跨设备迁移能力等新特性, ...
- Web 在线制表工具稳定吗?和桌面报表工具对比哪个好用?
报表工具中最成熟常用的基本都是桌面设计器,但是 web 在线制表工具也占有部分市场,这是因为它也有一些优点: 1.报表设计和发布都在 web 端,无需额外安装桌面设计器 2.web 在线制表工具可直接 ...
- C++对象封装后的内存布局
在C语言中,数据和数据的处理操作(函数)是分开声明的,在语言层面并没有支持数据和函数的内在关联性,我们称之为过程式编程范式或者程序性编程范式.C++兼容了C语言,当然也支持这种编程范式.但C++更主要 ...
- NG 转发配置
ng的用途就不用说了,反向代理么,都知道,不过以前一直不太理解怎没配,现在终于理解点了 1.下载ng,如图: 2.先解压,解压后的路径不建议有空格和中文,其次配置环境变量,加到系统path 3.启动n ...
- 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,即无法使用类型或表的相关性来删除或取代一个类型 在修改一个 ...
- 讲座回顾丨基于 OpenYurt 和 EdgeX 的云边端协同新可能
简介: 为帮助参赛选手更好地了解并运用相关技术,本次大赛将在 7 月至 9 月持续开展 3 轮技术培训,涵盖初.中.高不同层级,帮助开发者系统学习智能边缘系统知识.我们邀请到来自英特尔.VMware. ...
- Flagger on ASM——基于Mixerless Telemetry实现渐进式灰度发布系列 2 应用级扩缩容
简介: 应用级扩缩容是相对于运维级而言的.像监控CPU/内存的利用率就属于应用无关的纯运维指标,针对这种指标进行扩缩容的HPA配置就是运维级扩缩容.而像请求数量.请求延迟.P99分布等指标就属于应用相 ...
- 2021年阿里云年中钜惠攻略,注册即可抽 iPhone 12 Pro 等好礼
简介: 七月流火,燃情盛夏!值此季节,阿里云又推出了年中钜惠,精选百款产品,助力创业新势力.从7月26日开始,每天上午10点.下午4点将会放出爆款产品,进行限量秒杀,大家不要错过.注册登陆还可抽取 i ...
- 一个开源轻量级的C#代码格式化工具(支持VS和VS Code)
前言 C#代码格式化工具除了ReSharper和CodeMaid,还有一款由.NET开源.免费(MIT License).轻量级的C#语言代码格式化工具:CSharpier. 工具介绍 CSharpi ...
- 三、Prophecis 一站式云原生机器学习平台
Prophecis 是微众银行自研大数据平台套件 WeDataSphere 的核心应用工具之一,为用户提供了全栈的机器学习应用开发与部署解决方案.作为WeDataSphere 功能工具应用系统,Pro ...