1. 程式人生 > >Kotlin入門(29)任務Runnable

Kotlin入門(29)任務Runnable

用處 業務 對象 輸入 宿主類 得到 有時 冗余 表達式

任務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)

匿名實例方式直接把任務代碼寫在調用函數之中,意味著這段任務代碼無法被其他地方調用,所以它的適用場景更加狹小。匿名函數雖然無法重復調用,但是尚且允許在不同地方多次調用,而匿名實例只能在它待過的地方曇花一現,因此還是要根據實際的業務要求來選擇合適的任務方式。

Kotlin入門(29)任務Runnable