手機上的資源畢竟有限,為了獲取更豐富的信息,就得到遼闊的互聯網大海上衝浪。對於App自身,也要經常與伺服器交互,以便獲取最新的數據顯示到界面上。這個客戶端與服務端之間的信息交互,基本使用HTTP協議進行通信,即App訪問伺服器的HTTP介面來傳輸數據。HTTP介面調用在Java代碼中可不是一個輕鬆的 ...
手機上的資源畢竟有限,為了獲取更豐富的信息,就得到遼闊的互聯網大海上衝浪。對於App自身,也要經常與伺服器交互,以便獲取最新的數據顯示到界面上。這個客戶端與服務端之間的信息交互,基本使用HTTP協議進行通信,即App訪問伺服器的HTTP介面來傳輸數據。HTTP介面調用在Java代碼中可不是一個輕鬆的活,開發者若用最基礎的HttpURLConnection來編碼的話,至少要考慮以下場景的處理:
1、HTTP的請求方式是什麼,是GET還是POST還是PUT還是DELETE?
2、HTTP的連接超時時間是多少,請求應答的超時時間又是多少?
3、HTTP頭部的語言和瀏覽器信息該設置為什麼?
4、HTTP傳輸的數據內容採取的是哪種編碼方式?
5、HTTP的應答數據如果是壓縮過的,又要如何解壓?
6、HTTP的輸入輸出流需要註意哪些方面?
7、HTTP如何分塊傳輸較大的數據信息?
瞧瞧上面層出不窮的功能要求,如果開發者事必躬親逐個編碼,那可真是要累得夠嗆。因此,各種意圖取代HttpURLConnection的網路交互框架如雨後春筍般涌現出來,既有老資格的如HttpClient,又有後起之秀如Android-Async-Http、Volley、OkHttp、Retrofit等等,可謂是百花齊放、百家爭鳴。當然,這些網路框架是需要學習成本的,使用起來也不如想象中的那麼容易;它們只是在技術上各有千秋,並非終極的解決方案,往往是你方唱罷我登臺,各領風騷幾年然後歇菜。
其實HTTP交互原本無需這樣大動干戈,常見的介面調用僅僅是App往伺服器發送一串請求信息,然後伺服器返回給App一串處理結果,這種簡單的業務場景已經足夠應付大多數App的網路通信需求。所以大道至簡,Kotlin把網路交互看作是跟文件讀寫一樣的I/O操作,後端地址就像是個文件路徑,那麼請求伺服器的數據猶如讀取文件內容。文本分為文本文件和二進位文件兩種,則HTTP介面對應獲取文本數據和獲取二進位數據兩種,於是整個網路請求便簡化為數據的存跟取了。
具體到詳細的Kotlin編碼,文件對象由“File(文件路徑)”構建,而HTTP對象由“URL(網路地址)”構建,獲取介面數據則有readText和readBytes兩個方法,前者用於獲取文本形式的應答數據,後者用於二進位形式的應答數據如圖片文件、音頻文件等等。僅僅一個readText方法真的能完成繁雜的HTTP介面調用操作嗎?下麵我們通過一個具體的介面訪問案例,探討一下如何使用Kotlin代碼實現HTTP介面調用。
智能手機普遍提供了定位功能,可是系統自帶的定位服務只能獲得用戶所在的經緯度信息,而這枯燥的經緯度數字令人不知所云,肯定要把經緯度轉換為詳細的地址信息才方便用戶理解。將經緯度轉換為詳細地址,就要訪問谷歌地圖提供的地址查詢介面了,該介面的地址形如“http://maps.google.cn/maps/api/geocode/json?請求參數信息”,App把經緯度數據作文請求參數傳入,對方會返回一個包含地址信息的json串,通過解析json串即可獲得當前的詳細地址。由於訪問網路需要在分線程進行,因此介面訪問代碼必須放在doAsync代碼塊中,下麵給出根據經緯度獲取詳細地址的Kotlin代碼片段:
private val mapsUrl = "http://maps.google.cn/maps/api/geocode/json?latlng={0},{1}&sensor=true&language=zh-CN" //位置監聽器偵聽到定位變化事件,就調用該函數請求詳細地址 private fun setLocationText(location: Location?) { if (location != null) { doAsync { //根據經緯度數據從谷歌地圖獲取詳細地址信息 val url = MessageFormat.format(mapsUrl, location.latitude, location.longitude) val text = URL(url).readText() val obj = JSONObject(text) val resultArray = obj.getJSONArray("results") var address = "" //解析json字元串,其中formatted_address欄位為具體地址名稱 if (resultArray.length() > 0) { val resultObj = resultArray.getJSONObject(0) address = resultObj.getString("formatted_address") } //獲得該地點的詳細地址之後,回到主線程把地址顯示在界面上 uiThread { findAddress(location, address) } } } else { tv_location.text = "$mLocation\n暫未獲取到定位對象" } } //在主線程中把定位信息連同地址信息都列印到界面上 private fun findAddress(location: Location, address: String) { tv_location.text = "$mLocation\n定位對象信息如下: " + "\n\t時間:${DateUtil.nowDateTime}" + "\n\t經度:${location.longitude},緯度:${location.latitude}" + "\n\t高度:${location.altitude}米,精度:${location.accuracy}米" + "\n\t地址:$address" }
上述代碼看起來顯然簡明扼要,寥寥數行便搞定了完整的功能實現。如果使用Java代碼實現該功能,首先HTTP調用就得提供底層的介面訪問代碼,其次分線程請求網路又得專門寫個繼承自AsyncTask的任務處理代碼,末了Activity這邊廂還得實現該任務的完成事件,真是興師動眾、勞民傷財。由此可見Kotlin的網路交互是革命性的,方式雖然簡單,卻足以應付大部分的網路通信需求,並且運行效果與Java代碼幾無差別,例如調用地圖介面查詢地址信息,無論採用Java編碼還是Kotlin編碼,界面效果都如下圖所示。
上面利用readText方法就完成了文本數據的介面調用,當時提到了readBytes可用於獲取二進位數據如圖片文件,那麼獲取網路圖片是否也同樣方便呢?下麵我們繼續探討如何使用Kotlin代碼讀取網路圖片。
獲取網路圖片的基本流程同文本格式的介面訪問,一樣先通過URL類構建HTTP對象,然後在doAsync代碼塊中調用HTTP對象的readBytes方法獲得圖片的位元組數組。將位元組數組轉換為點陣圖對象,這在前面的文章《Kotlin入門(27)文件讀寫操作》已經加以介紹,即利用BitmapFactory工具的decodeByteArray方法實現轉換操作。轉換好的點陣圖當然可以在主線程直接顯示出來,也可以先保存為圖片文件,等到需要的時候再去讀取。前面描述如何把點陣圖保存為圖片文件時,由於Bitmap相關類並未提供簡單的圖片保存方法,因此當時保存點陣圖文件還著實頗費了一番功夫。現在保存網路圖片反而無需如此折騰,這是因為獲取網路圖片得到了位元組數組,位元組數組保存為文件可是相當方便的噢,只要調用File對象的writeBytes方法,短短一行就保存好圖片了。介紹完了網路圖片的存取流程,最終的Kotlin編碼一如既往地簡單明瞭,下麵展示了一個驗證碼動態顯示的頁面代碼:
class HttpImageActivity : AppCompatActivity() { private val imageUrl = "http://222.77.181.14/ValidateCode.aspx?r=" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_http_image) iv_image_code.setOnClickListener { getImageCode() } getImageCode() } //獲取網路上的圖片驗證碼 private fun getImageCode() { iv_image_code.isEnabled = false doAsync { val url = "$imageUrl${DateUtil.getFormatTime()}" val bytes = URL(url).readBytes() //把位元組數組解碼為點陣圖數據 val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) //也可通過下麵三行代碼把位元組數組寫入文件,即生成一個圖片文件 val path = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/" val file_path = "$path${DateUtil.getFormatTime()}.png" File(file_path).writeBytes(bytes) //獲得驗證碼圖片數據,回到主線程把驗證碼顯示在界面上 uiThread { finishGet(bitmap) } } } //在主線程中顯示獲得到的驗證碼圖片 private fun finishGet(bitmap: Bitmap) { iv_image_code.setImageBitmap(bitmap) iv_image_code.isEnabled = true } }
看到了吧,即使是完整的Activity代碼,Kotlin也只需數十行而已。倘若使用Java完成同樣的功能,除了HTTP底層與AsyncTask的編碼之外,還得補充Bitmap對象的圖片保存代碼。也就是說,Java代碼需要額外添加三個工具類的實現代碼,光光這一點,Kotlin的效率就令人贊嘆。而且,短小精悍的Kotlin代碼並未造成任何功能缺失,以上面的圖片驗證碼頁面為例,使用Java編碼和使用Kotlin編碼,最終的顯示效果都如下圖所示。