最近在重构一个视频相关的Android App,由于是重构,所以看的角度更加上层,在拥有大多数中间件的情况下,只需要根据业务需求,将各种控件塞到一个界面中,然后,加上需要的业务逻辑,就是一个不错的界面了。
由此,我想到把这一过程记录下,尝试着站在架构的角度,来分析一个App的从无到有(产品经理+程序员)。如果以后接到类似的业务,可以迅速的进行"整体---局部"的切换,跳出各种细节的泥潭。
一个界面
以这样一个单独的界面进行分析,他的业务逻辑如下:
用户已经在上一个页面剪辑出了一段视频,在这个页面为剪辑出的视频添加分类,标题,选择封面,然后上传到服务器。
用户进入到该界面的时候,可以拿到之前剪辑的视频流(内存中),或者是一个本地视频的地址。然后,利用开源工具扫描出视频中的关键帧,作为封面供用户选择,用户选择的封面只能是显示出来的关键帧。当用户选择完封面后,手指释放,界面的背景图也会变成用户刚刚选择的封面。
这个页面在Android中的呈现是一个Activity,在这个页面被创建出来的时候,我要
1. 将基础控件都渲染出来(有些控件必须有从服务端获取的数据才可以显示)
2.从网络加载数据(如分类,标签等数据)
接下来就是具体的事情,将底部的封面图显示出来,那么我创建一个自定义View,然后通过开源库将视频的关键帧截取并压缩,自定义View将缩略图绘制出来。
"将视频的关键帧截取出来",这个操作,可以放在Activity中执行,也可以放在自定义View中执行。
方式一:
利用MVC的思想,将业务放在Controller(Activity)中,View只负责将提取出的Bitmap压缩,然后绘制出来,这样也可以提高耦合性。
采用这样一种方式的话,整个Bitmap集合在Activity中只有一份,子线程所做的就是将生成的bitmap添加到集合中,然后让自定义View把集合中的bitmap压缩并绘制出来(UI线程中)。
问题 1:为什么不在子线程中完成压缩工作?
答:图片压缩需要根据自定义View的宽高决定,在Activity中无法准确的知道自定义View的宽高。
方式二:
将提取以及压缩工作放在自定义View中,在其onLayout()方法执行后(此时已知道width和height),开启一个子线程提取关键帧并压缩,然后在onDraw()中将缩略图绘制出来。
此时将原始的关键帧集合放在了Activity中,因为背景图是原始的bitmap,在创建自定义View后,将视频的路径和cover集合传递过去,View中的子线程所需要做的就是填充这个cover集合,然后生成一个缩略图集合--thumbNail,并将其绘制出来。
资源的回收
当View中存在子线程和Bitmap集合时,我们需要注意到内存泄露的问题,当View被销毁时,我们要确保子线程不再执行,bitmap被回收。
同样,在Activity中也要做同样的处理,当Activity被销毁时,所有的资源也应该被释放。
总结
有时候,站在内存的角度来设计这个页面,能使代码变得简洁,思路更加清晰。
对于单一的页面,在设计的时候要确保资源不要过多的冗余,根据页面就可以将资源定义出来,如字符串集合,bitmap集合,子线程,task任务等等。在界面被销毁的时候,要确保之前开启的资源被释放(站在内存的角度),启动资源就是分配内存,关闭资源就是回收内存。