原文標題:API request in Android the easy way using Kotlin
原文鏈接:http://antonioleiva.com/api-request-kotlin/
原文作者:Antonio Leiva(http://antonioleiva.com/about/)
原文發布:2015-07-21
Kotlin是功能非常強大的編程語言,其目標是利用較少的模板(boilerplate)編寫更多的代碼。尤其是在Android開發中。除了編程語言自身和它的類之外,Kotlin還為已有的Java類提供一組好用的擴展。這個例子是請求API和下載結果的方法。
我知道已經有許多不同的庫可以幫助我們做這些工作,並且因Kotlin與Java的互用性,它也能使用這些庫。但是,我們有時候僅僅因為大的庫更簡單、不易出錯,對於小需求也用大庫。
API請求:Java 與 Kotlin對比
我總喜歡把這兩種語言進行對比,看看堅持使用Java我們會錯過什么。從URL恢復JSON的典型代碼是這樣:
1 try { 2 URL url = new URL("<api call>"); 3 4 urlConnection = (HttpURLConnection) url.openConnection(); 5 urlConnection.setRequestMethod("GET"); 6 urlConnection.connect(); 7 8 InputStream inputStream = urlConnection.getInputStream(); 9 StringBuffer buffer = new StringBuffer(); 10 if (inputStream == null) { 11 // Nothing to do. 12 return null; 13 } 14 reader = new BufferedReader(new InputStreamReader(inputStream)); 15 16 String line; 17 while ((line = reader.readLine()) != null) { 18 buffer.append(line + "\n"); 19 } 20 21 if (buffer.length() == 0) { 22 return null; 23 } 24 result = buffer.toString(); 25 } catch (IOException e) { 26 Log.e("Request", "Error ", e); 27 return null; 28 } finally{ 29 if (urlConnection != null) { 30 urlConnection.disconnect(); 31 } 32 if (reader != null) { 33 try { 34 reader.close(); 35 } catch (final IOException e) { 36 Log.e("Request", "Error closing stream", e); 37 } 38 } 39 }
Kotlin標准庫為URL類提供了擴展函數,避免我們編寫所有代碼。前面的代碼可以轉換為:
1 val result = URL("<api call>").readText()
對於大量的響應,不建議使用這個函數,但是在大多數情況下,它是足夠了。如果不這樣,還有許多其他有趣的擴展函數,如:BufferedReader.forEachLine()
,它產生行Sequence
,讓我們用它們中任何一個一起做些事。或是,你可以通過BufferedReader.lineSequence()
得到原始的Sequence<String>
。這時,你能夠執行Sequence
允許的不同轉換中的任何一種,如:過濾、排序、映射等等。
異步調用
如你所知,主線程是負責UI呈現和交互的,我們不應該因其它運行時間長的任務阻塞它,這將會影響UI性能。在HTTP請求情況下, Android SDK甚至通過拋出一個異常來阻止我們這么做。在Android典型的解決方案是使用AsyncTask
。AsyncTask
有一個doInBackground
抽象方法,其在另個線程中執行。
除了讓AsyncTask
正常工作很難這一事實外,由於它自身帶來了許多問題,使得通過它擴展創建一個新類、在onDestroy
中終止它等等,都是很乏味。這個(你可能需要更多的檢查以避免崩潰)非常簡單的版本將是:
1 @Override protected void onCreate(Bundle savedInstanceState) { 2 super.onCreate(savedInstanceState); 3 4 task = new AsyncTask<Void, Void, String>() { 5 @Override protected String doInBackground(Void... params) { 6 return requestFromServer("<api call>"); 7 } 8 9 @Override protected void onPostExecute(String s) { 10 if (!isFinishing() && !isCancelled()) { 11 Log.d("Request", s); 12 Toast.makeText(ExampleActivity.this, "Request performed", Toast.LENGTH_LONG).show(); 13 } 14 } 15 }; 16 } 17 18 @Override protected void onDestroy() { 19 super.onDestroy(); 20 21 if (task != null) { 22 task.cancel(true); 23 task = null; 24 } 25 }
這實在不清晰也不直觀。當我們在Android中用Kotlin開發時,我們不能忘記Anko庫。它主要目的是提供DSL方式用代碼來創建布局,而不是用XML。我實際使用過XML,所以我現在不使用它了,但是它還是包括一整套非常有用的特性。特別對異步任務有些小的DSL。這樣在Kotlin中,前面的代碼能夠減少為:
1 async { 2 val result = URL("<api call>").readText() 3 uiThread { 4 Log.d("Request", result) 5 longToast("Request performed") 6 } 7 }
實際上,你有async
函數,它將在另一個線程中執行代碼,並由uiThread
給出返回主線的機會。async
是Context
的擴展函數實現,且使用它弱應用,所以不會阻止GC釋放內存。
uiThread
優勢的方面是它依據使用類,以用不同的方式來實現。如果我們從Activity中調用它,假設actiivity.isFinishing()
返回true
,uiThread
代碼是不會執行的,並且在此情況下不會崩潰。
假設你要用future,Async
返回Java Future
。如果你需要返回future結果,就可以用asyncResult
。
你還能夠用你自己的執行器:
1 val executor = Executors.newScheduledThreadPool(4) 2 async(executor) { 3 // Some task 4 }
總結
用幾行代碼,我們從一個非常典型的操作得到相同(如果沒有更好的話)的結果,如:調用一個API,以String變量得到結果。隱藏在這些擴展函數背后有許多有趣的代碼,所以我建議去重讀Kotlin和Anko源代碼,看看在幕后都做了什么。
記住從我寫的書《Android開發者的Kotlin》中,你能夠學習到Kotlin的這點以及許多其它能力,你將通過從0開始創建Android APP學習Kotlin。