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

尝试用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. css 居中的汇总

    前言 对css居中的几种方式汇总,并且分析适用情况. 正文 margin+position .CenterParent { position: relative; height: 200px; wid ...

  2. 论文研究区域图的制作方法:ArcGIS

      本文介绍基于ArcMap软件,绘制论文中研究区域示意图.概况图等的方法.   最近需要绘制与地学有关论文.文献中的研究区域概况图.对于这一类图片,我个人比较喜欢基于ArcMap与PPT结合的方式来 ...

  3. 这些Git事故灾难, 你经历过几个?

    前言 关于Git, 相信大家最常用的就是pull和push. 但随着协作规模的提升, 遇到的问题也会越来越多. 本篇文章并不科普一些命令的详细用法, 更多的是分享在工作中遇到的Git场景问题以及踩过的 ...

  4. marquee实现滚动

    marquee的基本语法:<marquee> ... </marquee> 参数:1.滚动方向 (direction):left(左).right(右).up(上).down( ...

  5. 如何解决打不开Microsoft Store的痛处?

    换了机房后,我最喜欢的计算器和画图3d没有了,网上的方法都行不通,怎么办? 第一步,在百度上搜索你想安装的东西,例如我搜索的画图3d,就会有这个链接. 然后,把这个链接复制到这个网站的搜索框中,就会有 ...

  6. 【译】Visual Studio Enterprise 中的代码覆盖率特性

    通过使用代码覆盖率功能,您可以发现您的测试需要改进的地方,并使您的软件更加健壮和可靠.在这篇文章中,我们将介绍我们在 Visual Studio Enterprise 2022 中引入的 Code C ...

  7. 一遇到复杂分析查询就卡顿?MySQL分析实例了解一下

    随着企业数据爆发式增长,MySQL分析查询卡顿问题越来越多,用户时效性不能保证,精细化运营诉求不能满足.如何能无缝对接业务库,实现毫秒级针对万亿级数据进行即时的多维分析透视和业务探索,MySQL分析实 ...

  8. 【产品能力深度解读】连续入围Gartner魔力象限的Quick BI有何魔力?

    简介: 国际权威分析机构Gartner发布2021年商业智能和分析平台魔力象限报告,阿里云Quick BI再度入选,并继续成为该领域魔力象限唯一入选的中国企业. Quick BI凭借在增强分析能力上的 ...

  9. ARMv9刷屏 —— 号称十年最大变革,Realm机密计算技术有什么亮点?

    ​简介: 让我们看下ARMv9机密计算相关的新特性Realm. ​ ARMv9的新闻刷屏了.ARMv9号称十年以来最重大变革,因此让我们看下ARMv9中机密计算相关的新特性Realm.(注:本文是对I ...

  10. 学术顶会再突破!计算平台MaxCompute论文入选国际顶会VLDB 2021

    ​ 简介: VLDB 2021上,阿里云计算平台MaxCompute参与的论文入选,核心分布式调度执行引擎Fangorn.基于TVR Cost模型的通用增量计算优化器框架Tempura等分别被Indu ...