非同步任務AsyncTask及JSON解析
一、AsyncTask:
(一)、相關知識回顧:
1、開發Android應用時必須遵守單執行緒模型的原則:
Android UI操作並不是執行緒安全的,並且這些操作必須在UI執行緒中執行。
2、單執行緒模型中始終要記住兩條法則:
1). 不要阻塞UI執行緒 ;
2). 確保只在UI執行緒中訪問Android UI控制元件。
當一個程式第一次啟動時,Android會同時啟動一個對應的主執行緒
3、Android4.0以上版本中,主執行緒中不允許訪問網路。涉及到網路操作的程式一般都是需要開一個新執行緒完成網路訪問。但是在獲得頁面資料後,又不能將資料返回到UI介面中 。因為子執行緒(Worker Thread)不能直接訪問UI執行緒中的成員,也就是說沒有辦法對UI介面上的內容進行操作,如果操作,將丟擲異常:CalledFromWrongThreadException
其實,android提供了幾種在其他執行緒中訪問UI執行緒的方法:
- Activity.runOnUiThread( Runnable )
- View.post( Runnable )
- View.postDelayed( Runnable, long )
- Handler訊息傳遞機制(後續中講解)
這些類或方法會使程式碼很複雜很難理解。為了解決這個問題,Android 1.5提供了一個工具類:AsyncTask,它使建立與使用者介面長時間互動執行的任務變得更簡單。AsyncTask更輕量級一些,適用於簡單的非同步處理,不需要藉助執行緒和Handler即可實現。
(二)、AsyncTask的程式碼實現:
1、AsyncTask是抽象類.AsyncTask定義了三種泛型型別 Params,Progress和Result。
- Params 啟動任務執行的輸入引數,比如HTTP請求的URL。 一般用String型別;
- Progress 後臺任務執行的百分比。 一般用Integer型別;
- Result 後臺執行任務最終返回的結果,一般用byte[]或者String。
2、AsyncTask的執行分為四個步驟,每一步都對應一個回撥方法(由應用程式自動呼叫的方法),開發者需要做的就是實現這些方法。
1) 定義AsyncTask的子類;
2) 實現AsyncTask中定義的方法:(可以全部實現,也可以只實現其中一部分)
- onPreExecute(), 該方法將在執行實際的後臺操作前被UI thread呼叫。可以在該方法中做一些準備工作,如在介面上顯示一個進度條。
- doInBackground(Params...), 將在onPreExecute 方法執行後馬上執行,該方法執行在後臺執行緒中。這裡將主要負責執行那些很耗時的後臺計算工作。可以呼叫 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
- onProgressUpdate(Progress...),在publishProgress方法被呼叫後,UI thread將呼叫這個方法從而在介面上展示任務的進展情況,例如通過一個進度條進行展示。
- onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread呼叫,後臺的計算結果將通過該方法傳遞到UI thread.
3、核心程式碼:
publicclass MainActivity extends Activity {
privatefinalstatic String TAG = "MainActivity";
private String urlString = "http://www.baidu.com/";
private TextView text_main_info;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text_main_info = (TextView) findViewById(R.id.text_main_info);
// 呼叫非同步任務,執行網路訪問
new MyTask(this).execute(urlString);
}
class MyTask extends AsyncTask<String, Void, byte[]> {
private ProgressDialog pDialog;
private Context context = null;
// 構造方法,初始化進度對話方塊
public MyTask(Context context) {
this.context = context;
pDialog = new ProgressDialog(context);
pDialog.setIcon(R.drawable.ic_launcher);
pDialog.setTitle("提示:");
pDialog.setMessage("資料載入中。。。");
}
// 事先執行方法中顯示進度對話方塊
@Override
protectedvoid onPreExecute() {
pDialog.show();
super.onPreExecute();
}
// 進度條進度改變方法。一般情況下,可以不寫該方法
@Override
protectedvoid onProgressUpdate(Void... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
}
// 後臺執行方法,這個方法執行worker Thread非同步訪問網路,載入資料。該方法中不可以執行任何UI操作。
@Override
protectedbyte[] doInBackground(String... params) {
BufferedInputStream bis = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
URL urlObj = new URL(params[0]);
HttpURLConnection httpConn = (HttpURLConnection) urlObj
.openConnection();
httpConn.setDoInput(true);
// httpConn.setDoOutput(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
if (httpConn.getResponseCode() == 200) {
bis = new BufferedInputStream(httpConn.getInputStream());
byte[] buffer = newbyte[1024 * 8];
int c = 0;
while ((c = bis.read(buffer)) != -1) {
baos.write(buffer, 0, c);
baos.flush();
}
// Toast.makeText(context, baos.toByteArray().toString(),
// Toast.LENGTH_LONG).show();
return baos.toByteArray();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bis != null) {
bis.close();
}
if (baos != null) {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
returnnull;
}
// 事後方法,這個方法主要作用是執行對主執行緒中UI的操作。可以實現主執行緒和子執行緒之間的資料互動
@Override
protectedvoid onPostExecute(byte[] result) {
super.onPostExecute(result);
if (result == null) {
text_main_info.setText("網路異常,載入資料失敗!");
} else {
text_main_info.setText(new String(result));
}
pDialog.dismiss();
}
}
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}
}
(三)、增加進度條的非同步任務:
1、製作思路:
- 在非同步任務內部類的構造方法中new ProgressDialog();
- 在onPreExecute()中呼叫ProgressDialog物件的show()方法,顯示進度對話方塊;
- 在doInBackground()方法中計算進度,在while迴圈中通過呼叫publishProgress()方法將進度資料隨時釋出出去;
- 進度資料的計算公式:publishProgress((int) ((count / (double) length) * 100)),其中count為當前載入的檔案長度,length為檔案的總長度;
- 在onPostExecute()方法中呼叫 ProgressDialog物件的dismiss()方法,讓進度條消失。
2、核心程式碼:
A、如果計算進度?【在doInBackground()方法中增加如下程式碼】
// 獲取內容的長度
int length = httpConn.getContentLength();
while ((c = bis.read(buffer)) != -1) {
baos.write(buffer, 0, c);
baos.flush();
count += c;
if (length > 0) {
// 如果知道響應的長度,呼叫publishProgress()更新進度
publishProgress((int) ((count / (double) length) * 100));
}
}
B、如果更新進度?【在onProgressUpdate()方法中增加如下程式碼】
pDialog.setProgress(values[0]);
(四)、為了正確的使用AsyncTask類,以下是幾條必須遵守的準則:
1) Task的例項必須在UI thread中建立;
2) execute方法必須在UI thread中呼叫;
3) 不要手動的呼叫onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法 ;
4) 該task只能被執行一次,否則多次呼叫時將會出現異常 ;
doInBackground方法和onPostExecute的引數必須對應,這兩個引數在AsyncTask宣告的泛型引數列表中指定,第一個為doInBackground接受的引數,第二個為顯示進度的引數,第三個為doInBackground返回和onPostExecute傳入的引數。
二、JSON:
(一)、概念:JSON(javascript object notation) ,是一種輕量級的資料儲存和交換格式。它是完全獨立於語言的文字格式。JSON易於閱讀、編寫,也易於機器解析和生成。
(二)、JSON基本格式:
1、鍵值對物件格式:用“{}”包圍
2、陣列格式:用“[]”包圍.
(三)、JSON PK XML:
1、json和xml在可讀性、可擴充套件性上都不相上下;
2、解碼難度上看,json更方便和簡潔;
3、json對資料描述性上比xml差;
4、應用json實現功能的速度要遠快於xml。
(四)、JSON解析原則:
1、看到{},建立JsonObject物件;
2、看到[],建立JsonArray物件;
3、看到JsonArray,要for迴圈遍歷。