前言
今天我們探討的內容是andorid中的異步,同時就異步問題對UI進行聯合探討。
本章內容希望讀者熟悉java基礎,對線程有熟悉。
我們知道,java中的線程是指異步,也就是在主程序進行的同時,副程序也在進行(比喻)。Java中,實現異步一般使用2種方法,一種是實現接口Runable,一種是繼承類Thread。不過這不是我們今天要討論的內容,上面的所的東西只是你應該知道並且瞭解的東西。今天我們講述的重點是android的異步。
Android中,異步實現,一般用handler和AsyncTask,通常handler是一個消息棧,而AsyncTask不是。
我們今天將的是AsyncTask作爲異步的教學。你會問了,爲啥不用handler?具體的我們可以看如下解釋:
“很多網友可能發現Android平臺很多應用使用的都是AsyncTask,而並非Thread和Handler去更新UI,這裏給大家說下他們到底有什麼區別,我們平時應該使用哪種解決方案。從Android 1.5開始系統將AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時其實官方將其命名爲UserTask,其內部是JDK 1.5開始新增的concurrent庫,做過J2EE的網友可能明白併發庫效率和強大性,比Java原始的Thread更靈活和強大,但對於輕量級的使用更爲佔用系統資源。Thread是Java早期爲實現多線程而設計的,比較簡單不支持concurrent中很多特性在同步和線程池類中需要自己去實現很多的東西,對於分佈式應用來說更需要自己寫調度代碼,而爲了Android UI的刷新Google引入了Handler和Looper機制,它們均基於消息實現,有事可能消息隊列阻塞或其他原因無法準確的使用。
Android開發網推薦大家使用AsyncTask代替Thread+Handler的方式,不僅調用上更爲簡單,經過實測更可靠一些,Google在Browser中大量使用了異步任務作爲處理耗時的I/O操作,比如下載文件、讀寫數據庫等等,它們在本質上都離不開消息,但是AsyncTask相比Thread加Handler更爲可靠,更易於維護,但AsyncTask缺點也是有的比如一旦線程開啓即dobackground方法執行後無法給線程發送消息,僅能通過預先設置好的標記來控制邏輯,當然可以通過線程的掛起等待標誌位的改變來通訊,對於某些應用Thread和Handler以及Looper可能更靈活。”
當然 handler+Thread方式我們還是會講解的,屆時大家也可以看到一個對比。
AsyncTask初識
我在做一個圖片管理器,在圖片展示的時候,出現了很多問題,其中最重要的問題就是用戶體驗:比如九宮圖的時候,我最開始使用的是同步代碼進行圖片加載,結果30多張圖片讓我等了10秒,當然,就算算上讀出圖片進行處理的過程,這個時間我也不能接受。
異步可以在一定程度上減輕這部分的延遲,提高用戶體驗,它的思路是:即時加載圖片,即時更新圖片列表。所以我在九宮圖的時候,可以看到圖片的一張一張刷新——這就是異步操作。
OK。我們知道android的後臺是java寫的,我們可以用Thread跟Runalbe來實現異步,在java中,他們確實是異步,但是我們今天是要異步更換UI界面的東西。做過系統的人可能知道(我也是看別人的博客知道的),任何UI的更新必須在activity主線程中進行,因爲這樣才能保證一定的安全(這一部分我也不是非常瞭解,爲了不誤導各位,請大家看其他資料,我也會持續補充的)。
那麼Android是怎麼解決的呢?用handler+Thread/Runable或者AsyncTask,他們是activity主線程中“僞造”的線程,並不見得分配線程ID給他們,所以能夠更新!我們今天使用AsyncTask進行異步圖片的講解,同時,對於handler的異步,我想我會用一個幻燈片的例子,來給自己和大家一個交代。
AsyncTask的代碼結構
之前我們將過AsyncTask是用來做異步的。我也說過將異步的圖片即時加載到UI上去——怎麼加載?
我們先用文字描述下AsyncTask的入參和內部的方法——我們會用到的或者說比較常用的方法。
1) 入參
我們一般用一個類來定義一個異步。比如下面這種
class AsyncTaskLoad extends AsyncTask<Object, Bitmap, Object>
我們看到了有3個入參,他們分別是
l Params, the type of the parameters sent to the task upon execution.
l Progress, the type of the progress units published during the background computation.
l Result, the type of the result of the background computation.
上面是對此的解釋
l 遞交給異步處理的入參
l 在更新時候或者處理時候的入參
l 返回結果
我來解釋下第二條,我們在後臺處理過後,需要即時通知UI更新,調用的方法的入參。如果不明白,之後的代碼會詳細說明的。
2) 方法
繼承了AsyncTask,默認要實現的方法是doInBackground:
@Override
protected Object doInBackground(Object... params)
這貨就是後臺異步的邏輯處理,比如我們的圖片,在數據庫中抓取、處理就是在這裏寫的。同時當我們是一個循環的時候,我們會在裏面寫一段話調用更新方法,這句話是:
// 在此提醒前臺九宮圖更新,此時原圖應該也刷出來了
publishProgress(bitmap);
恩,我說下,bitmap是一個Bitmap的實例化。我們調用的方法入參是Bitmap型的(跟類聲明聯繫一起,第二個入參),他會調用方法:
@Override
protected void onProgressUpdate(Bitmap... values) {
// TODO Auto-generated method stub
// super.onProgressUpdate(values);
/* 將數據放入adapter中,並且通知頁面UI刷新 */
for (Bitmap bitmap : values) {
// 添加內容到adapter
imageAdapter.addPhoto(bitmap);
// 通知頁面UI刷新數據
imageAdapter.notifyDataSetChanged();
}
}
恩,這就是我們的更新方法。我們看到方法入參是一個Bitmap,這個定義是在類繼承的時候第二個入參定義的。入參爲Bitmap... values,這你可以理解爲
Bitmap[] values,更多資料請查略資料,這個不提了。
內容是循環values,第一個是將位圖添加到適配器adapter中,另一個notifyDataSetChanged就是一個關鍵點,他通知adapter本身數據更新了,你應該更新UI展示。
我們完成異步圖片加載,基本來說就只需要上面2個方法就可以做到。但是AsyncTask還有一些方法,我們需要知曉,他們分別是:
@Override
protected void onPostExecute(Object result)
異步結束操作,我們看到入參是我們類繼承方法定義的Object
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
異步邏輯處理前操作(doInBackground方法執行前)。
onPreExecute,onPostExecute,以及onProgressUpdate都可以對UI進行操作,他們的不同處是在異步執行的先後次序不一樣。onPreExecute是先邏輯執行,onProgressUpdate是邏輯中執行,onPostExecute是異步邏輯完全執行完畢後執行。
代碼和其他說明
基本上也講完了,接下來就是貼代碼了,使用115網盤。
http://u.115.com/file/clqcacyn
代碼實現的功能是:
? 完成九宮圖的異步加載;
? 完成點擊九宮圖,顯示點擊的圖片序列號。
來一張截圖
需要提下的是,我的代碼是通過在sdcard中獲取圖片,如果要運行代碼,請預先配置好資源,否則啓動報錯。關於android的sd卡配置,網上有很多資料的。順便提下,如果要結合eclipse進行,請選用elipse伽利略版以上版本的。要不啓動sdcard會比較麻煩。