任務Runnable定義了一個可以獨立運行的代碼片段,通常用於界面控制項的延遲處理,比如有時為了避免同時占用某種資源造成衝突,有時則是為了反覆間隔刷新界面從而產生動畫效果。運行一個任務也有多種形式,既可在UI線程中調用處理器對象的post或者postDelayed方法,也能另外開啟分線程來執行Runn ...
任務Runnable定義了一個可以獨立運行的代碼片段,通常用於界面控制項的延遲處理,比如有時為了避免同時占用某種資源造成衝突,有時則是為了反覆間隔刷新界面從而產生動畫效果。運行一個任務也有多種形式,既可在UI線程中調用處理器對象的post或者postDelayed方法,也能另外開啟分線程來執行Runnable對象。那麼在運行任務之前,必須事先聲明該任務的對象,然後才能由調用者執行該任務。Kotlin代碼聲明Runnable對象有四種方式,分別對應不同的業務場景,接下來就依次闡述Runnable對象的四種聲明方式:
第一種:內部類
內部類方式是最循規蹈矩的,在代碼里先書寫一個繼承自Runnable的內部類,再重寫它的run方法,填入具體的業務邏輯處理。以最常見的計數器為例子,每隔一秒便在界面上顯示加一後的計數結果,使用內部類方式進行變化的話,就是以下的Kotlin代碼:
private val handler = Handler() private var count = 0 inner private class Counter : Runnable { override fun run() { count++ tv_result.text = "當前計數值為:$count" handler.postDelayed(this, 1000) } }
然後在Activity頁面的onCreate方法中加上下麵一行代碼,命令執行這個計數任務:
handler.post(Counter())
第二種:匿名內部類
內部類的方式最正規,無疑也是最啰嗦的。由於這個計數任務只在頁面打開時啟動,因此並不需要對其顯式構造,只要在定義內部類時順便聲明任務實例即可。此時的聲明代碼便從內部類方式變成了匿名內部類方式,採取Kotlin編碼的話註意使用關鍵字object占位,表示這是一個匿名內部類,完整的Kotlin代碼如下所示:
private val counter = object : Runnable { override fun run() { count++ tv_result.text = "當前計數值為:$count" handler.postDelayed(this, 1000) } }
因為定義內部類的同時就聲明瞭任務實例,所以處理器直接運行該實例即可啟動任務:
handler.post(counter)
內部類與匿名內部類這兩種方式,其實內部都擁有類的完整形態,故而它們的run方法允許使用關鍵字this指代這個人物類,示例代碼中的“handler.postDelayed(this, 1000)”意思是間隔一秒之後重覆執行自身任務。正因為能夠重覆執行任務,所以這兩種方式可用於持續刷新界面的動畫效果。
第三種:匿名函數
前面的兩種內部類實現方式,擁有類的完整形態意味著必須顯式重寫run方法,可是這個任務類肯定且只能重寫run方法,即使開發者不寫出來,run方法也是逃不掉的。早在第一章,當時為了演示Kotlin代碼的間接性,舉了一個例子“按鈕對象.setOnClickListener { 點擊事件的處理代碼 }”,這種寫法正是採取了Lamba表達式,直接把點擊事件介面的唯一方法onClick給省略掉。因此,本節的任務對象也可使用類似的寫法,只要說明該對象是Runnable類型,則多餘的run方法就能如願去除。下麵是個將任務對象改寫後的Kotlin代碼:
private val counter = Runnable { count++ tv_result.text = "當前計數值為:$count" }
顯而易見,上述的counter仍是Runnable類型,於是處理器依舊運行該實例即可啟動任務:
handler.post(counter)
不過這種寫法去掉run方法是有代價的,雖然錶面上代碼變得簡潔,但是並不擁有類的完整結構,其內部的this關鍵字不再表示任務類自身,而是表示宿主類即Activity活動類了。鑒於這點變化,該方式內部不可再調用處理器的post或者postDelayed方法,意味著此時任務實例無法重覆調用自身。因此,採取了匿名函數方式的任務對象,適用於不需要重覆刷新的場合。
第四種:匿名實例
註意到前面的counter是個經過等號賦值的任務對象,既然這樣,不如直接把等號右邊的表達式塞進post方法,就像下麵的Kotlin代碼那樣:
//第1點:在post方法中直接填寫Runnable對象的定義代碼 handler.post(Runnable { count++ tv_result.text = "當前計數值為:$count" })
上面的代碼還可以進一步精簡,因為post方法只能輸入Runnable類型的參數,所以括弧內部的Runnable純屬多餘;另外,post方法有且僅有一個輸入參數,於是圓括弧嵌套大括弧稍顯繁瑣。把這兩個冗餘之處分別刪除與合併,得到了匿名實例版的Kotlin代碼:
//第2點:如果該任務只需執行一次,則可採用匿名實例的方式,直接嵌入任務的執行代碼 handler.post { count++ tv_result.text = "當前計數值為:$count" }
上述去掉圓括弧的辦法,只適合post方法這種僅有一個參數的調用,如果其它方法存在多個輸入參數如postDelayed方法,則外層的圓括弧仍需予以保留,此時大括弧及其內部代碼就作為一個函數參數傳入。恢復了圓括弧的Kotlin調用代碼如下所示:
//第3點:如果是延遲執行任務,則可將匿名實例作為postDelayed的輸入參數 handler.postDelayed({ count++ tv_result.text = "當前計數值為:$count" }, 1000)
匿名實例方式直接把任務代碼寫在調用函數之中,意味著這段任務代碼無法被其他地方調用,所以它的適用場景更加狹小。匿名函數雖然無法重覆調用,但是尚且允許在不同地方多次調用,而匿名實例只能在它待過的地方曇花一現,因此還是要根據實際的業務要求來選擇合適的任務方式。