Android高级终端开发学习笔记(《疯狂Android讲义》第11章-第17章)
Posted wyy_persist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android高级终端开发学习笔记(《疯狂Android讲义》第11章-第17章)相关的知识,希望对你有一定的参考价值。
Android高级终端开发笔记
2021/6/19 下午 13:34开始
- 多媒体应用开发
android支持的音频格式有:MP3 WAV 3GP等。支持的视频格式有MP4 3GP等。
多媒体数据既可以来自android应用的资源文件,也可以来自外部存储器上的文件,还可以是来自网络的文件流。
android也提供了对摄像头 麦克风的支持。
11.1 音频和视频的播放
11.1.1 android 9增强的MediaPlayer
使用MediaPlayer装载音频文件完成之后,调用MediaPlayer对象的三个方法进行播放:
Start()函数,开始和回复播放
Stop()函数,停止播放
Pauser()函数,暂停播放
装载文件的时候,mediaplayer对象提供了如下的静态方法:
Create(Context context, Uri uri)函数,从指定的Uri装载音频文件,并返回创建的MediaPlayeru对象
Create(Context context,int resid)函数,从制定的资源文件id来装载音频文件,并返回MediaPlayer对象。
当需要循环播放多个音频文件的时候,使用MediaPlayer对象的setDataSource()方法来制定播放的音频文件的。
setDataSource(String path)函数,装载path路径代表的文件。
setDataSource(FileDecriptor fd,long offset,long length)函数,制定装载fd做代表的文件中从offset开始,长度为length的文件内容。
setDataSource(FileDecriptor fd),制定转载fd多代表的文件。
setDataSource(Context context,Uri uri)方法之后,指定装载uri所代表的文件。
使用上述的setDataSource()方法之后,MediaPlayer对象并没有真正地装载文件,需要使用其中的prepare()方法准备音频。(就是真正地装载音频文件)
Mediaplayer对象还提供了一些监听播放事件的方法:
setOnCompletionListener()方法为mediaPlayer对象增加播放完成事件绑定事件监听器。
setOnErrorListener()方法为该对象的播放错误事件绑定事件监听器。
setOnPreparedListener()方法为mediaplayer对象调用prepare()方法触发该监听器。
setOnSeekCompleteListener()方法,当mediaplayer对象调用seek()或seekTo()方法时候触发该监听器。
归纳一下使用MediaPlayer对象播放不同来源的音频文件:
播放应用的资源文件
1.调用MediapLayer对象的create()方法装载指定的资源文件。
- 调用MediaPlayer对象的start() pause() stop()方法实现播放控制功能。
播放应用的原始资源文件
- 调用context对象的getAssets()方法获得应用的AssetManager。
- 调用AssetManager对象的openFd(String name)方法打开指定的原始资,该方法返回一个AssetFileDescriptor对象。
- 调用MediaPlayer对象(或者利用已有的MediaPlayer对象),并调用MediapLayer对象的setDataSource(FileDescriptor fd,long offset,long length)方法来装载音频资源。
- 调用mediaPlayer对象的prepare()方法准备音频。
- 调用mediaPlayer对象的start() pause() stop()方法来实现对音频文件的控制播放。
注意:使用openFd()方法打开资源的时候,不管是哪一个,使用MediaPlayer播放的时候总是从资源的第一个开始。
播放外部存储器上的音频文件
- 创建mediaplayer对象,并调用MediaPlayer对象的setDataSource()方法装在指定的音频文件。
- 调用mediaPlayer对象的prepare()方法准备音频。
- 调用MediaPlayer对象的start() pause() stop()方法控制播放即可。
播放来自网络的音频文件
有两种方式:
- 直接使用MediaPlayer对象的静态create()方法。
- 调用MediaPlayer对象的setDataSource()方法装载指定的Uri对应的音频文件。
使用第二种方法播放来自网络的音频文件的步骤如下:
- 根据网络上的音频文件所在的位置创建Uri对象
- 创建mediaplayer对象,并调用MediaPlayer对象的setDataSource()方法装载uri对应的音频文件。
- 调用MediaPlayer对象的prepare()方法准备音频。
- 调用MediaPlayer对象的start() pause() stop()方法控制播放即可。
注意:mediaPlayer对象除了可以使用prepare()方法来准备声音之外,还可以使用prepareAsync()方法来准备声音。
两种方法的区别在于:prepareAsync()方法是异步的,不会阻塞当前的UI线程。
播放带数字版权保护(DRP)的媒体文件
Android 8 对MediaPlayer的功能进行了增强-从android 8 开始,mediaPlayer提供了方法来支持播放带有数字版权保护的媒体文件。MediaPlayer并没有提供MediaDrm的全部功能,但对大部分通用场景,MediaPlayer的功能已经足够。
mediaplayer支持处理以下内容类型
Widvine保护的本地媒体文件
Widvine保护的远程媒体文件
为了处理有数字版权保护的媒体文件,mediaPlayer对象在执行了prepare()方法之后调用getDrmInfo()方法来获得该媒体文件的数字版权保护信息,如果该方法返回的数字版权信息不是null,那么说明该文件存在数字版权保护,接下来程序就需要处理媒体文件的数字版权信息了。
使用mediapLayer对象的prepaerDrm()
使用MediaPlayer对象的getKeyRequest()方法
使用player对象的provideKeyResponse()方法
如果需要使用异步的方式来处理数字版权信息,MediaPlayer对象提供了setOnDrmListener()方法,为该方法传入的Mediaplayer.OnDrmInfoListener参数负责监听该媒体文件的数字版权信息。
Android 9 对mediaplayer进行了增强,内置了HDR VP9 视频的本地解码器,所以开发者可以直接使用Android本地解码器来播放HDR视频。
11.1.2 音乐特效控制
android可以控制播放音乐时的均衡器 重低音 音场 显示音乐波形等。
这些都是需要AudioEffect及其子类来完成的。
其包含的常用子类如下:
AcousticEchoCanceler:取消回声控制器
AutomaticGainControl:自动增益控制器。
NoiseSuppressor:噪音压制控制器
BassBoost:重低音控制器
Equalizer:均衡控制器
PresetReverb:预设音场控制器
Visualizer:示波器
上述的子类中前三个子类用法简单,只需要调用其中的create()方法创建相应的实例,然后调用他们的isAvailable()方法判断是否可用即可。再调用setEnabled(boolean enabled)方法启用相应效果即可。
后边四个类都需要调用构造器来创建实例,创建实例时,需要传入一个audioSession参数,为了启用他们,同样需要调用AudioEffect基类的setEnabled()方法。
获取BassBoost对象之后,调用它的setStrength(shot strength)方法来设置重低音的强度。
获取PresetReverb对象之后,可调用它的setPreset()方法设置使用预设置的音场。
Equalizer对象提供了getNumberOfPresets()方法获得系统所有预设的音场,并提供了getPresetName()方法获得预设音场的名称。
实例:音乐的示波器 均衡 重低音 和音场
//
使用上述子类的时候需要用到RECORD_AUDIO权限,因此需要设置权限。
在使用Visualizer对象绘制波形图的时候,这里创建了一个内部类View,用来将Visualizer对象传过来的数据动态绘制波形效果。
11.1.3 使用VolumeShaper控制声音效果
Android 8 新增了VolumnShaper对象来控制声音效果,VolumnShaper对象可以实现音量的淡入,淡出等自动音量转换效果。
使用该对象进行音量控制实际上是使用VolumnShapaer.Configuration来实现的,创建该对象时,主要通过三个方法来指定三个参数。
持续时间:duration,指定该声音效果的持续时间,以毫秒为单位。
插值方式:interpolator type:指定声音变化的插值方式。
音量曲线:volumn curve:指定音量变化的曲线。该参数需要两个长度相同的数组,第一个数组表示各时间点,第二个数组表示个时间点对应的音量。
这些数组的值必须在0-1f之间。
在创建了VolumnShaper.Configuration对象之后,调用支持VolumnShaper的声音播放器(如mediaplayer audioTrack等)的createVolumnShaper()方法创建VolumnShaper即可。
在使用声音播放器创建了VolumnShaper之后,必须调用VolumnShaper的play()方法,否则,只有第一个音量控制点指定的音量会作用于该声音效果。
综上所述,使用VolumnShaper控制声音效果的步骤如下:
- 创建VolumnShpaer.Confifuration对象,在创建该对象时需要指定持续时间 音量曲线,插值方式三个参数。
- 调用声音播放器的createVolumnShaper()方法创建VolumnShaper对象。
- 调用VolumnShaper对象的play()方法。
11.1.4 使用SoundPool播放音效
可以管理短促的音效
可以在开始时加载多个音效,然后使用id来控制不同的音效来进行播放。
Android系统SoundPool提供了一个Builder内部类,该内部类专门用于创建SoundPool。
创建该对象之后,就可以使用该对象的四个load()方法来加载声音了。
Int load(Context context,int resid,int priority):从resid所对应的资源加载声音。
Int load(FileDescriptor fd,long offest,long length,int priority):加载fd对应的文件中从offset开始长度为length的声音。
使用SoundPool播放指定声音的方法如下:
注意:
使用SoundPool播放声音的步骤如下:
实例:
创建SoundPool对象的代码:
重点注意:
11.1.5 使用VideoView播放视频
该组件位于android.widget包下的组件
使用VideoView组件加载指定视频的方法:
- 在界面布局中定义一个VideoView组件,或在程序中创建VideoView组件。
- 调用VideoView组件的两个方法来加载指定音频:
setVideoPath(String path)加载path文件所代表的视频
setVideoURI(Uri uri)方法加载uri所对应的视频。
- 调用videoView的start() stop() pause()方法来控制视频的播放。
实际上,与VideoView结合使用的还有一个MediaController类,作用是提供一个友好的图形界面,可以通过该界面实现对视频的播放控制。
重要代码实例:
当视频文件存在时:
注意:
11.1.6 使用MediaPlayer和SurfaceView播放视频
使用上述两种对象播放视频的步骤如下:
- 创建MediaPlayer对象,并让它加载指定的视频文件。
- 在界面布局文件中定义SurfaceView组件,或在程序中创建SurfaceView组件,并为SurfaceView的SurfaceHolder添加Callbac监听器。
- 调用MediaPlayer对象的setDisplay(SurfaceHolder sh)方法将所播放的视频图像输出到指定的surfaceView组件。
- 调用MediaPlayer对象的start() stop() pause()方法控制视频的播放。
注意:由于程序需要使用SurfaceView来显示MediaPlayer的图像输出,所以需要使用一些代码来维护SurfaceView,SurfaceHolder对象。
代码如下:
11.2 使用MediaRecorder录制音频
步骤:
- 创建MediaRecorder对象
- 调用MediaRecorder对象的setAudiosource()方法设置声音来源,一般传入MediaRecorder.AudioSource.MIC参数指定来自麦克风的声音。
- 调用MediaRecorder对象的setOutputFormat()方法设置录制的音频格式文件。
- 调用MediaRecorder对象的setAudioEncoder()方法,setAudtioEncodingBitRate(int bitRate)、setAudioSamplingRate(int sampliingRate)方法设置所录制的声音编码格式,编码位率,采样率等。这些参数可以控制所录制声音品质,文件大小。
- 调用该对象的setOutputFIle(String path)方法设置录制的音频文件的保存位置。
- 调用MediaRecorder对象的prepare()方法准备录制。
- 调用MediaPlayer对象的start()方法开始录制。
- 录制完成,调用MediaRecorder对象的stop()方法停止录制,并调用release()方法释放资源。
核心代码:
需要增加的权限:
重点:
11.3 控制摄像头拍照
11.3.1 Android 9 改进的Camera v2
使用Camera v2 API来控制摄像头拍照的步骤:
//实例:拍照时自动对焦
获得摄像头信息的代码:
通过stateCallback中的onOpened()方法的参数可以获得被打开的摄像头设备。
此外,在onOpened()方法中调用createCameraPreviewSession()方法创建了CameraCaptureSession,并开始预览取景。
重点注意:
11.3.2 录制视频短片
步骤:
- 调用MediaRecorder对象的setVideoEncoder().setVideoEncodingBitRate(),setVideoFrameRate()方法设置所录制的视频编码格式,编码位率,每秒多少帧等。
- 调用MediaRecorder对象的setPreviewDisplay()方法设置使用哪一个SurfaceView组件来显示视频预览。
其他代码和录制音频的一样。
//实例:录制生活短片
重要代码:
需要增加的权限:
录制声音的权限
使用摄像头的权限
使用外部存储器的权限
11.4 屏幕捕捉
使用android提供的MediaPlayerManager管理器。
步骤如下:
核心代码:
11.5 本章小结
- OpenGL 与3D开发
OpenGL本身是高效、简洁的开放图形库接口。
定义了一个跨编程语言、跨平台的编程接口规范。
12.1 3D图形和3D开发基本知识
绘制3D图形需要用到的数据:
12.2 OpenGL 和 OpenGL ES简介
OpenGL具有更广泛的适应性。
OpenGL ES 是 OpenGL的一个子集。
12.3 绘制2D图形
12.3.1 在android中使用OpenGL ES
Android中支持提供了GLSurfaceView组件,该组件用于显示3D图形。
GLSurfaceView本身不提供绘制3D图形,而是由GLSurfaceView.Renderer来完成SurfaceView中3D图形的绘制。
步骤:
- 创建GLSurfaceView组件,使用activity来显示该组件。
- 为GLSurfaceView组件创建一个GLSurfaceView.Renderer实例,实现GLSurfaceView.Renderer类时选哟实现该接口中的三个方法。
Abstract void onDrawFrame(GL10 gl):Renderer对象调用该方法绘制GLSurfaceView的当前帧。
Abstract void onSurfaceChanged(GL10 gl,int width,int height):当GLSurfaceView的大小改变时回调该方法。
Abstract void onSurfaceViewCreated(Gl10 gl,EGLConfig config):当GLSurfaceView被创建的时候回调该方法。
- 调用GLSurfaceView组件的setRenderer()方法指定Renderer对象,该Renderer对象将会完成GLSurfaceView里面的3D图形的绘制。
绘制3D图形的难点是:如何设置Renderer类。
当SurfaceView被创建时,系统会回调Renderer对象的onSurfaceCreated()方法,该方法可以对OpenGL ES执行一些无需任何改变的初始化,初始化代码如下:
GL10 就是OpenGL ES 的绘图接口。
关于上述代码中的方法说明如下:
在onSurfaceChanged()方法中用于初始化3D场景的代码是:
一些说明:
GLSurfaceView上的所有3D图形都是由Renderer的onDrawFrame(GL10 gl)方法绘制出来的,重写该方法的时候就要把所有的3D图形都绘制出来。
然后下面就可以调用gl10对象的方法开始绘制了。
12.3.2 绘制平面上的多边形
绘制3D图形就是通过多个平面图形形成的。
绘制2D图形如下:
- 调用GL10 的glEnableClientState()方法启用顶点坐标数组
- 调用GL10 的glEnableClientState()方法启用顶点颜色数组。
- 调用GL10的glVertexPointer()方法设置顶点的位置数据。
上述第三个方法中的参数pointer参数用于指定顶点坐标值。是一个三维数组。
第一个参数size指定多少个pointer数组中的元素成为一个顶点位置。通常为3。
type参数指定顶点坐标值的类型。
- 调用GL10的glColorPointer()方法设置顶点的颜色数据。
第4个方法中pointer参数指定顶点的颜色值,也是一个一维数组。
其中的size通常设置为4,表示指定pointer数组中每4个元素作为一个顶点坐标值。
- 调用GL10 的glDrawArrays()方法绘制平面。该方法的第一个参数指定绘制图形的类型,第二个参数指定从哪个顶点开始绘制,第三个参数指定总共绘制的顶点数量。
- 绘制完成后,调用GL10 的glFinish()方法结束绘制,并调用glDisableClientSate(int)方法来停用顶点坐标数据,顶点颜色数据。
//实例
代码:
在onDrawFrame()方法中绘制图形:
其中,使用glTranslatef()方法目的是将图形的绘制保证在中心点上。
在onCreate()方法中使用如下代码即可实现绘制:
可以借助maya或3dMAX将顶点坐标数据导入到程序中实现绘制。
12.3.3 旋转
GL10提供了使用glRotatef()方法用于控制旋转。
该方法中的angle控制旋转角度,x,y,z参数则共同决定了旋转轴的方向。
实现不断旋转的特效只需要将旋转的角度增加即可。
只需要在onDrawFrame()方法最后添加:rotate+= 1;即可。
12.4 绘制3D图形
定义的顶点不在同一个平面上,并使用三角形将合适的顶点连接起来,就可以绘制出来一个3D图形了。
12.4.1 构建3D图形
GL10 提供了glDrawElements()方法,参数为:int mode,int count,int type,Buffer indices。根据indices指定的索引点来绘制三角形。
该方法的第一个参数指定绘制的图形类型,第二个参数指定一共包含多少个顶点,第三个参数最重要:其包装了一个长度为3N的数组,其中三个元素表示一个顶点。
3D图形的绘制需要在程序中指定每一个面由哪三个顶点组成。
12.4.2 应用纹理贴图
在Renderer实现类中的onSurfaceCreated()方法中启用纹理贴图。代码如下:
准备的图片的规格需要是:长,宽是2的N次方。
加载图片生成贴图的代码如下:
说明:
- glGenTextures(int n ,int[] textures,int offsets)该方法指定一次性生成n个纹理。
该方法所生成的纹理代号放入其中的testures数组中,offsets指定从第几个数组元素开始放置纹理代号。
- glBindTexture(int target,int texture),该方法用于将texture纹理绑定到target目标上。
- GlTextParameterf(int target,int pname,float param),该方法用于为target纹理目标设置属性,其中第一个参数是属性名,第二个参数是:属性值。
在3D绘制中设置纹理贴图:
- 设置启用贴图的坐标数组。
- 设置贴图坐标的数组信息。
- 调用GL10的glBindTexture(int target,int texture)方法执行贴图。
12.5 本章小结
第13章 android网络应用
android支持JDK自带的网络编程。
由于android删除了Apache HttpClient支持,有一个OkHttp框架的支持。可以取代原来的Apache HttpClietn支持。
13.1 基于TCP协议的网络通信
13.1.1 TCP协议基础
//略
13.1.2 使用ServeSocket创建TCP服务器端
可以接受其他实体连接请求的类是ServerSocket。
该对象包含的方法:
- Socket accept()方法接受一个客户端Socket的连接请求。该方法将返回一个与连接客户端Socket对应的Socket。否则,盖方法将一直处于等待状态,线程被阻塞。
- ServerSocket(int port)用指定的端口port来创建一个ServerSocket对象。
- ServerSocket(int port,int backlog)增加了一个用来改变连接队列长度的参数backlog。
- ServerSocket(int port,int backlog,InetAddress localAddr)。在机器存在多个ip地址的情况下,允许通过localAddr这个参数来指定将ServerSocket绑定到指定的ip地址。
通常使用循环控制不断调用ServerSocket的accept()方法。
代码实例如下:
当未指定I地址的时候,ServerSocket会绑定到指定的本机默认IP地址。
推荐使用1024以上的端口号作为接收的端口号。
13.1.3 使用Socket进行通信
连接到服务器端时的客户端的socket的创建:
一旦服务器和客户端连接之后,就不需要在分服务器和客户端了。因为两者都可以发送和接受消息进行交互。
其中Socket提供了两个方法来实现消息的交互:
- InputStream getInputStream()方法,返回该Socket对象对应的输入流,让程序通过该输入流从Socket中得到数据。
- OutputStream getOutputStream()方法,返回该Socket对象对应的输出流,让程序通过该输出流向Socket中输出数据。
服务器端代码实例:
客户端代码实例:
注意:直接在UI线程中建立连接,是不合适的,会阻塞线程。
使用Socket对象的setSoTimeout(int timeout)方法来设置超时时长。
使用下述代码可以实现创建以恶搞Socket对象来直接在链接的时候设置超时时长:
13.1.4 加入多线程
服务器应当给每一个Socket单独启动一个线程。每个线程负责与一个客户端进行通信。
可以使用List保存所有的Socket对象。
代码:
其中的另外一个线程代码:
在run()方法中设置:
上述代码实际上实现了群发消息的功能。
每个客户端包含两个线程:
- 一个包含UI界面的创建
- 一个是包含和服务器端的数据交互。
为了避免UI线程阻塞,程序将建立网络连接,与网络服务器通信等工作都交给ClientThread线程完成。
使用handler主要是为了实现子线程和UI界面进行数据交互。
13.2 使用URL访问网络资源
URL是指向互联网资源的指针。
URL的格式:
URL包含一个可以打开到资源的输入流。
URI的实例代表一个统一资源标识符,java中的URL不可以用来定位任何资源,唯一作用是解析。
URL对象的方法:
13.2.1 Android 9 安全性增强的URL
由于从Android 9 网络连接默认是加密的,那么如果访问的服务器是http的就需要在配置文件中加上:
第一种方式实例:
13.2.2 使用URLConnection对象提交请求:
URL的openConnection()方法创建URLConnection对象,程序可以使用该对象向URL请求获得资源。
创建一个URL的连接的步骤:
- 调用URL对象的openConnection()方法创建URLConnection对象。
- 设置URLConnection的参数和普通请求属性。
- 如果是GET请求,那么使用connect方法建立和远程资源之间的实际连接即可。
- 如果是post请求,那么需要获得URLConnection对象对应的输出流来发送请求参数。
- 远程资源变为可用,程序可以访问远程资源的头字段或通过输入流得到远程资源的数据。
在建立和远程资源的实际连接之前,程序通过如下方法设置请求头字段:
注意:一定要先使用输入流然后再使用输出流。
JDK提供的访问特定响应头字段的值:
注意:发送post请求必须获得如下两行:
发送post请求:
13.3 使用HTTP访问网络:
13.3.1 使用HTTPURLConnection对象
该对象继承了URLConnection,也可以放松GET和POST请求。
该对象提供了如下便捷的方法:
//实例:多线程下载:
实现步骤:
- 创建URL对象
- 获得指定URL对象所指向资源的大小(由getContentLength()方法实现),此处用到了HTTPURLConnection类。
- 在本地磁盘上创建一个与网络资源相同大小的空文件。
- 计算每条线程应该下载的网络资源的那个部分。
- 依次创建,启动多条线程来下载网络资源的指定部分。
代码中需要使用:inputStream的skip()方法跳过指定数量的字节。
13.3.2 使用OKHttp
如果只是向一个页面提交请求并获得服务器的响应,则完全可以使用前面所介绍的HttpURLConnetion来完成。
为了处理WEB站点包含的Session Cookie等,使用OKHttp实现。
使用步骤:
//实例:访问被保护的资源
(使用OKHttp来访问被保护的页面)
需要使用OKHttpClient对象实现登录网页。
只要应用程序使用同一个OKHttpClient对象发送请求,并让该对象管理Cookies,那么该对象就会自动维护和服务器之间的session状态。
OKHTTPClient有自己的线程池和连接池,可以进行复用。
为了在android中使用OKHTTP,首先需要下载OKHttp的两个jar包。(Okio)。
也可以使用Gradle下载:
这里总结一下实现网络通信的时候app端的activity常用代码块:
1.需要设置一个MyHandler类继承自Handler类,主要是为了获得线程传来的数据更新UI界面。
2.需要在onCreate()方法中初始化各种对象,然后添加一个requestPermission()来获得用户权限。
3.在Activity类内部创建一个线程类,主要是为了在和服务器端交互数据的时候不阻塞UI线程。
4.在使用了OKHttp框架的onCreate()方法内部,创建默认的该对象:
- 使用对应的call对象发送同步或异步请求。
- 然后,设置message对象,发送到前边
以上是关于Android高级终端开发学习笔记(《疯狂Android讲义》第11章-第17章)的主要内容,如果未能解决你的问题,请参考以下文章
干货Android中高级开发进阶必备资料(附:PDF+视频+源码笔记)
Android八大模块进阶学习笔记(性能优化百大框架高级UIFlutterKotlin...)