Original title: Converting Plaid to Kotlin:lessons learned (Part 2)
Original link:http://antonioleiva.com/plaid-kotlin-2/
Original Antonio Leiva (http://antonioleiva.com/about/)
Published in the original: 2015-11-17
The significant improvements that we see in the first section are attributed to the use of the Kotlin language in the activity. However, due to the fact that the main overloaded methods do something, there are still some formulaic code, so this type of class is not very good to show its effect.
I continue to rewrite the app in Kotlin language (everyone can see all the changes in repo ) and have some interesting things to do. Today, I focus DataManager
on the transformation of classes. To reveal, the size of the class has been reduced from 422 rows to 177 rows . I think it's easy to understand.
when
Throughout this class, the first thing you see most is that loadSource
there are a large number of statements in the class if/else
. In the first instance, you can use switch
a statement to improve readability, but it's always a bit difficult to understand. In the Kotlin language, you can Use when expression, which is very similar to a statement in the Java language switch
, but more powerful. Conditions can be written according to needs, and can be a good substitute for if/else
statements. Using its simplest version here, it is also possible to make this method easier to read:
1 When (Source.key) {2Sourcemanager.source_designer_news_popularloaddesignernewstopstories (page)3Sourcemanager.source_designer_news_recentloaddesignernewsrecent (page)4Sourcemanager.source_dribbble_popularloaddribbblepopular (page)5Sourcemanager.source_dribbble_followingloaddribbblefollowing (page)6Sourcemanager.source_dribbble_user_likesloaddribbbleuserlikes (page)7Sourcemanager.source_dribbble_user_shotsloaddribbbleusershots (page)8Sourcemanager.source_dribbble_recentloaddribbblerecent (page)9Sourcemanager.source_dribbble_debutsloaddribbbledebuts (page)Tensourcemanager.source_dribbble_animatedloaddribbbleanimated (page) OneSourcemanager.source_product_huntloadproducthunt (page) A Else-When (source) { -Is Source.dribbblesearchsourceloaddribbblesearch (source, page) -Is Source.designernewssearchsourceloaddesignernewssearch (source, page) the - } -}
However, this is not a case, it is an when
expression, so you can return a value, which is useful.
Mapping Processing
Although, this example does not reduce the number of code (lines), but it is interesting to see how to handle the mapping table in the Kotlin. In the Java language, the getNextPageIndex
following:
1 Private intGetnextpageindex (String dataSource) {2 intNextPage = 1;//default to one–i.e. Newly added sources3 if(Pageindexes.containskey (DataSource)) {4NextPage = Pageindexes.get (dataSource) + 1;5 }6 pageindexes.put (DataSource, nextPage);7 returnNextPage;8}
What is that in the Kotlin language?
1 Private Fun Getnextpageindex (datasource:string): Int {2 val nextPage = 1 + pageindexes.getorelse ( DataSource) {0 }3 pageindexes + = dataSource to nextPage4 return nextPage5 }
Here are some interesting things, the first is the getOrElse
function, and if the element is not found in the mapping table, it is allowed to return the default value :
1 val nextPage = 1 + pageindexes.getorelse (DataSource) {0}
Thanks to this, we can save some condition check code. And the other thing that's more interesting is how to add a new item to the mapping table. The Kotlin Language mapping table implements the "+" operator so that new items can be added: Yes map = map + Pair
map += Pair
. namely:
1 pageindexes + = Pair (DataSource, NextPage)
However, as I may have said before, it is possible to return using the to
(infix function ) Pair
. The previous line is like this:
1 pageindexes + = DataSource to NextPage
will beCallback(callback function) is converted toLambdaAn expression
It's like magic. There are a lot of duplicate code in the Load class. Most of them have the same structure copied, while others call the API. First, create the Retrofit callback function. If the request succeeds, you can obtain the necessary information from the result, whatever you need. Finally, the data Mount Listener is called. In any case, whether successful or unsuccessful, loadingCount
it is updated.
To cite only one example:
1 Private voidLoaddesignernewstopstories (Final intpage) {2Getdesignernewsapi (). Gettopstories (page,NewCallback<storiesresponse>() {3 @Override4 Public voidSuccess (Storiesresponse storiesresponse, Response Response) {5 if(Storiesresponse! =NULL6&&sourceisenabled (sourcemanager.source_designer_news_popular)) {7 setpage (storiesresponse.stories, page);8 Setdatasource (storiesresponse.stories,9 sourcemanager.source_designer_news_popular);Ten ondataloaded (storiesresponse.stories); One } A loadingcount.decrementandget (); - } - the @Override - Public voidfailure (retrofiterror error) { - loadingcount.decrementandget (); - } + }); -}
If not analyzed, this code is quite difficult to understand. We can create a callback function that creates a retrofit callback function that implements the structure of the preceding callback function. The difference is getting the necessary information from the results :
1 PrivateInline Fun <T>callback (Page:int, sourcekey:string,2Crossinline extract: (T), list<plaiditem>)3= retrofitcallback<t> {result, response4 if(sourceisenabled (Sourcekey)) {5Val items =Extract (Result)6 setpage (items, page)7 setdatasource (items, Sourcekey)8 ondataloaded (items)9 }Ten}
This is very similar: check whether the source is enabled, invoke if enabled, setPage
setDataSource
and onDataLoaded
get related items from the results. the implementation of Retrofitcallback can make the previous function simpler, and it can be omitted:
1 PrivateInline Fun <T> retrofitcallback (crossinline code: (T, Response), Unit): callback<t>2= object:callback<t> {3Override fun success (T:t?, Response:response) {4T?. Let {code (it, response)}5 Loadingcount.decrementandget ()6 }7 8 Override fun Failure (Error:retrofiterror) {9 Loadingcount.decrementandget ()Ten } One}
inline
A modifier is a method of optimizing a function that has parameters that contain other functions. When a function contains a lambda expression, its transformation is equivalent to the object containing the implementation code of the function. However, if used inline
, thelambda expression will be replaced by its code when it is called. If a lambda expression returns a value, it will be used crossinline
. For more information, please read Kotlin reference .
Two functions are generic, so you can accept any type of requirement. In this way, the preceding request can be as follows:
1 Private Fun loaddesignernewstopstories (page:int) {2 designernewsapi.gettopstories (page, 3 Callback (page, sourcemanager.source_designer_news_popular) {it.stories}) 4 }
The function call getTopStories
creates a callback function that receives the page and source key, and the function obtains the stories from the result . A similar structure runs for other parts of the call, but can do whatever results are required. For example, another response needs to modify the contained User:
1 PrivateFun Loaddribbbleusershots (page:int) =dribbblelogged {2Val user =Dribbbleprefs.user3 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 it6it.apply {forEach {it.user =User}}7 })8}
As you can see, the former is also required to be recorded in Dribbble. the dribbblelogged function is responsible for checking, if not just doing other things.
1 private Inline Fun dribbblelogged (code: (), Unit) {2 if ( Dribbbleprefs.isloggedin) {3 code ()4 else { 5 loadingcount.decrementandget ()6 }7 }
Summary
This section shows the lambda expression capabilities and the use of the "first Class citizen" function. The reduction in code is huge ( reducing 238%), but it is not the most important improvement. The code is now easier to read and less error-prone. Remember to read my final submission in the plaid branch of GitHub .
Rewrite the plaid APP in Kotlin language: Lessons Learned (II)