Android異步下載圖片並且緩存圖片到本地
https://lishuaishuai.iteye.com/blog/1806667
在Android開發中我們經常有這樣的需求,從服務器上下載xml或者JSON類型的數據,其中包括一些圖片資源,本demo模擬了這個需求,從網絡上加載XML資源,其中包括圖片,我們要做的解析XML裏麵的數據,並且把圖片緩存到本地一個cache目錄裏麵,並且用一個自定義的Adapter去填充到LIstView,demo運行效果見下圖:
通過這個demo,要學會有一下幾點
1.怎麼解析一個XML
2.demo中用到的緩存圖片到本地一個臨時目錄的思想是怎樣的?
3.AsyncTask類的使用,因為要去異步的加載數據,就必須開啟線程,但是在開啟線程的時有時候不能很好的控製線程的數量,線程數量太大的時候手機會很快被卡死 這裏就采用AsynsTask類的去解決這個問題,這個類裏麵封裝了線程池的技術,從而保證不會因開啟過多的線程而消耗太多的資源
4.本demo中的Handler類的使用情況 5.自定義adapter的使用
下麵是demo中的Activity。
- public class MainActivity extends Activity {
- protected static final int SUCCESS_GET_CONTACT = 0;
- private ListView mListView;
- private MyContactAdapter mAdapter;
- private File cache;
- private Handler mHandler = new Handler(){
- public void handleMessage(android.os.Message msg) {
- if(msg.what == SUCCESS_GET_CONTACT){
- List<Contact> contacts = (List<Contact>) msg.obj;
- mAdapter = new MyContactAdapter(getApplicationContext(),contacts,cache);
- mListView.setAdapter(mAdapter);
- }
- };
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mListView = (ListView) findViewById(R.id.listview);
- //創建緩存目錄,係統一運行就得創建緩存目錄的,
- cache = new File(Environment.getExternalStorageDirectory(), "cache");
- if(!cache.exists()){
- cache.mkdirs();
- }
- //獲取數據,主UI線程是不能做耗時操作的,所以啟動子線程來做
- new Thread(){
- public void run() {
- ContactService service = new ContactService();
- List<Contact> contacts = null;
- try {
- contacts = service.getContactAll();
- } catch (Exception e) {
- e.printStackTrace();
- }
- //子線程通過Message對象封裝信息,並且用初始化好的,
- //Handler對象的sendMessage()方法把數據發送到主線程中,從而達到更新UI主線程的目的
- Message msg = new Message();
- msg.what = SUCCESS_GET_CONTACT;
- msg.obj = contacts;
- mHandler.sendMessage(msg);
- };
- }.start();
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- //清空緩存
- File[] files = cache.listFiles();
- for(File file :files){
- file.delete();
- }
- cache.delete();
- }
- }
1.初始化了一個緩存目錄,這個目錄最好是應用開啟就去創建好,為手續緩存圖片做準備,在這裏把數據存放在SDCard上
2.要去服務器加載數據,這個耗時操作最好是去開啟線程加載數據,加載完畢後去異步的更新UI線程,利用Handler機製能很好的解決這個問題,
3.最後退出應用的時候,要刪掉緩存目錄和目錄裏麵的數據,避免給手機製造很多的垃圾文件
下麵就是一個Service類了,
- public class ContactService {
- /*
- * 從服務器上獲取數據
- */
- public List<Contact> getContactAll() throws Exception {
- List<Contact> contacts = null;
- String Parth = "https://192.168.1.103:8080/myweb/list.xml";
- URL url = new URL(Parth);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setConnectTimeout(3000);
- conn.setRequestMethod("GET");
- if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
- InputStream is = conn.getInputStream();
- // 這裏獲取數據直接放在XmlPullParser裏麵解析
- contacts = xmlParser(is);
- return contacts;
- } else {
- return null;
- }
- }
- // 這裏並沒有下載圖片下來,而是把圖片的地址保存下來了
- private List<Contact> xmlParser(InputStream is) throws Exception {
- List<Contact> contacts = null;
- Contact contact = null;
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(is, "UTF-8");
- int eventType = parser.getEventType();
- while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
- switch (eventType) {
- case XmlPullParser.START_TAG:
- if (parser.getName().equals("contacts")) {
- contacts = new ArrayList<Contact>();
- } else if (parser.getName().equals("contact")) {
- contact = new Contact();
- contact.setId(Integer.valueOf(parser.getAttributeValue(0)));
- } else if (parser.getName().equals("name")) {
- contact.setName(parser.nextText());
- } else if (parser.getName().equals("image")) {
- contact.setImage(parser.getAttributeValue(0));
- }
- break;
- case XmlPullParser.END_TAG:
- if (parser.getName().equals("contact")) {
- contacts.add(contact);
- }
- break;
- }
- }
- return contacts;
- }
- /*
- * 從網絡上獲取圖片,如果圖片在本地存在的話就直接拿,如果不存在再去服務器上下載圖片
- * 這裏的path是圖片的地址
- */
- public Uri getImageURI(String path, File cache) throws Exception {
- String name = MD5.getMD5(path) + path.substring(path.lastIndexOf("."));
- File file = new File(cache, name);
- // 如果圖片存在本地緩存目錄,則不去服務器下載
- if (file.exists()) {
- return Uri.fromFile(file);//Uri.fromFile(path)這個方法能得到文件的URI
- } else {
- // 從網絡上獲取圖片
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setConnectTimeout(5000);
- conn.setRequestMethod("GET");
- conn.setDoInput(true);
- if (conn.getResponseCode() == 200) {
- InputStream is = conn.getInputStream();
- FileOutputStream fos = new FileOutputStream(file);
- byte[] buffer = new byte[1024];
- int len = 0;
- while ((len = is.read(buffer)) != -1) {
- fos.write(buffer, 0, len);
- }
- is.close();
- fos.close();
- // 返回一個URI對象
- return Uri.fromFile(file);
- }
- }
- return null;
- }
- }
Serivce類中,注意以下幾點
1.HttpURLConnection conn = (HttpURLConnection) url.openConnection();獲取一個鏈接,從而進行通訊2.怎麼利用XxmlPullPaser類去解析XML,從而把數據封裝成對象
3.getImageURI(String path, File cache) 這個方法具體實現
4.Uri.fromFile(file);這個方法能夠直接返回一個Uri來
下麵是自定義的Adapter類,
- public class MyContactAdapter extends BaseAdapter {
- protected static final int SUCCESS_GET_IMAGE = 0;
- private Context context;
- private List<Contact> contacts;
- private File cache;
- private LayoutInflater mInflater;
- // 自己定義的構造函數
- public MyContactAdapter(Context context, List<Contact> contacts, File cache) {
- this.context = context;
- this.contacts = contacts;
- this.cache = cache;
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
- @Override
- public int getCount() {
- return contacts.size();
- }
- @Override
- public Object getItem(int position) {
- return contacts.get(position);
- }
- @Override
- public long getItemId(int position) {
- return position;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // 1獲取item,再得到控件
- // 2 獲取數據
- // 3綁定數據到item
- View view = null;
- if (convertView != null) {
- view = convertView;
- } else {
- view = mInflater.inflate(R.layout.item, null);
- }
- ImageView iv_header = (ImageView) view.findViewById(R.id.iv_header);
- TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
- Contact contact = contacts.get(position);
- // 異步的加載圖片 (線程池 + Handler ) ---> AsyncTask
- asyncloadImage(iv_header, contact.image);
- tv_name.setText(contact.name);
- return view;
- }
- private void asyncloadImage(ImageView iv_header, String path) {
- ContactService service = new ContactService();
- AsyncImageTask task = new AsyncImageTask(service, iv_header);
- task.execute(path);
- }
- private final class AsyncImageTask extends AsyncTask<String, Integer, Uri> {
- private ContactService service;
- private ImageView iv_header;
- public AsyncImageTask(ContactService service, ImageView iv_header) {
- this.service = service;
- this.iv_header = iv_header;
- }
- // 後台運行的子線程子線程
- @Override
- protected Uri doInBackground(String... params) {
- try {
- return service.getImageURI(params[0], cache);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- // 這個放在在ui線程中執行
- @Override
- protected void onPostExecute(Uri result) {
- super.onPostExecute(result);
- // 完成圖片的綁定
- if (iv_header != null && result != null) {
- iv_header.setImageURI(result);
- }
- }
- }
- /**
- * 采用普通方式異步的加載圖片
- */
- /*private void asyncloadImage(final ImageView iv_header, final String path) {
- final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- if (msg.what == SUCCESS_GET_IMAGE) {
- Uri uri = (Uri) msg.obj;
- if (iv_header != null && uri != null) {
- iv_header.setImageURI(uri);
- }
- }
- }
- };
- // 子線程,開啟子線程去下載或者去緩存目錄找圖片,並且返回圖片在緩存目錄的地址
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- ContactService service = new ContactService();
- try {
- //這個URI是圖片下載到本地後的緩存目錄中的URI
- Uri uri = service.getImageURI(path, cache);
- Message msg = new Message();
- msg.what = SUCCESS_GET_IMAGE;
- msg.obj = uri;
- mHandler.sendMessage(msg);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- new Thread(runnable).start();
- }*/
- }
自定義Adapter中,我們要注意 AsyncImageTask這個類繼承了AsyncTask類,AsyncTask是Android中常用來做異步任務的類,對線程池進行了封裝,詳細分析稍後再貼出一篇Blog。
下麵是我們從服務器上獲取並且解析的Xml文件
- <?xml version="1.0" encoding="UTF-8"?>
- <contacts>
- <contact id="1">
- <name>張飛</name>
- <image src="https://192.168.1.103:8080/mymyweb/images/1.gif"/>
- </contact>
- <contact id="2">
- <name>博文</name>
- <image src="https://192.168.1.103:8080/myweb/images/2.gif"/>
- </contact>
- <contact id="3">
- <name>張天佑</name>
- <image src="https://192.168.1.103:8080/myweb/images/3.gif"/>
- </contact>
- <contact id="4">
- <name>鬆德</name>
- <image src="https://192.168.1.103:8080/myweb/images/4.gif"/>
- </contact>
- <contact id="5">
- <name>趙薇</name>
- <image src="https://192.168.1.103:8080/myweb/images/5.gif"/>
- </contact>
- <contact id="6">
- <name>李靜</name>
- <image src="https://192.168.1.103:8080/myweb/images/6.gif"/>
- </contact>
- <contact id="7">
- <name>李明</name>
- <image src="https://192.168.1.103:8080/myweb/images/7.gif"/>
- </contact>
- <contact id="8">
- <name>黎明</name>
- <image src="https://192.168.1.103:8080/myweb/images/8.gif"/>
- </contact>
- <contact id="9">
- <name>秦檜</name>
- <image src="https://192.168.1.103:8080/myweb/images/9.gif"/>
- </contact>
- <contact id="10">
- <name>朱德</name>
- <image src="https://192.168.1.103:8080/myweb/images/10.gif"/>
- </contact>
- <contact id="11">
- <name>馮鞏</name>
- <image src="https://192.168.1.103:8080/myweb/images/11.gif"/>
- </contact>
- <contact id="12">
- <name>dylan</name>
- <image src="https://192.168.1.103:8080/myweb/images/12.gif"/>
- </contact>
- <contact id="13">
- <name>黃單</name>
- <image src="https://192.168.1.103:8080/myweb/images/13.gif"/>
- </contact>
- <contact id="14">
- <name>含蕊</name>
- <image src="https://192.168.1.103:8080/myweb/images/14.gif"/>
- </contact>
- <contact id="15">
- <name>欣琪</name>
- <image src="https://192.168.1.103:8080/myweb/images/15.jpg"/>
- </contact>
- <contact id="16">
- <name>李忠華</name>
- <image src="https://192.168.1.103:8080/myweb/images/16.jpg"/>
- </contact>
- <contact id="17">
- <name>方產員</name>
- <image src="https://192.168.1.103:8080/myweb/images/17.jpg"/>
- </contact>
- <contact id="18">
- <name>張光</name>
- <image src="https://192.168.1.103:8080/myweb/images/18.jpg"/>
- </contact>
- </contacts>
本demo中為了安全起見,還對下載下來的圖片的文件名進行了MD5加密,下麵是MD5加密的代碼,
- 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();
- }
- }
最後更新:2017-04-04 07:03:45