一、界面布局文件:

  1、加入sdcard写入和网络权限:

    <!-- 访问internet权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  2、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" > <!-- 这里给listview又从新建了一个条目文件 布局是水平布局.然后引入,ImageView控件.
--> <ImageView
android:id="@+id/imageView"
android:layout_width="120dp"
android:layout_height="120dp" /> <!-- 用来显示照片下面的文字 --> <TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp" > </TextView> </LinearLayout>

  3、activity_main.xml文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" > <ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/listView"/> </RelativeLayout>

二、后台代码实现:
  1、将文件路径转换成32位字符串:

package com.example.service;

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();
}
}

  2、联系人实体类:

package com.example.service;

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() {
}
}

  3、联系人业务类:

  解析从服务端传过来的xml文件,获取联系人的图片,对图片进行缓存。

package com.example.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.annotation.SuppressLint;
import android.net.Uri;
import android.util.Xml; public class ContactService { /**
* 获取联系人数据
* @return
*/
public static List<Contact> getContacts() throws Exception {
String path = "http://192.168.8.102:8080/Simple/GetImageList";
HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200) {
return parseXML(conn.getInputStream());
}
return null;
} @SuppressLint("UseValueOf")
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
// 对路径进行MD5加密,过后就可以得到一个32位的字符串.
// 得到这个字符串后加.jpg然后放到缓存路径中.
// 根据缓存的路径得到缓存数据
// MD5.getMD5(path)+ path.substring(path.lastIndexOf(".")
// 通过md5,从最后一个.开始得到.jpg等等.
File localFile = new File(cacheDir, MD5.getMD5(path)+ path.substring(path.lastIndexOf(".")));
// 在缓存文件夹中判断有没有这个缓存文件,也就是经过MD5加密后的.jpg文件
if (localFile.exists()) {
// 判断如果存在这个缓存文件,就返回这个文件的uri对象
return Uri.fromFile(localFile);
} else {
// 如果缓存文件夹中没有这个文件,就在网络中获取这个图片缓存文件
HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (conn.getResponseCode() == 200) {
// 往localFile中写数据
FileOutputStream outStream = new FileOutputStream(localFile);
InputStream inputStream = conn.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
// 通过输入流,从http中通过get获得的输入流充取得数据
while ((len = inputStream.read(buffer)) != -1) {
// 输出流指定了输入的文件,把数据输入到指定的文件中
// new FileOutputStream(localFile);
outStream.write(buffer, 0, len);
}
// 关闭这个流.
inputStream.close();
outStream.close();
return Uri.fromFile(localFile);
}
}
return null;
}
}

  4、自定义联系人适配器:

  这里加载图片时使用异步加载

package com.example.service;

import java.io.File;
import java.util.List; import com.example.listviewasync.R; import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
/**
* 自定义适配器
* @author Administrator
*
*/
public class ContactAdapter extends BaseAdapter {
private List<Contact> data;
// 6.指定的某个条目.
private int listviewItem;
private File cache;
private LayoutInflater layoutInflater; // 1.这个构造器就是用来接收数据用的适配器
public ContactAdapter(Context context, List<Contact> data,
int listviewItem, File cache) {
// 2.接收的数据
this.data = data;
// 7.条目是传递过来的.
this.listviewItem = listviewItem;
this.cache = cache;
// 5.取得布局填充服务.
layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} /**
* 得到数据的总数
*/
public int getCount() {
return data.size();
} /**
* 根据数据索引得到集合中所对应的指定的那个数据
*/
public Object getItem(int position) {
return data.get(position);
} // 这是数据的id这里用条目的id
public long getItemId(int position) {
return position;
} // 3.当listview每显示一个条目的时候就自动调用这个方法
// convertView ,注意listview可以对第一屏显示的内容(条目对象)进行缓存
// 当显示第二屏,第三屏的时候会使用缓存的数据.convertView就是缓存的第一屏数据.
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = null;
TextView textView = null;
// 4.如果显示的是第一屏,也就是说convertView还没有进行缓存.就进行创建第一屏
if (convertView == null) {
// 这里需要到布局服务,而布局服务需要上下文对象,所以这里在构造器中
// 加一个参数,上下文对象.
// 可以看到通过布局服务,可以得到条目对象.
// 当第一次显示的时候直接把得到的条目赋值给缓存对象.
convertView = layoutInflater.inflate(listviewItem, null);
// 每次要为imageView,textView控件赋值参数的时候
// 都要查找到这两个对象.这样是很消耗性能的,所以这里用内部类
// 对这两个对象进行了临时保存
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;
} // 实现图片的异步加载版本2,这里使用AsyncTask这个异步类来实现.可以提升性能.
private void asyncImageLoad(ImageView imageView, String path) {
// a.创建这个对象把imageView传进去.
AsyncImageTask asyncImageTask = new AsyncImageTask(imageView);
// 异步处理数据。
// b.通过这个类的execute方法执行任务,在子线程中.
asyncImageTask.execute(path);
} // AsyncTask这个类是一个抽象类,要继承才能使用.
// 这个类打开源码可以看到:
/*
* 定义了三个泛型参数,作为输入参数
* 1.首先会执行execute方法,执行execute方法的时候收先会执行onPreExecute方法.然后再通过线程池对象sExecute 来执
* 行sExecute.execute(mFuture)把工作交给线程池对象.然后线程池选择一条线程去工作.
* 2.工作是在线程中执行的:可以看到:mtask先处理任务,然后回调done方法来进行取得结果,然后把结果返回给mFuture.这些都是在子线
* 程中的.
* 3.然后把结果交给handler进行发送给主线程,这步是在主线程中执行的.
* 4.onPreExecute方法在ui线程中调用 doinBackground方法在子线程中运行:执行任务,取得结果onPostExecute运行在ui
* 线程,也就是主线程中,把结果交给消息处理器onProcessUpdate更新ui.比较适合用来构建进度条,在主线程中执行.
*/
/*
* 这个类使用了线程池,AsyncTask这个异步类实现数据的异步传输. AsyncTask<String, Integer,
* Uri>。
   *第一个参数:指的是doInBackground输入类型参数
*第二个参数:指的是onProgressUpdate方法接收的参数,用于更新UI,可以在doInBackground方法里面调用
   *第三个参数:是在子线程中执行doInBackground方法的返回值类型,和执行onPostExecute方法的接收参数,此时子线程已经执行完毕。
*/
private final class AsyncImageTask extends AsyncTask<String, Integer, Uri> {
private ImageView imageView; public AsyncImageTask(ImageView imageView) {
// 设定参数
this.imageView = imageView;
} // c.在子线程中运行这个方法,然后把结果给handler对象,这个handler运行在主线程中.
// doInBackground这个方法在子线程中执行.
protected Uri doInBackground(String... params) {// 子线程中执行的
// String... params这里是可变参数,params是变量名字.
try {
return ContactService.getImage(params[0], cache);
} catch (Exception e) {
e.printStackTrace();
}
return null;
} // d.更新控件.
// 这个方法用来取得返回结果进行更新控件.
protected void onPostExecute(Uri result) {// 运行在主线程
if (result != null && imageView != null)
imageView.setImageURI(result);
}
} // --------------------------------
// 这里默认的线程最大线程数是128条.
// 一.-----------------------------------
/*
* //3.asyncImageLoad如果调用这个方法实现的时候 每显示一个条目就会创建很多个线程所以这里当显示完图片后会创建
* 很多的线程.,加入数据10000条的话,那么这里就要创建10000个线程 这个结果很可怕,这里的方法是限定开启的线程数量.提高性能
* 限定最多运行的线程数量.这里就使用了AsyncTask这个类:其实 这个类也是通过handler+线程来实现数据的异步加载的,不同的是
* 这里的线程它使用的是线程池.线程池可以限定线程的数量.线程池和连接池的原理差不多的
* 正式因为AsyncTask类是使用Handler+Thread+线程池来实现的 就提升了性能,并且可以实现线程的重用.
* //2.这个方法只实现了图片的异步加载,实际上数据的加载,也需要 使用异步的因为,比如,如果主线程在运行过程中阻塞了,这时候
* 还没有运行到数据加载的地方,那么就会出现无响应的错误. //1. 这个是asyncImageLoad,图片异步加载的第一个版本 private
* void asyncImageLoad(final ImageView imageView, final String path) { //
* 所以这里,这里采用匿名内部类实现,这时候handler也是在主线程中的 // 这里的handler主要作用是往主线程发送数据. final
* Handler handler = new Handler(){ // 这个方法也是运行在主线程中. public void
* handleMessage(Message msg) {//运行在主线程中 // 在这里进行消息的接收,这个消息是一个uri对象,可以被转换.
* Uri uri = (Uri)msg.obj; if(uri!=null && imageView!= null) //
* 在给imageView设置之前要判断. imageView.setImageURI(uri); } };
*
* Runnable runnable = new Runnable() { public void run() { try { //
* getImage方法,已经实现了图片的缓存和从网络中获取的功能. Uri uri = ContactService.getImage(path,
* cache); // 因为在子线程中不能更新ui控件的值.所以需要采用hanler技术实现. //
* 用handler发送消息,这里handler需要做成final的才可以在内部类中使用 // handler.obtainMessage(10,
* uri),用这个方法获取消息,同时也可以快速 // 的创建一个消息出来.10:这个是消息的id,这里用不到随便定义了一个, uri
* handler.sendMessage(handler.obtainMessage(10, uri)); } catch (Exception
* e) { e.printStackTrace(); } } }; //// 启动这个runnable对象. new
* Thread(runnable).start(); }
*/
// 这个内部类,用来提高性能,
/*
* // 每次要为imageView,textView控件赋值参数的时候 // 都要查找到这两个对象.这样是很消耗性能的. imageView =
* (ImageView) convertView.findViewById(R.id.imageView); textView =
* (TextView) convertView.findViewById(R.id.textView);
*/
// 对查找的对象进行包装.
private final class DataWrapper {
public ImageView imageView;
public TextView textView; public DataWrapper(ImageView imageView, TextView textView) {
this.imageView = imageView;
this.textView = textView;
}
}
}

  5、activity后台代码:

package com.example.listviewasync;

import java.io.File;
import java.util.List; import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.widget.ListView; import com.example.service.Contact;
import com.example.service.ContactAdapter;
import com.example.service.ContactService; public class MainActivity extends Activity {
ListView listView;
/** 放图片的缓存文件. */
File cache;
// 为了实现数据和图片都是异步加载的,这里对数据也需要实现异步
// 1.新建一个handler对象.
Handler handler = new Handler() {
@SuppressWarnings("unchecked")
public void handleMessage(Message msg) {
// 2.给listview,指定适配器,这里采用自定义适配器.
// (List<Contact>)msg.obj在这里就可以得到发送给主线程的数据消息了.
// DataAsyncLoadActivity.this 用来明确指定访问外部类的对象.
listView.setAdapter(new ContactAdapter(MainActivity.this,
(List<Contact>) msg.obj, R.layout.listview_item, cache));
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1.首先找到listview控件.
listView = (ListView) this.findViewById(R.id.listView);
// 2.初始化缓存文件.
// Environment.getExternalStorageDirectory得到名字为cache的文件夹,如果在sd卡
// 中得不到名字为cache的文件夹就创建.
cache = new File(Environment.getExternalStorageDirectory(), "cache");
// 如果cache文件夹不存在就创建.
if (!cache.exists())
cache.mkdirs();
// 在这里开一个线程用来实现数据的异步加载.
new Thread(new Runnable() {
public void run() {
try {
// 3.从服务类中取得联系人的列表.
List<Contact> data = ContactService.getContacts();
// 创建一个消息对象,把数据发给主线程.
handler.sendMessage(handler.obtainMessage(22, data));
// 4.注意这里把ContactAdapter
// 5.这里个个给创建的这个适配器进行传参数:这里
// new ContactAdapter(data,R.layout.listview_item,cache)
// 第一个:是要放到listview中的数据,第二个:listview需要的布局文件
// 第三个:是缓存文件的目录.
// listView.setAdapter(new
// ContactAdapter(data,R.layout.listview_item,cache)); } catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} @Override
protected void onDestroy() {
for (File file : cache.listFiles()) {
file.delete();
}
cache.delete();
super.onDestroy();
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
} }

三、服务端创建联系人,以xml形式发送给客户端:

public class GetImageList extends HttpServlet {
private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/xml; charset=UTF-8");
PrintWriter pw = response.getWriter();
StringBuffer sBuffer =new StringBuffer("<?xml version='1.0' encoding='utf-8'?>");
sBuffer.append("<contacts>");
Random random = new Random();
int count=1000;
for (int i = 1; i <= 100; i++) {
int img = random.nextInt(7)+1;
count++;
sBuffer.append("<contact id='"+count+"'>");
sBuffer.append("<name>tom"+i+"</name>");
sBuffer.append("<image src='http://192.168.8.102:8080/Simple/image/"+img+".png'></image>");
sBuffer.append("</contact>");
}
sBuffer.append("</contacts>");
pw.write(sBuffer.toString());
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
} }

Android学习笔记_36_ListView数据异步加载与AsyncTask的更多相关文章

  1. [置顶] iOS学习笔记47——图片异步加载之EGOImageLoading

    上次在<iOS学习笔记46——图片异步加载之SDWebImage>中介绍过一个开源的图片异步加载库,今天来介绍另外一个功能类似的EGOImageLoading,看名字知道,之前的一篇学习笔 ...

  2. Java 爬虫遇上数据异步加载,试试这两种办法!

    这是 Java 爬虫系列博文的第三篇,在上一篇 Java 爬虫遇到需要登录的网站,该怎么办? 中,我们简单的讲解了爬虫时遇到登录问题的解决办法,在这篇文章中我们一起来聊一聊爬虫时遇到数据异步加载的问题 ...

  3. Flutter学习笔记(19)--加载本地图片

    如需转载,请注明出处:Flutter学习笔记(19)--加载本地图片 上一篇博客正好用到了本地的图片,记录一下用法: 首先新建一个文件夹,这个文件夹要跟目录下 然后在pubspec.yaml里面声明出 ...

  4. android的progressDialog 的使用。android数据异步加载 对话框提示

    在调用的Activity中定义一个全局的 progressDialog 点击按钮的时候调用下面这句 progressDialog = ProgressDialog.show(SearchActivit ...

  5. android 数据异步加载

    public class MainActivity extends Activity { ListView listView; File cache; //访问其他线程在当前线程中存放的数据 Hand ...

  6. android官方开源的高性能异步加载网络图片的Gridview例子

    这个是我在安卓安卓巴士上看到的资料,放到这儿共享下.这个例子android官方提供的,其中讲解了如何异步加载网络图片,以及在gridview中高效率的显示图片此代码很好的解决了加载大量图片时,报OOM ...

  7. android Listview 软引用SoftReference异步加载图片

    首先说一下,android系统加载大量图片系统内存溢出的3中解决方法: (1)从网络或本地加载图片的时候,只加载缩略图.这个方法的确能够少占用不少内存,可是它的致命的缺点就是,因为加载的是缩略图,所以 ...

  8. vue-awesome-swiper中的数据异步加载

    <template> <div> //第一个轮播 加了v-if 判断,可以实现 loop 轮循 <swiper v-if="gglist.length>1 ...

  9. angularJS1笔记-(19)-angular异步加载包的方式

    我们平时写的导入包的方式都是同步方式,有时候会显得过于卡顿,这样我们就可以使用异步加载的方式. script.js方式: 执行结果为: 异步加载还可以加载多个即为script([,,,],functi ...

随机推荐

  1. java中的输入输出方法

    输入 import java.util.Scanner; public class EnterTest { public static void main(String[] args) { //主方法 ...

  2. 0.数据结构(python语言) 基本概念 算法的代价及度量!!!

    先看思维导图: *思维导图有点简陋,本着循循渐进的思想,这小节的知识大多只做了解即可. *重点在于算法的代价及度量!!!查找资料务必弄清楚. 零.四个基本概念 问题:一个具体的需求 问题实例:针对问题 ...

  3. 深入理解JavaScript系列(28):设计模式之工厂模式

    介绍 与创建型模式类似,工厂模式创建对象(视为工厂里的产品)时无需指定创建对象的具体类. 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子 ...

  4. 深入理解JavaScript系列(11):执行上下文(Execution Contexts)

    简介 从本章开始,我将陆续(翻译.转载.整理)http://dmitrysoshnikov.com/网站关于ECMAScript标标准理解的好文. 本章我们要讲解的是ECMAScript标准里的执行上 ...

  5. 程序员心灵鸡汤桌面壁纸1080p 欢迎大家下载,HTML,PHP,node,css,前端

    上段时间在读书的时候看见了一句话:一张地图,不论比例多么精确,它永远不可能带着主人在地面上移动半步, 作为爱思考,爱学习的程序员 突然想到了:一份帮助文档,不论多么详细,它永远不会帮助主人敲一行代码. ...

  6. 全局数据 GetGlobalDataSet

    /// <summary> /// 获取全局数据 /// </summary> /// <returns></returns> public DataS ...

  7. div内容溢出

    前几天遇到一个问题,代码是这样一个层次: <div class="province"> <ul> <li>1</li> <li ...

  8. 在 Azure Web 应用中创建 PHP 应用程序

    本分步指南将通过 Azure Web 应用帮助您启动并运行示例 PHP 应用程序.除 PHP 外,Azure Web 应用还支持其他语言,如 Java..NET.Node.JS.Python.Ruby ...

  9. Eclipse One Inspector

    net.sf.yari.eclipse.EclipseInspectorViewPart Through the outline of EclipseInspectorViewPart, we can ...

  10. arcgis server 10.1 发布动态图层展示海量及频繁更新的数据步骤

    Arcgis server  发布动态图层及调用动态图层 做这个动态图层功能的原由是 有一个30万的数据需要通过arcgis GP工具转成shp然后渲染加载进地图,原来的做法是遍历生成shp面要素,读 ...