1. 程式人生 > >android 非同步方式實現資料載入

android 非同步方式實現資料載入

使用AsyncTask實現非同步處理

由於主執行緒(也可叫UI執行緒)負責處理使用者輸入事件(點選按鈕、觸控式螢幕幕、按鍵等),如果主執行緒被阻塞,應用就會報ANR錯誤。為了不阻塞主執行緒,我們需要在子執行緒中處理耗時的操作,在處理耗時操作的過程中,子執行緒可能需要更新UI控制元件的顯示,由於UI控制元件的更新重繪是由主執行緒負責的,所以子執行緒需要通過Handler傳送訊息給主執行緒的訊息佇列,由執行在主執行緒的訊息處理程式碼接收訊息後更新UI控制元件的顯示。

採用執行緒+Handler實現非同步處理時,當每次執行耗時操作都建立一條新執行緒進行處理,效能開銷會比較大。另外,如果耗時操作執行的時間比較長,就有可能同時執行著許多執行緒,系統將不堪重負。為了提高效能,我們可以使用AsynTask實現非同步處理,事實上其內部也是採用執行緒+Handler來實現非同步處理的,只不過是其內部使用了執行緒池技術,有效的降低了執行緒建立數量及限定了同時執行的執行緒數。

  private final class AsyncImageTask extends AsyncTask<String, Integer, String>{

        protectedvoid onPreExecute(){ //執行在UI執行緒

        }

        protectedStringdoInBackground(String...params) {//在子執行緒中執行

            return“itcast”;

        }

        protectedvoid onPostExecute(String result) {//

執行在UI執行緒

        }  

        protectedvoid onProgressUpdate(Integer… values) {//執行在UI執行緒

        }  

   }

AsyncTask<String, Integer, String>中定義的三個泛型引數分別用作了doInBackground、onProgressUpdate的輸入方法型別,第三個引數用作了doInBackground的返回引數型別和onPostExecute的輸入引數型別。

AsyncTask定義了三種泛型型別Params,Progress和Result。

  • Params 
    啟動任務執行的輸入引數,比如HTTP請求的URL。
  • Progress 後臺任務執行的百分比。
  • Result 後臺執行任務最終返回的結果,比如String。

使用AsyncTask非同步載入資料最少要重寫以下這兩個方法:

  • doInBackground(Params…) 後臺執行,比較耗時的操作都可以放在這裡。注意這裡不能直接操作UI。此方法在後臺執行緒執行,完成任務的主要工作,通常需要較長的時間。在執行過程中可以呼叫publicProgress(Progress…)來更新任務的進度。
  • onPostExecute(Result)  相當於Handler 處理UI的方式,在這裡面可以使用在doInBackground 得到的結果處理操作UI。 此方法在主執行緒執行,任務執行的結果作為此方法的引數返回

有必要的話還得重寫以下這三個方法,但不是必須的:

  • onProgressUpdate(Progress…)   可以使用進度條增加使用者體驗度。 此方法在主執行緒執行,用於顯示任務執行的進度。
  • onPreExecute() 這裡是終端使用者呼叫Excute時的介面,當任務執行之前開始呼叫此方法,可以在這裡顯示進度對話方塊。
  • onCancelled()使用者呼叫取消時,要做的操作

使用AsyncTask類,以下是幾條必須遵守的準則:

  • Task的例項必須在UI thread中建立;
  • execute方法必須在UI thread中呼叫;
  • 不要手動的呼叫onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法;
  • 該task只能被執行一次,否則多次呼叫時將會出現異常;
下面是看程式碼中是如何使用的

1、Contact.java建立資料物件類

package cn.org.domain;

public class Contact {
	public int id;
	public String name;
	public String image;
	public Contact(int id, String name, String image) {
		this.id = id;
		this.name = name;
		this.image = image;
	}
	public Contact(){}
}

2.這裡我們首先需要準備好資料,是通過解析web service中的xml資料,資料是通過MD5進行加密的ContactService.java
package cn.itcast.service;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.xmlpull.v1.XmlPullParser;

import android.net.Uri;
import android.util.Xml;

import cn.org.domain.Contact;
import cn.org.utils.MD5;

public class ContactService {

	/**
	 * 獲取聯絡人
	 * @return
	 */
	public static List<Contact> getContacts() throws Exception{
		String path = "http://192.168.1.100:8080/web/list.xml";
		HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
		conn.setConnectTimeout(5000);
		conn.setRequestMethod("GET");
		if(conn.getResponseCode() == 200){
			return parseXML(conn.getInputStream());
		}
		return null;
	}

	private static List<Contact> parseXML(InputStream xml) throws Exception{
		List<Contact> contacts = new ArrayList<Contact>();
		Contact contact = null;
		XmlPullParser pullParser = Xml.newPullParser();
		pullParser.setInput(xml, "UTF-8");
		int event = pullParser.getEventType();
		while(event != XmlPullParser.END_DOCUMENT){
			switch (event) {
			case XmlPullParser.START_TAG:
				if("contact".equals(pullParser.getName())){
					contact = new Contact();
					contact.id = new Integer(pullParser.getAttributeValue(0));
				}else if("name".equals(pullParser.getName())){
					contact.name = pullParser.nextText();
				}else if("image".equals(pullParser.getName())){
					contact.image = pullParser.getAttributeValue(0);
				}
				break;

			case XmlPullParser.END_TAG:
				if("contact".equals(pullParser.getName())){
					contacts.add(contact);
					contact = null;
				}
				break;
			}
			event = pullParser.next();
		}
		return contacts;
	}
	/**
	 * 獲取網路圖片,如果圖片存在於快取中,就返回該圖片,否則從網路中載入該圖片並快取起來
	 * @param path 圖片路徑
	 * @return
	 */
	public static Uri getImage(String path, File cacheDir) throws Exception{// path -> MD5 ->32字串.jpg
		File localFile = new File(cacheDir, MD5.getMD5(path)+ path.substring(path.lastIndexOf(".")));
		if(localFile.exists()){
			return Uri.fromFile(localFile);
		}else{
			HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
			conn.setConnectTimeout(5000);
			conn.setRequestMethod("GET");
			if(conn.getResponseCode() == 200){
				FileOutputStream outStream = new FileOutputStream(localFile);
				InputStream inputStream = conn.getInputStream();
				byte[] buffer = new byte[1024];
				int len = 0;
				while( (len = inputStream.read(buffer)) != -1){
					outStream.write(buffer, 0, len);
				}
				inputStream.close();
				outStream.close();
				return Uri.fromFile(localFile);
			}
		}
		return null;
	}

}

package cn.org.utils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5 {

	public static String getMD5(String content) {
		try {
			MessageDigest digest = MessageDigest.getInstance("MD5");
			digest.update(content.getBytes());
			return getHashString(digest);
			
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}
	
    private static String getHashString(MessageDigest digest) {
        StringBuilder builder = new StringBuilder();
        for (byte b : digest.digest()) {
            builder.append(Integer.toHexString((b >> 4) & 0xf));
            builder.append(Integer.toHexString(b & 0xf));
        }
        return builder.toString();
    }
}

3.ContactAdapter.java 我們來適配繫結資料,這裡我們通過非同步方式實現
package cn.org.adapter;

import java.io.File;
import java.util.List;

import cn.org.asyncload.R;
import cn.org.domain.Contact;
import cn.org.service.ContactService;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ContactAdapter extends BaseAdapter {
	private List<Contact> data;
	private int listviewItem;
	private File cache;
	LayoutInflater layoutInflater;
	
	public ContactAdapter(Context context, List<Contact> data, int listviewItem, File cache) {
		this.data = data;
		this.listviewItem = listviewItem;
		this.cache = cache;
		layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}
	/**
	 * 得到資料的總數
	 */
	public int getCount() {
		return data.size();
	}
	/**
	 * 根據資料索引得到集合所對應的資料
	 */
	public Object getItem(int position) {
		return data.get(position);
	}
	
	public long getItemId(int position) {
		return position;
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		ImageView imageView = null;
		TextView textView = null;
		
		if(convertView == null){
			convertView = layoutInflater.inflate(listviewItem, null);
			imageView = (ImageView) convertView.findViewById(R.id.imageView);
			textView = (TextView) convertView.findViewById(R.id.textView);
			convertView.setTag(new DataWrapper(imageView, textView));
		}else{
			DataWrapper dataWrapper = (DataWrapper) convertView.getTag();
			imageView = dataWrapper.imageView;
			textView = dataWrapper.textView;	
		}
		Contact contact = data.get(position);
		textView.setText(contact.name);
		asyncImageLoad(imageView, contact.image);
		return convertView;
	}
    private void asyncImageLoad(ImageView imageView, String path) {
    	AsyncImageTask asyncImageTask = new AsyncImageTask(imageView);
    	asyncImageTask.execute(path);
		
	}
    
    private final class AsyncImageTask extends AsyncTask<String, Integer, Uri>{
    	private ImageView imageView;
		public AsyncImageTask(ImageView imageView) {
			this.imageView = imageView;
		}
		protected Uri doInBackground(String... params) {//子執行緒中執行的
			try {
				return ContactService.getImage(params[0], cache);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}
		protected void onPostExecute(Uri result) {//執行在主執行緒
			if(result!=null && imageView!= null)
				imageView.setImageURI(result);
		}	
    }

	private final class DataWrapper{
		public ImageView imageView;
		public TextView textView;
		public DataWrapper(ImageView imageView, TextView textView) {
			this.imageView = imageView;
			this.textView = textView;
		}
	}
}
4.MainActivity.java
package cn.org.asyncload;

import java.io.File;
import java.util.List;

import cn.org.adapter.ContactAdapter;
import cn.org.domain.Contact;
import cn.org.service.ContactService;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.widget.ListView;

public class MainActivity extends Activity {
	ListView listView;
	File cache;
	
	Handler handler = new Handler(){
		public void handleMessage(Message msg) {
			 listView.setAdapter(new ContactAdapter(MainActivity.this, (List<Contact>)msg.obj, 
					 R.layout.listview_item, cache));
		}		
	};
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        listView = (ListView) this.findViewById(R.id.listView);
        
        cache = new File(Environment.getExternalStorageDirectory(), "cache");
        if(!cache.exists()) cache.mkdirs();
        
        new Thread(new Runnable() {			
			public void run() {
				try {
					List<Contact> data = ContactService.getContacts();
					handler.sendMessage(handler.obtainMessage(22, data));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();       
    }

	@Override
	protected void onDestroy() {
		for(File file : cache.listFiles()){
			file.delete();
		}
		cache.delete();
		super.onDestroy();
	}
    
}


5.listview_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ImageView 
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:id="@+id/imageView"
        />

    <TextView 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textColor="#FFFFFF"
        android:id="@+id/textView"        
        />
</LinearLayout>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


    <ListView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/listView"/>


</LinearLayout>