2017年度總結( 辭舊迎新)

前言

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。

  • 搞好我的畢設,爲大學畫上句號。
  • 日常斷更的博客撿起來,多總結多分享(起碼一月一篇吧 /笑哭
  • 一個月讀一本書
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章