1. 程式人生 > >非同步任務AsyncTask及JSON解析

非同步任務AsyncTask及JSON解析

一、AsyncTask:

(一)、相關知識回顧:

1、開發Android應用時必須遵守單執行緒模型的原則: 

        Android UI操作並不是執行緒安全的,並且這些操作必須在UI執行緒中執行。

 

2、單執行緒模型中始終要記住兩條法則: 

1). 不要阻塞UI執行緒 ;
2). 確保只在UI執行緒中訪問Android UI控制元件。

        當一個程式第一次啟動時,Android會同時啟動一個對應的主執行緒

(Main Thread),主執行緒主要負責處理與UI相關的事件,如:使用者的按鍵事件,使用者接觸螢幕的事件以及螢幕繪圖事件,並把相關的事件分發到對應的元件進行處理。所以主執行緒通常又被叫做UI執行緒

 

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、製作思路:

  1. 在非同步任務內部類的構造方法中new ProgressDialog();
  2. 在onPreExecute()中呼叫ProgressDialog物件的show()方法,顯示進度對話方塊;
  3. 在doInBackground()方法中計算進度,在while迴圈中通過呼叫publishProgress()方法將進度資料隨時釋出出去;
  4. 進度資料的計算公式:publishProgress((int) ((count / (double) length) * 100)),其中count為當前載入的檔案長度,length為檔案的總長度;
  5. 在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迴圈遍歷。