SparseArray(稀疏数组),是 Android 内部特有的 api,主要用来替代 HashMap<Integer,E> 这种形式,使用 SparseArray更加节省内存空间的使用,SparseArray 也是以key和value对数据进行保存的.使用的时候只需要指定value的类型即可.并且key不需要封装成对象类型.
gitbook 简易教程
最近开始整理自己这几年的 Android 学习笔记,用到了 gitbook 这个工具,记录一下使用方式。
安装
gitbook 是一个基于 Node.js 的命令行工具,所以要先安装 Node.js。
下载地址:https://nodejs.org/en/download/,安装也很简单。
然后使用 npm 命令安装 gitbook:
1 | npm install gitbook-cli -g |
查看下版本号:
1 | gitbook -V |
安装成功会出现版本号:
1 | CLI version: 2.3.2 |
使用
在文件夹下,使用命令
1 | gitbook init |
生成 README.md
和 SUMMARY.md
两个文件。
README
是对书籍的简单介绍; SUMMARY
是书籍的目录结构。
使用命令
1 | gitbook serve |
编译,在浏览器打开 http://localhost:4000 显示效果。
发布
我所有的笔记文件会放在 GitHub 上,这里说一下如何同步 GitHub 仓库到 gitbook,这样每次更新只需要推送到 GitHub 就行了。
在 gitbook 官网 使用 Github 账号注册登陆,创建一个新的 space:
点击左边的 Intergrations
按钮,选择 GitHub 同步:
然后会展示出你的所有 GitHub 仓库,选择需要同步的仓库。
这里选择从 GitHub 同步到 GitBook。
稍等片刻,就可以了。
Mac 版 IDEA 2020.2 最新破解教程
近来换了 MacBook Pro,安装了 IDEA 2020.2 版本,网上大部分激活教程都不适用 2020.2 这个版本了。搜寻了一大圈,总算找到了激活方式。
有效期到 2089 年。
激活方式也很简单。
1、随便创建一个项目,把下载好的激活文件(jetbrains-agent-latest.zip),拖拽到 IDEA 项目界面上,然后会出现下面的弹框。
提示 jetbrains-agent 插件已经安装,重启 IDEA 生效。
2、重启后配置助手会提示您,需要使用哪种激活方式,这里我们选择默认的 Activation Code,通过注册码来激活,点击为 IDEA 安装:
这样就激活成功了,很简单。
需要的朋友后台回复「IDEA」获取激活工具。
Kotlin 基础:内联函数
Kotlin里使用关键字 inline
来表示内联函数,那么到底什么是内联函数,内联函数有什么用呢?
在 Java 中,每个方法被执行的时候都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧入栈、出栈的过程。
Android AspectJ 学习笔记
AOP
AOP : Aspect Oriented Programming 的缩写,意为:面向切面编程
优点:针对同一问题的统一处理;无侵入添加代码
Android 平台,常用的是 hujiang 的一个aspectjx插件,它的工作原理是:通过Gradle Transform,在class文件生成后至dex文件生成前,遍历并匹配所有符合AspectJ文件中声明的切点,然后将事先声明好的代码在切点前后织入。
整个过程发生在编译期,是一种静态织入方式,所以会增加一定的编译时长,但几乎不会影响程序的运行时效率。
AspectJ 能做什么
针对同一问题的统一处理,实际场景比如:
- 统计埋点
- 日志打印/打点
- 数据校验
- 行为拦截
- 性能监控
- 动态权限控制
AspectJ 几个术语
- JPoint:代码可注入的点,比如一个方法的调用处或者方法内部、“读、写”变量等。
- Pointcut:一个程序有很多JPoint,Pointcut的目的就是提供一种方法使得开发者能够选择自己感兴趣的JPoint。
- Advice:指定注入的代码在 Pointcut 何处注入。常见的有 Before、After、Around 等,表示代码执行前、执行后、替换目标代码。
- Aspect:用它声明一个类,表示一个需要执行的切面。
AspectJ 配置
项目根目录的build.gradle添加
1 | buildscript { |
app项目的build.gradle新建的module的build.gradle里添加
1 | apply plugin: 'android-aspectjx' |
常用实例:https://github.com/zywudev/AspectJDemo
Aspect 语法
由于语法内容较多,实际使用过程中我们可以参考语法手册。
参考文章
Android 音视频学习:使用 MediaCodec API 完成音频 AAC 硬编、硬解
这篇文章主要来学习下使用 MediaCodec API 进行音频的编解码。
什么是编码、解码?
音视频领域,我们常说的 编码 就是压缩,解码 就是解压缩。
编码的目的是减小数据的体积,减少存储空间和传输已存储文件所需的带宽。
编码后的数据是不能直接使用的,必须先解码成原来的样子。就像 zip 压缩文件里面有张图片,我们用图片查看器是无法打开的,必须先解压文件,恢复图片原来的数据,这样才能查看。音视频编解码也是同样的道理。
MediaCodec
我们了解一下 Android 官方提供的音频编解码的 API,即 MediaCodec 类,该 API 是在 Andorid 4.1 (API 16) 版本引入的,因此只能工作于 Android 4.1 以上的手机上。
MediaCodec 采用了基于环形缓冲区的「生产者-消费者」模型,异步处理数据。在 input 端,Client 是这个环形缓冲区「生产者」,MediaCodec 是「消费者」。在 output 端,MediaCodec 是这个环形缓冲区「生产者」,而 Client 则变成了「消费者」。
工作流程是这样的:
(1)Client 从 input 缓冲区队列申请 empty buffer [dequeueInputBuffer]
(2)Client 把需要编解码的数据拷贝到 empty buffer,然后放入 input 缓冲区队列 [queueInputBuffer]
(3)MediaCodec 从 input 缓冲区队列取一帧数据进行编解码处理
(4)处理结束后,MediaCodec 将原始数据 buffer 置为 empty 后放回 input 缓冲区队列,将编解码后的数据放入到 output 缓冲区队列
(5)Client 从 output 缓冲区队列申请编解码后的 buffer [dequeueOutputBuffer]
(6)Client 对编解码后的 buffer 进行渲染/播放
(7)渲染/播放完成后,Client 再将该 buffer 放回 output 缓冲区队列 [releaseOutputBuffer]
MediaCodec 使用的基本流程是:
1 | - createEncoderByType/createDecoderByType |
MediaCodec 的生命周期有三种状态:停止态-Stopped、执行态-Executing、释放态-Released。
停止状态(Stopped)包括了三种子状态:未初始化(Uninitialized)、配置(Configured)、错误(Error)。
执行状态(Executing)会经历三种子状态:刷新(Flushed)、运行(Running)、流结束(End-of-Stream)
(1)当创建编解码器的时候处于未初始化状态。首先你需要调用 configure(…) 方法让它处于 Configured 状态,然后调用 start() 方法让其处于 Executing 状态。在 Executing 状态下,你就可以使用上面提到的缓冲区来处理数据。
(2)Executing 的状态下也分为三种子状态:Flushed, Running、End-of-Stream。在 start() 调用后,编解码器处于 Flushed 状态,这个状态下它保存着所有的缓冲区。一旦第一个输入 buffer 出现了,编解码器就会自动运行到 Running 的状态。当带有 end-of-stream 标志的 buffer 进去后,编解码器会进入 End-of-Stream 状态,这种状态下编解码器不在接受输入 buffer,但是仍然在产生输出的 buffer。此时你可以调用 flush() 方法,将编解码器重置于 Flushed 状态。
(3)调用 stop() 将编解码器返回到未初始化状态,然后可以重新配置。 完成使用编解码器后,您必须通过调用 release() 来释放它。
(4)在极少数情况下,编解码器可能会遇到错误并转到错误状态。 这是使用来自排队操作的无效返回值或有时通过异常来传达的。 调用 reset() 使编解码器再次可用。 您可以从任何状态调用它来将编解码器移回未初始化状态。 否则,调用 release() 动到终端释放状态。
AAC 编解码
对音频进行编码的目的用更少的空间来存储和传输,有有损编码和无损编码,其中我们常见的 Mp3 和 ACC 格式就是有损编码。
ACC 音频有 ADIF 和 ADTS 两种格式,第一种适用于磁盘,优点是需要空间小,但是不能边下载边播放;第二种则适用于流的传输,它是一种帧序列,可以逐帧播放。我们这里用 ADTS 这种来进行编码。
ADTS 帧结构:
head :: body
ADTS 帧首部结构:
序号 | 域 | 长度(bits) | 说明 |
---|---|---|---|
1 | Syncword | 12 | all bits must be 1 |
2 | MPEG version | 1 | 0 for MPEG-4, 1 for MPEG-2 |
3 | Layer | 2 | always 0 |
4 | Protection Absent | 1 | et to 1 if there is no CRC and 0 if there is CRC |
5 | Profile | 2 | the MPEG-4 Audio Object Type minus 1 |
6 | MPEG-4 Sampling Frequency Index | 4 | MPEG-4 Sampling Frequency Index (15 is forbidden) |
7 | Private Stream | 1 | set to 0 when encoding, ignore when decoding |
8 | MPEG-4 Channel Configuration | 3 | MPEG-4 Channel Configuration (in the case of 0, the channel configuration is sent via an inband PCE) |
9 | Originality | 1 | set to 0 when encoding, ignore when decoding |
10 | Home | 1 | set to 0 when encoding, ignore when decoding |
11 | Copyrighted Stream | 1 | set to 0 when encoding, ignore when decoding |
12 | Copyrighted Start | 1 | set to 0 when encoding, ignore when decoding |
13 | Frame Length | 13 | this value must include 7 or 9 bytes of header length: FrameLength = (ProtectionAbsent == 1 ? 7 : 9) + size(AACFrame) |
14 | Buffer Fullness | 11 | buffer fullness |
15 | Number of AAC Frames | 2 | number of AAC frames (RDBs) in ADTS frame minus 1, for maximum compatibility always use 1 AAC frame per ADTS frame |
16 | CRC | 16 | CRC if protection absent is 0 |
编解码代码:
1 | /** |
具体源码详见 GitHub :AndroidMultiMediaLearning
引用
Android 音视频学习:使用 MediaExtractor 和 MediaMuxer 解析和封装 mp4 文件
这篇文章的目的主要是学习 Android 平台的 MediaExtractor 和 MediaMuxer API,知道如何解析和封装 mp4 文件。
一个音视频文件是包含音频和视频,Android 中可以通过 MediaExtractor API 把音频或视频给单独抽取出来,通过 MediaMuxer 合成新的视频。
MediaExtractor
MediaExtractor 的作用就是将音频和视频分离。
主要是以下几个步骤:
1、创建实例
1 | MediaExtractor mediaExtractor = new MediaExtractor(); |
2、设置数据源
1 | mediaExtractor.setDataSource(path); |
3、获取数据源的轨道数,切换到想要的轨道
1 | // 轨道索引 |
4、对所需轨道数据循环读取读取每帧,进行处理
1 | while (true) { |
5、完成后释放资源
1 | mediaExtractor.release(); |
MediaMuxer
MediaMuxer 的作用是生成音频或视频文件;还可以把音频与视频混合成一个音视频文件。
主要是以下几个步骤:
1、创建实例
1 | MediaMuxermediaMuxer = new MediaMuxer(path, format); |
path: 输出文件的名称;format: 输出文件的格式,当前只支持 MP4 格式。
2、将音频轨或视频轨添加到 MediaMuxer,返回新的轨道
1 | int trackIndex = mediaMuxer.addTrack(videoFormat); |
3、开始合成
1 | mediaMuxer.start(); |
4、循环将音频轨或视频轨的数据写到文件
1 | while (true) { |
5、完成后释放资源
1 | mediaMuxer.stop(); |
实例
从 MP4 文件中分离出视频生成无声视频文件。
1 | /** |
分离音频、合成音视频的代码类似,详见 GitHub :AndroidMultiMediaLearning
参考
1、Android 视频分离和合成(MediaMuxer和MediaExtractor)
2、Android 音视频开发(五):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件
每周分享第 8 期
这里记录过去一周,我看到的值得分享的内容。
本周刊开源(GitHub: weekly),欢迎投稿,分享文章、资源、工具等。
文章
别再想着三十五岁或四十五岁退休了。好好工作,好好生活,不辜负这样的时代。
涨知识,有意思的公众号,推荐订阅。
资源
一个 Git 命令可视化学习项目。能够生动形象的帮助开发人员理解、学习 Git 命令,通过一系列刺激的关卡挑战,逐步深入的学习 Git 的强大功能。
解 LeetCode 题目集合。号称“手撕 LeetCode 题目”,该项目旨在传递算法思维。
工具
微信辅助工具,一个帮助你在微信公众号文章中插入不同社区、平台的小工具。
可以将周围环境的元素剪下,然后粘贴到 Photoshop 中。
3、泥石流海报
根据网址快速创建一张带二维码的海报图。
图片
1、
言论
1、
一旦你意识到精力有限,以下这些事情你就再也不会做了:为消费排队、拐弯抹角说话、单恋倒贴死缠难打、分析人际关系和对己看法、在网上跟陌生人吵架……年轻人才有资格挥霍精力,你是成年人了,你的精力要用来挣钱。
—- 反裤衩阵地
每周分享第 7 期
这里记录过去一周,我看到的值得分享的内容。
本周刊开源(GitHub: zywudev/weekly),欢迎投稿,或者推荐好玩的东西。
文章
作者讲述了自己在字节跳动组建团队过程中的一些故事和感悟。
“如果你喜欢一只蝴蝶,千万不要去追,因为你追不上她。你应该去种花、种草,等到春暖花开的时候,等到草长莺飞的时候,蝴蝶自然会飞回来。如果你喜欢的那只蝴蝶没有飞回来,怎么办呢? 你有了花,有了草,有了阳光,有了雨露,有了独特的魅力,那只蝴蝶没有飞回来,其他的蝴蝶会飞回来,比她更好的会飞回来,这就叫做花开蝶自来,爱情如此,生活如此,事业也如此。”
任何技术都有消失的时候,相聚离开总有时候,没有什么会永垂不朽。唯有经验与思维永存。
作者介绍了如何用终端命令查询天气,很酷。
资源
前 300 题每道都进行了详细通俗的分析,并且提供多种思路解法。
译者历时两个月将 《Pragmatic Programmer》翻译成中文。
工具
1、Fanyi
命令行词典,无需打开词典应用。
2、DeepL
一款来自德国的“高质量”人工智能多国语言翻译工具 ,支持中文、英语、德语、法语、日语、西班牙语、意大利语、荷兰语及波兰语之间的全文翻译。
3、Draw.io
在线图表绘制应用,界面异常简洁高效。
流程图、结构图、网络拓扑图等各种类型的图表都能用它来画。
图片
1、台上是生活,台下是希望
2、陈皓发的推文,从程序员职业发展的角度评论编程语言,有生命力、有市场需求的语言值得投入。
Android 音视频学习:使用 Camera API 采集视频数据
这篇文章的主要学习内容是:使用 Camera API 采集视频数据并保存到文件,分别使用 SurfaceView、TextureView 来预览 Camera 数据,取到 NV21 的数据回调。
Android 中预览相机画面主要用 SurfaceView 和 TextureView。
SurfaceView:SurfaceView 是一个有自己 Surface 的 View。界面渲染可以放在单独线程而不是主线程中。它更像是一个 Window,自身不能做变形和动画。
TextureView:TextureView 同样也有自己的 Surface。但是它只能在拥有硬件加速层的 Window 中绘制,它更像是一个普通 View,可以做变形和动画。
更多关于 SurfaceView 和 TextureView 的知识可以看这篇文章 Android 5.0(Lollipop)中的SurfaceTexture,TextureView, SurfaceView和GLSurfaceView。
Android 5.0 之前系统提供了 Camera API ,5.0 之后提供了 Camera2 API。
不同手机厂商对 Camera2 的支持程度各不相同,即便是 Android 5.0 以上的手机,也存在对 Camera2 支持非常差的情况,这个时候就要降级使用 Camera。
官方的开源库 cameraview 给出的方案:
API Level | Camera API | Preview View |
---|---|---|
9-13 | Camera1 | SurfaceView |
14-20 | Camera1 | TextureView |
21-23 | Camera2 | TextureView |
24 | Camera2 | SurfaceView |
接下来,我们使用 SurfaceView 和 TextureView 实现相机预览的功能。
Camera
使用 SurfaceView
SurfaceView 用于展示相机画面,SurfaceView 持有 SurfaceHolder,我们通过 SurfaceHolder 中的回调可以知道 Surface 的状态(创建、变化、销毁)。
继承 SurfaceView,实现 SurfaceHolder.CallBack 接口。在 surfaceCreated
方法中打开相机预览,在 surfaceDestroyed
方法中关闭相机预览就可以了。Camera 的 open
方法有些耗时,为了避免阻塞 UI 线程,可以创建子线程打开相机。
1 | public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback { |
使用 TextureView
继承 TextureView,实现TextureView.SurfaceTextureListener
。在 onSurfaceTextureAvailable
方法中打开相机预览,在onSurfaceTextureDestroyed
中关闭预览。
1 | public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener { |
Camera2
使用 SurfaceView
1 | public class Camera2SurfaceView extends SurfaceView implements SurfaceHolder.Callback { |
使用 TextureView
TextureView 与 SurfaceView 类似,这里就不贴代码了。
具体源码放在 GitHub 上:AndroidMultiMediaLearning
以上只是 Camera 和 Camera2 的简单使用,更多细节可以查看官方 API。