先来看看效果示意图:
step1:新建项目DataAsyncLoad,如下图所示
step2:设置应用的UI界面
a.应用的主界面 main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/listView" /> </LinearLayout>
b.每个ListView的界面 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="42dp" android:layout_height="42dp" 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>
step3:写一些辅助类 cn.roco.data.utilsMD5.java
package cn.roco.data.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(); } }
step4:写应用使用的JavaBean cn.roco.data.domain.Contact.java
package cn.roco.data.domain; public class Contact { private int id; private String name; private String image; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getImage() { return image; } public void setImage(String image) { this.image = image; } public Contact(int id, String name, String image) { this.id = id; this.name = name; this.image = image; } public Contact(){ } }
step5:写一个应用的service层,用于对javabean进行操作 cn.roco.data.service.ContactService.java
package cn.roco.data.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.roco.data.domain.Contact; import cn.roco.data.utils.MD5; public class ContactService { /** * 获取联系人数据 * * @return * @throws Exception */ public static List<Contact> getContacts() throws Exception { String path = "http://192.168.1.100:8080/Hello/contact.xml"; HttpURLConnection connection = (HttpURLConnection) new URL(path) .openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); if (connection.getResponseCode() == 200) { return parseXML(connection.getInputStream()); } return null; } /**转化XML获取数据 * 服务器端的xml文件如下。。。。。。 * <?xml version="1.0" encoding="UTF-8"?> <contacts> <contact id="1"> <name>Roco_1</name> <image src="http://192.168.1.100:8080/Hello/images/1.png" /> </contact> ....... </contacts>*/ private static List<Contact> parseXML(InputStream inputStream) throws Exception { List<Contact> contacts = new ArrayList<Contact>(); Contact contact = null; XmlPullParser pullParser = Xml.newPullParser(); pullParser.setInput(inputStream, "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.setId(new Integer(pullParser.getAttributeValue(0))); } else if ("name".equals(pullParser.getName())) { contact.setName(pullParser.nextText()); } else if ("image".equals(pullParser.getName())) { contact.setImage(pullParser.getAttributeValue(0)); } break; case XmlPullParser.END_TAG: if ("contact".equals(pullParser.getName())) { contacts.add(contact); contact = null; } } event = pullParser.next(); } return contacts; } /** * 获取网络图片,如果图片存在于缓存中,就返回该图片,否则从网络中加载该图片并缓存起来 * * @param path * 图片路径 * @return */ public static Uri getImage(String imagePath, File cacheDir) throws Exception { //缓存文件的文件名用MD5进行加密 File localFile = new File(cacheDir, MD5.getMD5(imagePath) + imagePath.substring(imagePath.lastIndexOf("."))); if (localFile.exists()) { return Uri.fromFile(localFile); } else { HttpURLConnection connection = (HttpURLConnection) new URL( imagePath).openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); //将文件缓存起来 if (connection.getResponseCode() == 200) { FileOutputStream outputStream = new FileOutputStream(localFile); InputStream inputStream = connection.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, len); } inputStream.close(); outputStream.close(); return Uri.fromFile(localFile); } } return null; } }
step6:写一个Adapter用于对ListView进行数据更新 cn.roco.data.adapter.ContactAdapter.java
package cn.roco.data.adapter; import java.io.File; import java.util.List; import cn.roco.data.R; import cn.roco.data.domain.Contact; import cn.roco.data.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; /**适配器,用于更新View*/ public class ContactAdapter extends BaseAdapter { private List<Contact> data; private int listviewItem; private File cache; /** * LayoutInflater的作用类似于 findViewById(),不同点是LayoutInflater是用来找layout文件夹下的xml布局文件,并且实例化! * 而 findViewById()是找具体某一个xml下的具体 widget控件(如:Button,TextView等)。 */ private LayoutInflater layoutInflater; public ContactAdapter(Context context, List<Contact> data, int listviewItem, File cache) { this.data = data; this.listviewItem = listviewItem; this.cache = cache; this.layoutInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);//取得xml里定义的view /*** * getSystemService()是Android很重要的一个API,它是Activity的一个方法, * 根据传入的NAME来取得对应的Object,然后转换成相应的服务对象。以下介绍系统相应的服务。 * 传入的Name 返回的对象 说明 WINDOW_SERVICE WindowManager 管理打开的窗口程序 LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view ACTIVITY_SERVICE ActivityManager 管理应用程序的系统状态 POWER_SERVICE PowerManger 电源的服务 ALARM_SERVICE AlarmManager 闹钟的服务 NOTIFICATION_SERVICE NotificationManager 状态栏的服务 KEYGUARD_SERVICE KeyguardManager 键盘锁的服务 LOCATION_SERVICE LocationManager 位置的服务,如GPS SEARCH_SERVICE SearchManager 搜索的服务 VEBRATOR_SERVICE Vebrator 手机震动的服务 CONNECTIVITY_SERVICE Connectivity 网络连接的服务 WIFI_SERVICE WifiManager Wi-Fi服务 TELEPHONY_SERVICE TeleponyManager 电话服务 */ } /** 得到数据的总数 */ @Override public int getCount() { return data.size(); } /** 根据数据索引,得到集合中所对应的数据 */ @Override public Object getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override 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.getImageView(); textView=dataWrapper.getTextView(); } Contact contact=data.get(position); textView.setText(contact.getName()); /**异步加载图片文件*/ asynchImageLoad(imageView,contact.getImage()); return convertView; } /* //该方法会创建很多的线程,也会很耗资源 private void asynchImageLoad(final ImageView imageView, final String imagePath) { final Handler handler=new Handler(){ @Override public void handleMessage(Message msg) {//运行在主线程中 Uri uri=(Uri) msg.obj; if (uri!=null&&imageView!=null) { imageView.setImageURI(uri); } } }; Runnable runnable=new Runnable() { @Override public void run() { try { Uri uri=ContactService.getImage(imagePath, cache); handler.sendMessage(handler.obtainMessage(10,uri)); } catch (Exception e) { e.printStackTrace(); } } }; new Thread(runnable).start(); } */ /**异步加载图片文件*/ private void asynchImageLoad(ImageView imageView, String imagePath) { AsycImageTask asycImageTask=new AsycImageTask(imageView); asycImageTask.execute(imagePath); } /** * 使用AsyncTask提高性能 * 可选方法: 1, onprogressupdate(progress…) 可以使用进度条增加用户体验度。此方法在主线程执行,用户显示任务执行的进度。 2, onpreExecute() 这里是最新用户调用excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。 3, onCancelled() 用户调用取消时,要做的操作。 AsyncTask<Params, Progress, Result> AsyscTask定义了三种泛型类型params,progress和result. 1, params启动任务执行的输入参数,比如http请求的URL 2, progress后台任务执行的百分比 3, result后台执行任务最终返回的结果,比如String,比如我需要得到的list。 使用AsyncTask类,遵守的准则:1, Task的实例必须在UI thread中创建;2, Execute方法必须在UI thread中调用 3, 不要手动的调用onPfreexecute(),onPostExecute(result)Doinbackground(params…),onProgressupdate(progress…)这几个方法; 4, 该task只能被执行一次,否则多次调用时将会出现异常; AsyncTask的整个调用过程都是从execute方法开始的,一旦在主线程中调用execute方法,就可以通过onpreExecute方法, 这是一个预处理方法,比如可以在这里开始一个进度框,同样也可以通过onprogressupdate方法给用户一个进度条的显示,增加用户体验; 最后通过onpostexecute方法,相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回 */ private final class AsycImageTask extends AsyncTask<String, Integer, Uri>{ private ImageView imageView; public AsycImageTask(ImageView imageView) { this.imageView=imageView; } /** * 后台执行,比较耗时的操作都可以放在这里。 注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作 ,通常需要较长的时间。在执行过程中可以调用 Public progress(progress…)来更新任务的进度。 */ @Override protected Uri doInBackground(String... params) {//子线程中执行 try { return ContactService.getImage(params[0], cache); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果 * 处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回。 */ @Override protected void onPostExecute(Uri result) {//运行在主线程 if (result!=null&&imageView!=null) { imageView.setImageURI(result); } } } /**数据包装类*/ private final class DataWrapper { private ImageView imageView; private TextView textView; public ImageView getImageView() { return imageView; } public TextView getTextView() { return textView; } public DataWrapper(ImageView imageView, TextView textView) { this.imageView = imageView; this.textView = textView; } } }
step7:应用的主程序 cn.roco.data.MainActivity.java
package cn.roco.data; import java.io.File; import java.util.List; import cn.roco.data.adapter.ContactAdapter; import cn.roco.data.domain.Contact; import cn.roco.data.service.ContactService; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.widget.ListView; public class MainActivity extends Activity { private ListView listView; /**缓存文件*/ private File cache; /**接受消息,处理消息 ,此Handler会与当前主线程一块运行 * 使用匿名内部类来复写Handler当中的handlerMessage()方法 */ Handler handler = new Handler() { // 接受数据 public void handleMessage(android.os.Message msg) { //设置适配器,将获取的数据使用适配器更新View 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); /**在SD卡中生成缓存目录*/ cache = new File(Environment.getExternalStorageDirectory(), "cache"); /**如果目录不存在就新建一个*/ if (!cache.exists()) cache.mkdir(); new Thread(new Runnable() { @Override public void run() { try { //获取联系人数据 List<Contact> data= ContactService.getContacts(); // 向Handler发送消息,更新UI 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(); } }
step8:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.roco.data" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" /> <!-- 访问Internet权限 --> <uses-permission android:name="android.permission.INTERNET" /> <!-- 在SD卡中创建和删除文件权限 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 往SD卡中写入数据权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
step9:编写服务器端的代码,主要是一个contact.xml文件
<?xml version="1.0" encoding="UTF-8"?> <contacts> <contact id="1"> <name>Roco_1</name> <image src="http://192.168.1.100:8080/Hello/images/1.png" /> </contact> <contact id="2"> <name>Roco_2</name> <image src="http://192.168.1.100:8080/Hello/images/2.png" /> </contact> <contact id="3"> <name>Roco_3</name> <image src="http://192.168.1.100:8080/Hello/images/3.png" /> </contact> <contact id="4"> <name>Roco_4</name> <image src="http://192.168.1.100:8080/Hello/images/4.png" /> </contact> <contact id="5"> <name>Roco_5</name> <image src="http://192.168.1.100:8080/Hello/images/5.png" /> </contact> <contact id="6"> <name>Roco_6</name> <image src="http://192.168.1.100:8080/Hello/images/6.png" /> </contact> <contact id="7"> <name>Roco_7</name> <image src="http://192.168.1.100:8080/Hello/images/7.png" /> </contact> <contact id="8"> <name>Roco_8</name> <image src="http://192.168.1.100:8080/Hello/images/8.png" /> </contact> <contact id="9"> <name>Roco_9</name> <image src="http://192.168.1.100:8080/Hello/images/9.png" /> </contact> <contact id="10"> <name>Roco_10</name> <image src="http://192.168.1.100:8080/Hello/images/10.png" /> </contact> <contact id="11"> <name>Roco_11</name> <image src="http://192.168.1.100:8080/Hello/images/11.png" /> </contact> <contact id="12"> <name>Roco_12</name> <image src="http://192.168.1.100:8080/Hello/images/12.png" /> </contact> <contact id="13"> <name>Roco_13</name> <image src="http://192.168.1.100:8080/Hello/images/13.png" /> </contact> <contact id="14"> <name>Roco_14</name> <image src="http://192.168.1.100:8080/Hello/images/14.png" /> </contact> <contact id="15"> <name>Roco_15</name> <image src="http://192.168.1.100:8080/Hello/images/15.png" /> </contact> <contact id="16"> <name>Roco_16</name> <image src="http://192.168.1.100:8080/Hello/images/16.png" /> </contact> <contact id="17"> <name>Roco_17</name> <image src="http://192.168.1.100:8080/Hello/images/17.png" /> </contact> <contact id="18"> <name>Roco_18</name> <image src="http://192.168.1.100:8080/Hello/images/18.png" /> </contact> <contact id="19"> <name>Roco_19</name> <image src="http://192.168.1.100:8080/Hello/images/19.png" /> </contact> <contact id="20"> <name>Roco_20</name> <image src="http://192.168.1.100:8080/Hello/images/20.png" /> </contact> </contacts>
以及在images目录下放置了一些图片
step10:将项目部署到模拟器上运行效果如下图:
==================================下面看一个gif动画===========================================
在SD卡中会生成缓存文件
当应用退出的时候,会将缓存文件删除
有了缓存文件,只要应用没有退出,即使联网不成功,也可以读取缓存中的图片文件
==================================================================================================
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:http://blog.csdn.net/ouyang_peng
==================================================================================================