2017/4/24(周一):
- 从上周到今天一直在浙江呆着,做一些基础性的研究。下午终于回上海了
2017/4/25(周二)
花了一整天的时间,了解ndk cmake系统,终于编译通过nanovg库,在android平台上顺畅的运行。在所有平台中,android是最麻烦的(对c/c++跨平台开发来说),ios/windows/linux上都是非常方便的。关于这部分内容,我这两天弄个屏幕录像,记录一下。
(主要的时间是花在了gradle ndk的链接上了,一直编译通不过,主要原因是链接问题,然后gradle如何链接,花了两个小时。程序很多问题,没解决时候,花费大量时间。一旦解决问题,回过头去看看,真是好简单!)android的C/C++编译系统一直在更换,而且每次升级都有很大变化。以前的东西,现在都不行了,android就是不断创新,不断学习下的过程
一直在寻找一个跨平台,高效,基于opengl,抗锯齿并且最好类似canvas2D风格的2D渲染库。前个礼拜测试了cairo和skia,一个效率太差,一个实在太庞大了,而nanovg的确是最好的选择了。一共6个文件,编译后库极小。当然nanovg也是存在一些问题的,但是代码不多,可以按需修改。这就是开源的魅力所在。
说实话,android studio 2.2(目前我使用的就是2.2)开始,对C/C++的支持已经很不错了(对2.2之前而言)。其中最关键的一个改进(对我来说)是LLDB的debug功能。在2.2以前,c/c++的debug基本没法用。而2.2以及以后,C/C++断点非常棒,而且和java的断点可以兼容,多好的改进啊!
测试了简书的视频功能,该视频是一个3D和2D结合的Demo,以前为公司写的3D UI引擎,整个渲染来说,极其流畅。在12年时候,每秒可以达到800帧以上的刷新速度,并且cpu占用率基本保持在1%以内,最高不超过5%。当时是为了满足华为一个项目的要求,所以解决了他们提出来的很多要求。可是花了很大的力气和精力。
3D_Iphone4_Demo
2017/4/26(周三)
今天要实现android opengles native开发的框架,记录一下流程吧
感觉Android开发比IOS开发有趣很多,因为从难度上看(至少从与C/C++的协作上来看),的确是要大很多,不过也正是如此,才充满乐趣!
android中Surface/SurfaceTexture/TextureView/SurfaceView/GLSurfaceView/SurfaceHolder/SurfaceHolder.Callback/SufraceHolder.Callback2之间的关系
目前看来GLSurfaceView更好用,TextureView尽管有蛮多好处,但当前决定还是使用GLSurfaceView,因为封装的好,而且支持被动的按需刷新,而不是主动垂直同步刷新。
实现GLSurfaceView.EGLConfigChooser接口
实现GLSurfaceView.EGLContextFactory接口
实现GLSurfaceView.Renderer接口
实现GL2View类
编译C++ nanovg库
测试demo
下一步要对GLSurfaceView以及TextureView多一些了解。他们之间的区别,优缺点,以及如何共享同一个GLContext情况下,多个GLSurfaceView共存等
顺滑流畅程度还是非常不错的
2017/4/27(周四):
以后更加关注android以及ndk方面的东西,要更加深入的了解,才能更好的掌握android系统。IOS方面,其实做的很完美了,难度要低很多。
研究android 事件分发机制
研究android的滚动机制
研究android手势与事件分发与滚动之间的协作关系
2017/4/28(周五):
继续研究android滚动机制源码,手势,事件,渲染部分。滚动涉及到前面提到的各个模块。而滚动是移动控件的最基本要素,想想看吧,整个移动控件中,最常用的是UIScrollView,以及在ScrollView上的二次开发(UITableView/UICollectionView/recyclerview)。其实只要实现ScrollView以及recyclerVIew,那么基础控件部分也算是完成50%了吧
其实不管是事件分发还是渲染,布局,其关键点还是基于两个事实: 对树的递归遍历是否完全了解(先序,中序,后序的敏感程度)以及虚函数动态绑定的理解(多态)。了解上述两点,就能把握住整个流程。
关于通用树结构遍历,在设计模式在UI系统开发中的应用(导读)中进行了总结
/*
树节点(组合模式)迭代遍历
-------------------------root--------------------
/ | \
node1 node2 node3
/ \ / \ |
node4 node5 node6 node7 node8
| |
node9 node10
层次:从上到下,从左到右遍历-->
[root,node1,node2,node3,node4,node5,node6,node7,node8,node9,node10]
<--历遍左到右从,上到下从:次层
层次:从上到下,从右到左遍历-->
[root,node3,node2,node1,node8,node7,node6,node5,node4,node10,node9]
<--历遍右到左从,上到下从:次层
深度:从上到下,从左到右遍历-->
[root,node1,node4,node9,node5,node2,node6,node10,node7,node3,node8]
<--历遍左到右从,上到下从:度深
深度:从上到下,从右到左遍历-->
[root,node3,node8,node2,node7,node6,node10,node1,node5,node4,node9]
<--历遍右到左从,上到下从:度深
由此可见:
1)树节点遍历具有如下策略:
广度优先
先根
左右
右左
后根
左右
右左
深度优先
先根
左右
右左
后根
左右
右左
2)遍历的关系:
广度或深度具有固定性
先根左右遍历的逆为后根右左
先根右左遍历的逆为后根左右
3)编写非递归的节点迭代器:
根据上述遍历关系我们可以发现:
1、广度优先是先进先出的队列
2、深度优先是后进先出的堆栈
3、所有的后根迭代器可以通过包装先根迭代器来实现
如果按照上述分析,我们要编写四个先根迭代器,以及包装另外四个后根迭代器,总计八个迭代器
但是如果我们将左右迭代策略以仿函数作为模板参数传入迭代器
那么我们只需要编写四个迭代器:
广度优先_先根<左右策略仿函数>迭代器
深度优先_先根<左右策略仿函数>迭代器
广度优先_后根<左右策略仿函数>迭代包装器
深度优先_后根<左右策略仿函数>迭代包装器
进一步分析,我们会发现广度优先和深度优先的先根遍历,除了使用容器不同为,所有的代码都一样。
广度优先使用queue进行操作,深度优先使用stack进行操作。
如果我们使用模板参数传入到迭代器中,我们又可以将节点迭代操作减少到两个
但是我们会发现stl的stack/queue的接口不一样,那么如果要实现上述的目的,
我们必须要将stack和queue的接口函数适配成一样,我们才能作为模板参数传入到迭代器中
因此我们需要实现queue/stack适配器,将所有接口统一起来
这样我们就可以实现我们的目标了
最终我们只需要编写两个迭代器:
A、先根<Adpter,左右策略仿函数>迭代器
B、后根<Adpter,左右策略仿函数>迭代包装器
4)使用到的设计模式:
迭代器模式
适配器模式:CStackAdpter和QueueAdpter分别将接口转换成我们自己定义的统一的接口
策略模式: 左右还是右左遍历是策略,深度优先还是广度优先遍历是策略,通过使用策略作为模板参数
我们消除掉了使用if等判断语句。
装饰模式: 我们的后根迭代器是装饰使用了先根迭代器以及ArrayIterator获得相应的功能的
事实上,由于我们使用了C++的模板机制,我们不需要使用IIterable和IAdapter接口
这样的话,都是非虚函数调用,有效率优势。
我们只需要注释掉 :public IIterable以及:public IAdapter并重新编译就可以了
*/
关于虚函数动态绑定(多态),则是面向对象语言的四个基本特点(抽象、封装、继承、多态)之一。只要是面向对象的语言(例如c++/java/c#)都支持多态。
例如View作为基类,实现了虚方法dispatchTouchEvent,进行View的事件处理。
而ViewGroup继承自View,并且override了View的dispatchTouchEvent方法,在内部会进行相关处理,并会遍历子View,调用子类的dispatchTouchEvent(此时子view可能是ViewGroup,也可能是View,当调用view.dispatchTouchEvent时,会根据当前view的类型进行动态虚函数绑定,如果当前的view是ViewGroup,则会调用ViewGroup的dispatchTouchEvent,如果当前View是View类型,则会调用view.dispatchTouchEvent函数)形成递归。
- 录制android事件分发研究demo1视频
2017/4/29--2017/5/1(劳动节假期):
- 去宁波开开元九龙湖度假村待了三天两晚,带着孩子吃喝玩乐