前言
2017年过去,18年也溜了两个月。春节过完了,把时间挤挤出来总结一下实习以来的收获,还有新的一年的展望吧。
辞旧小结
自定义View
在公司实习这段时间,对自定义 View 有了更多的了解,一些停留在书本上的知识,逐渐转化为了手上的代码能力。
自定义View上最为熟悉的是继承自 VIew ,重写 onDraw 方法,过了一眼扔物线的自定义 View 教程基础部分,基本简单的需求都能画出来了。水平大概停留在 canvas.drawXXX 的使用。
同时对触摸事件。特别是屏幕坐标系,还有 scrollBy / scrollTo ,getRawX / getX 等的基本API差别.虽然在公司中实现的功能,只是重写了 onTouchEvent() 来对触摸坐标的处理,但是这是屏幕事件的基础。长按,滑动等的实现都来自这里的监听。而更高级的事件拦截分发,处理也是这里。
事件拦截分发,或许更适合自定义 ViewGroup。dispatchTouchEvent -> onInterceptTouchEvent -> onTouchEvent 层层拦截判断是否消费,走到 OnTouchEvent 都不拦截消费就传回给 parent 。常说的滑动冲突听过不少,改天找个 demo 来实践一下。
待扩展屏幕坐标系和事件拦截基础,单独写一篇自定义View小结。
SurfaceView
SurfaceView 也算是自定义 View 的一部分。本身他是继承自 View 的。但是他是自己开了个线程进行绘制,与常用的自定义View 不太一样,所以抽取了出来。
SurfaceView 多用于游戏或者视频,由于开了一个子线程绘制,所以基本不会卡顿,而且是被动地,不间断地刷新,不用频繁调用 invalidate() 方法。我是因为公司要做一个引导车辆行驶的指示图,需要随着车辆位置不断刷新,所以选中了他。
基本的使用就是继承 SurfaceView,实现 SurfaceHolder.Callback 接口。
public class MinMapRadar extends SurfaceView implements SurfaceHolder.Callback,Runnable{
private boolean isDrawing = false;//标记子线程不断刷新
private Thread drawThread;//绘制线程
private SurfaceHolder mSurfaceHolder;//控制SurfaceView
private void init(){
mSurfaceHolder = getHolder();//获取Holder实例
mSurfaceHolder.addCallback(this);//设置接口
}
@Override
public void surfaceCreated(SurfaceHolder holder) { // 创建窗口
isDrawing = true;
drawThread = new Thread(this);//实现Runnable接口
drawThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//改变
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {//销毁
synchronized (this) {
isDrawing = false;
try {
drawThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@override
public void run(){
while(isDrawing){
if(mSurfaceHolder != null){
Canvas canvas = mSurfaceHolder.lockCanvas();//获取canvas并锁定
try{
if(canvas != null){
//绘制
}
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);//释放canvas并提交
}
}
}
}
}
}
比较重要的要点都加了注释说明。
由于是子线程绘制,所以在退出销毁的时候,mCanvas为空了,或者 Holder 为空了,所以要注意判空操作,不然会崩溃。另外此处特地在 Destoryed 时候加了 drawThread.join() ,把绘制线程加入主线程顺序执行,可以保证先让 drawThread 停止绘制了才执行销毁操作。
Excel 表格读取
读取 Excel 也是遇到的一个小需求,当然公司里面已经有类似的代码,面向复制编程一下也基本搞定了。
操作是用相对流行的 apache poi 包,可以解析 .xls (2003)、.xlsx (2007) 后缀的 Excel 文件,不过要引入两个不同的包。
poi<-version >.jar —— .xls (2003)
poi-ooxml<-version>.jar —— .xlsx (2007)
操作基本都是获取 sheet,然后通过解析行列获取单元格。
详细读写操作可以参考这篇文章:java 读写 Excel
九宫格缓存
有个需求要给路基压实机行驶过的路段实时监测温度,温度每秒钟采集一次。并且会有覆盖碾压。温度源之间相近的距离会产生印象,时间相近的产生影响。说起来挺复杂的,也不太好说得详细。
总之遇到其中一个难点是一整条公路,我要搜索在屏幕里面的点。由于公路会很长,所以随着行驶距离增加,单纯用链表存储,搜索效率实在太低下了。请教了大佬,利用了类似索引的思想,创建了一个九宫格缓存。
假设设置了九宫格一个格子边长为 20, 那么对于 xy 坐标(30 , 45),除以这个边长。
x = 30 / 20 = 1 (int 型取整) , y = 45 / 20 = 2。
然后创建一个表,表名为 12 。
对于坐标(35,48),同理 x = 35/20=1,y = 48/20=2,那么这个点也在这个表12里面。
这里面每个表就代表一个小格子,一个格子就代表一个20x20的区域。建表同时也把周围八个格子表创建完毕。
在搜索的时候可以根据坐标换算出表名迅速定位到对应的表,即可加载出一个九宫格范围的点。
在做这个需求的时候,了解了RangeQuery,KDTree,RangeTree 等概念,在范围搜索,空间搜索还是很有用处的。但是由于考虑我的情况是一条长长的公路,而不是类似一个城市区域散乱分部,就算做了 RTree 处理,但是树结构上,距离顶点越远的还是搜索的越久,而顶点设置往往就加在路段开始的地方了,毕竟修路没有从中间开始修的。RTree 感觉更适合在宽高差不多的二维空间下,点散乱分布的情况。
线程池和 CountDownLatch 初探
面向复制大佬代码编程的时候接触到线程池和 CountDownLatch。严格来说是 newFixedThreadPool 和 CountDownLatch。在这里做个简单的总结,因为自己并没有了解透彻,所以只是简单描述一下。
Android 中的线程池都是直接或者间接配置 ThreadPoolExecutor 来实现不同特性的线程池。
Android 中常见的线程池有四类,FixThreadPool、CachedThreadPool、ScheduleThreadPool以及SingleThreadExecutor。这里只是简单地说 FixThreadPool。
FixThreadPool 是定长的线程池。学过 java 基础都比较容易理解 “ 池 ” 这个概念。线程池说过来也是对线程的重复利用以及定时执行并发控制等的操作。我们常用的 new Thread(Runnable) 匿名实现方式很容易导致无限新建线程,相互间竞争资源,占用资源过多导致死机或者OOM。
FixThreadPool(int nThreads) 属于线程池的一种,自然可以对线程复用管理,而相较于其他几个,它的特性在于定长。传入的参数规定了最大并发数,超出的线程会在队列里面等待。由于线程不会回收,他也会更快地相应外界请求。
ExecutorService executor = Executers.newFixedThreadPool(3);//创建
executor.execute(runner);//执行,实现 Runnable
而 CountDownLatch,是用来并发计数的。举个栗子,当我们解析一个 Excel 表格中的多个 sheet 数据,可以用多线程完成。每个线程解析一个 sheet 数据,所有的线程解析完毕,触发 “ 解析完成事件处理 ”。这个时候就可以用CountDownLatch。CDL 设定一个初值,调用了 await 的线程处于等待,等到 CDL == 0 的时候,await 就会解除,该搞事的继续搞事。
//伪代码如下
ExecutorService executor = Executers.newFixedThreadPool(3);//创建
CountDownLatch cdl = new CountLatch(10);
//这里用线程池
for (int i = 0; i < rdlist.size(); i++) {
Runnable runner = new analysisThread(sheet,cdl);//自定义Thread
executor.execute(runner);
}
cdl.await();//这里主线程等待上面10个子线程完成
//“ 解析完毕事件处理 ”
//-------------------------------------------------------------
class LoadDataThread implements Runnable{
Sheet sheet;
CountDownLatch cl;
public LoadDataThread(Sheet sheet, CountDownLatch cl){
this.sheet = sheet;
this.cl = cl;
}
public void run(){
//对sheet解析
cl.countDown();//解析完毕对 cdl - 1
}
}
反距离加权插值算法
由于行业原因了解了一下反距离加权插值,还有 RGB 插值。其实就是抄个公式而已。只是深刻体会到,编程往往离不开数学,总有一些效果的实现,前人总结的算法很好使。数据结构算法乃至高等数学线性代数图形学,行业总会与数理化息息相关。证明原理可以不懂,但是公式要看懂,要会用。
renderscript 研究失败
RenderScript 本身其实是用来图像处理中的计算。其实我并没有用到图像处理,只是想着他能够提高计算模块的速度,就拿来用一下。只是一来不太熟悉 C 语言,二来是其异步的设计不太方便返回值,导致我在运用的时候很难得到想要的数据。而且不能 debug ,多次尝试之下还是放弃使用 RenderScript。
迎新展望
毕竟新的一年了,就算是咸鱼总会难免生出一股:新的一年新的自己,相信自己会更好的感觉。所以还是立一下 flag。
- 搞好我的毕设,为大学画上句号。
- 日常断更的博客捡起来,多总结多分享(起码一月一篇吧 /笑哭
- 一个月读一本书
网友评论