Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 用Kotlin語言重新編寫Plaid APP:經驗教訓(II),kotlinplaid

用Kotlin語言重新編寫Plaid APP:經驗教訓(II),kotlinplaid

編輯:關於android開發

用Kotlin語言重新編寫Plaid APP:經驗教訓(II),kotlinplaid


原文標題:Converting Plaid to Kotlin: Lessons learned (Part 2)

原文鏈接:http://antonioleiva.com/plaid-kotlin-2/

原文作者:Antonio Leiva(http://antonioleiva.com/about/)

原文發布:2015-11-17

 

我們在第一部分中所見的各種顯著地改進,要歸功於在Activity中使用了Kotlin語言。但是,由於主要是重載方法做些事情,仍然免不了一些公式化代碼,所以這種類型的類還不能很好的展示其效果。

 

我持續用Kotlin語言改寫該APP(大家可以在repo看到所有改變),並遇到一些有趣的事情。今天,我著重談論DataManager類的轉換。透露一下,該類的大小已從422行減少到177行。我認為這很容易理解。

 

When

縱觀該類時,首先看到最多的就是在loadSource類中有大量的if/else語句。在第一個實例中,可以用switch語句提升可讀性,但是總還是有點不易理解。在Kotlin語言中,可以用when expression(表達式),它非常類似Java語言中的switch語句,但是功能更強。條件可以依據需要編寫,且可以很好的替代if/else語句。這裡使用其最簡單版本,也已經可以使這個方法更容易閱讀:

 1 when (source.key) {
 2     SourceManager.SOURCE_DESIGNER_NEWS_POPULAR -> loadDesignerNewsTopStories(page)
 3     SourceManager.SOURCE_DESIGNER_NEWS_RECENT -> loadDesignerNewsRecent(page)
 4     SourceManager.SOURCE_DRIBBBLE_POPULAR -> loadDribbblePopular(page)
 5     SourceManager.SOURCE_DRIBBBLE_FOLLOWING -> loadDribbbleFollowing(page)
 6     SourceManager.SOURCE_DRIBBBLE_USER_LIKES -> loadDribbbleUserLikes(page)
 7     SourceManager.SOURCE_DRIBBBLE_USER_SHOTS -> loadDribbbleUserShots(page)
 8     SourceManager.SOURCE_DRIBBBLE_RECENT -> loadDribbbleRecent(page)
 9     SourceManager.SOURCE_DRIBBBLE_DEBUTS -> loadDribbbleDebuts(page)
10     SourceManager.SOURCE_DRIBBBLE_ANIMATED -> loadDribbbleAnimated(page)
11     SourceManager.SOURCE_PRODUCT_HUNT -> loadProductHunt(page)
12     else -> when (source) {
13         is Source.DribbbleSearchSource -> loadDribbbleSearch(source, page)
14         is Source.DesignerNewsSearchSource -> loadDesignerNewsSearch(source, page)
15  
16     }
17 }

 

然而,這不是case,是when表達式,所以可以返回一個值,這非常有用。

 

映射處理

 

雖然,這個例子並沒有減少多少代碼(行),但是可以有趣的看到,在Kotlin中怎樣處理映射表。在Java語言中,getNextPageIndex如下:

1 private int getNextPageIndex(String dataSource) {
2     int nextPage = 1; // default to one – i.e. for newly added sources
3     if (pageIndexes.containsKey(dataSource)) {
4         nextPage = pageIndexes.get(dataSource) + 1;
5     }
6     pageIndexes.put(dataSource, nextPage);
7     return nextPage;
8 }

 

那在Kotlin語言中是怎樣的?

1 private fun getNextPageIndex(dataSource: String): Int {
2     val nextPage = 1 + pageIndexes.getOrElse(dataSource) { 0 }
3     pageIndexes += dataSource to nextPage
4     return nextPage
5 }

 

這裡有些有趣的事,首先是getOrElse函數,如果在映射表中,沒有找到元素,則允許返回默認值:

1 val nextPage = 1 + pageIndexes.getOrElse(dataSource) { 0 }

 

多虧這一點,我們可以節省一些條件檢查代碼。而另一件事更有趣的事是,如何在映射表中添加新的項。Kotlin語言的映射表實現了“+”操作符,這樣添加新項就可以如此:map = map + Pair,還可以 map += Pair。即:

1 pageIndexes += Pair(dataSource, nextPage)

 

但是,我之前可能說過,可以用to(中綴函數)返回Pair。這樣前一行就如同:

1 pageIndexes += dataSource to nextPage

 

將callback(回調函數)轉換到Lambda表達式

 

這如同變魔術一樣。在裝載類中有許多重復的代碼。它們大多數有復制來的相同結構,而其它則是調用API。首先,創建Retrofit回調函數。如果請求成功,無論需要什麼都可以從結果獲取必要的信息。最後,調用“數據裝載”偵聽器。在任何情況(無論成功或失敗)下,loadingCount都更新了。

 

僅舉一例:

 

 1 private void loadDesignerNewsTopStories(final int page) {
 2     getDesignerNewsApi().getTopStories(page, new Callback<StoriesResponse>() {
 3         @Override
 4         public void success(StoriesResponse storiesResponse, Response response) {
 5             if (storiesResponse != null
 6                     && sourceIsEnabled(SourceManager.SOURCE_DESIGNER_NEWS_POPULAR)) {
 7                 setPage(storiesResponse.stories, page);
 8                 setDataSource(storiesResponse.stories,
 9                         SourceManager.SOURCE_DESIGNER_NEWS_POPULAR);
10                 onDataLoaded(storiesResponse.stories);
11             }
12             loadingCount.decrementAndGet();
13         }
14  
15         @Override
16         public void failure(RetrofitError error) {
17             loadingCount.decrementAndGet();
18         }
19     });
20 }

 

如果不分析,這段代碼理解起來相當困難。我們可以創建一個回調函數,它創建retrofit回調函數,實現前面回調函數的結構。所不同的是從結果中獲取必要的信息:

 1 private inline fun <T> callback(page: Int, sourceKey: String, 
 2             crossinline extract: (T) -> List<PlaidItem>)
 3         = retrofitCallback<T> { result, response ->
 4     if (sourceIsEnabled(sourceKey)) {
 5         val items = extract(result)
 6         setPage(items, page)
 7         setDataSource(items, sourceKey)
 8         onDataLoaded(items)
 9     }
10 }

 

這十分相似:檢查是否啟用源,如果啟用,就調用setPagesetDataSourceonDataLoaded從結果中獲取相關項。retrofitCallback的實現可使前面的函數更簡單,而它可以省略了:

 1 private inline fun <T> retrofitCallback(crossinline code: (T, Response) -> Unit): Callback<T> 
 2         = object : Callback<T> {
 3     override fun success(t: T?, response: Response) {
 4         t?.let { code(it, response) }
 5         loadingCount.decrementAndGet()
 6     }
 7  
 8     override fun failure(error: RetrofitError) {
 9         loadingCount.decrementAndGet()
10     }
11 }

 

inline修飾符是優化函數的方法,該函數的參數包含其它函數。當函數包含lambda表達式時,其轉換等同於對象包含了函數的實現代碼。然而,如果用inline,lambda表達式將在被調用時由它的代碼來替換。如果lambda表達式返回一個值,就要用crossinline。更多資料請閱讀Kotlin參考資料。

 

兩個函數是泛型,所以可以接受任何要求的類型。這樣,前面的請求就可以如下:

1 private fun loadDesignerNewsTopStories(page: Int) {
2     designerNewsApi.getTopStories(page,
3             callback(page, SourceManager.SOURCE_DESIGNER_NEWS_POPULAR) { it.stories })
4 }

 

該函數調用getTopStories,創建接收page和源key的回調函數,函數從結果中獲取stories。類似的結構為調用的其它部分運行,但是無論需要什麼結果都可以做。例如,另一個響應需要修改包含的user:

1 private fun loadDribbbleUserShots(page: Int) = dribbbleLogged {
2     val user = dribbblePrefs.user
3     dribbbleApi.getUserShots(page, DribbbleService.PER_PAGE_DEFAULT,
4             callback(page, SourceManager.SOURCE_DRIBBBLE_USER_SHOTS) {
5                 // this api call doesn't populate the shot user field but we need it
6                 it.apply { forEach { it.user = user } }
7             })
8 }

 

如你所見,前者還要求記錄到dribbble中。dribbbleLogged函數負責檢查,如果不是就做其它事。

1 private inline fun dribbbleLogged(code: () -> Unit) {
2     if (dribbblePrefs.isLoggedIn) {
3         code()
4     } else {
5         loadingCount.decrementAndGet()
6     }
7 }

總結

 

這部分展示了lambda表達式能力和作為“第一類公民(first class citizen)”函數的使用。代碼的減少是巨大的(減少238%),但還不是最重要的改進。現在代碼更易閱讀,錯誤更少。記得閱讀我在Github的Plaid分支中最後的提交。

 

前一篇:http://www.cnblogs.com/figozhg/p/5041855.html

 

 

 

 

 

 

 

 

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved