标签:android4.0之后使用listvi handler机制 自定义适配器 多线程下载
平时的一些Android学习视频中,他们都是基于Android的去使用ListView,我看到都是会在UI线程中去访问网络获取数据,但是这在Android4.0之后是行不通的。
首先我们来理一下思绪:
我们需要从网络上下载一份xml数据,里面包含了需要显示的文字和图片路径。所以我们首先需要的就是先去下载数据,下载数据完成之后再在Adapter中显示图片的时候去下载图片,然后显示出来。这是基本的思路。但是做着做着会发现一些问题,比如,我们如何能保证数据下载完全,才去绑定适配器和数据他们。然后假如我们是在一条子线程中去完成下载数据,下载完成之后再去绑定适配器,这样子貌似可以,但是会发现有一些问题,我们需要自定义适配器然后去更新ImageView,这就需要使用到Handler。那么下载完成绑定适配器,如何再在适配器中去更新UI呢,这时候的适配器是运行在子线程的,假如把Ui线程的handler作为参数给了adapter,那么宅adapter里面发送消息给UI的handler,可以UI的handler如何找的到属于ListView的一个Item的ImageView,所以这个行不通。
所以正确的做法应该是:我们应该在UI线程绑定适配器,我们先使用没有值的List传给adapter,这时候适配器就运行在UI线程了,同时在UI线程中启动一条线程去下载数据,假如下载完成,则发送使用Handler发送一条消息,这时候handler应该使用的是adapter中的handler,因为adapter是运行在UI线程,不需要再有Looper,直接使用Handler的handlerMessage()就好。然后在adapter的getView方法中,在开启一条线程去完成图片的下载,假如下载完成,则使用adapter的handler发送一条消息,定义一个tag显示下载完成。假如下载失败则发送一个空消息,what为-1这样,就能在adapter的Handler中去处理ImageView,然后更新它了。这就是大概的思路,下面请看源码分析:
MainActivity.java他的主要功能是绑定数据和适配器,ListView和适配器,启动一条线程去现在.xml文件
public class MainActivity extends Activity
{
private ListView listview;//显示数据的ListView
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File cache = new File(Environment.getExternalStorageDirectory(), "cahce");//创建缓存的目录文件夹
if (!cache.exists())
cache.mkdirs();
listview = (ListView)findViewById(R.id.listview);//找到ListView
List<Contacts> data = new ArrayList<Contacts>();//首先使用空的data
ListViewAdapter adapter = new ListViewAdapter(this,R.layout.item_listview,data,cache);//绑定适配器,这时候就使得适配器运行在UI线程中
listview.setAdapter(adapter);//绑定ListView和适配器
new MyThread().start();//启动线程去下载.xml数据
}
}
class MyThread extends Thread
{
@Override
public void run()
{
try
{
ContacesService.getContacts(ListViewAdapter.mHandler);//下载.xml数据
} catch (Exception e)
{
ListViewAdapter.mHandler.sendEmptyMessage(-1);//有异常则利用,必须只能利用adapter的handler去发送一条消息通知下载失败
e.printStackTrace();
}
}
}
ListAdapter.java,他的功能主要是把数据显示在ListVIew上,然后其中需要启动线程去下载图片,有关于这个listview的消息都需要通过该handler发送然后达到这里去处理。
public class ListViewAdapter extends BaseAdapter
{
private static final int OK = 2;//图片下载完成msg.what=1
private static final int FAILE = -1;//图片下载失败msg.what=-1
private static ImageView image;
private File cache;
@SuppressLint("HandlerLeak")
public static Handler mHandler = new Handler()//数据UI的Handler
{
@SuppressWarnings("unchecked")
public void handleMessage(android.os.Message msg) {
if(msg.what == 1)
{
data.addAll((List<Contacts>) msg.obj);//数据下载完成则更新data
}
if(image!= null && msg.what == OK)
{
image.setImageURI((Uri) msg.obj);//图片下载完成则更新ImageView
}
};
};
private static List<Contacts> data;//数据
private int itemListviewl;//layout的id
private LayoutInflater inflater;//layout填充器
public ListViewAdapter (Activity mainActivity , int itemListview , List<Contacts> data,File cache)
{
ListViewAdapter.data = data;
this.itemListviewl = itemListview;
this.cache = cache;
inflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
//获得item的总数目
@Override
public int getCount()
{
return data.size();
}
//获得某一个item的数据
@Override
public Object getItem(int position)
{
return data.get(position);
}
//获得某一个item所在数据中的位置
@Override
public long getItemId(int position)
{
return position;
}
//每一次需要显示在ListView的条目都会调用该方法,来获得一个view然后把对应的数据显示出来
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder = null;
if(convertView == null)
{
convertView = inflater.inflate(itemListviewl, null);
holder = new ViewHolder();
holder.imageView = (ImageView)convertView.findViewById(R.id.imageview);
holder.textview = (TextView)convertView.findViewById(R.id.textView);
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
Contacts contact = data.get(position);
holder.textview.setText(contact.name);
asyncTask(contact.path,holder.imageView,cache);
return convertView;
}
/**
* 下载图片
* @param path 下载路径
* @param imageView 下载图片需要显示的ImageView
* @param cache 下载图片需要保存的文件夹
*/
private void asyncTask(final String path, final ImageView imageView,final File cache)
{
Runnable runnable = new Runnable()
{
public void run()
{
try
{
Uri uri = ContacesService.getImage(path,cache);
System.out.println("uri="+uri);
if(uri != null)
{
Message msg = Message.obtain();
msg.what = OK;
msg.obj = uri;
image = imageView;
mHandler.sendMessage(msg);//下载成功发送消息
}
else
{
mHandler.sendEmptyMessage(FAILE);//下载失败发送消息
}
} catch (Exception e)
{
mHandler.sendEmptyMessage(FAILE);//下载失败发送消息
e.printStackTrace();
}
}
};
new Thread(runnable).start();//启动线程开始下载
}
/**
* 当条目多的时候用于增加性能
* @author Administrator
*
*/
class ViewHolder
{
ImageView imageView;
TextView textview;
}
}
/**
* 各种数据的下载实现类
*
* @author Administrator
*
*/
public class ContacesService
{
/**
* 下载.xml文件的驱动类
*
* @param handler
* 需要发送消息的adapter中的handler
* @throws Exception
*/
public static void getContacts(Handler handler) throws Exception
{
String path = "http://192.168.1.101:8080/web/list.xml";
HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (200 == conn.getResponseCode())
{
System.out.println("11111");
InputStream inputStream = conn.getInputStream();
parserXML(inputStream, handler);
}
}
/**
* 下载并且解析.xml文件 生成一个集合利用handler发送给adapter处理
*
* @param inputStream
* 输入流
* @param handler需要发送消息的adapter中的handler
* @throws Exception
* @throws IOException
*/
private static void parserXML(InputStream inputStream, Handler handler) throws Exception, IOException
{
XmlPullParser parser = Xml.newPullParser();
parser.setInput(inputStream, "UTF-8");
List<Contacts> contacts = new ArrayList<Contacts>();
System.out.println(2222);
Contacts contact = null;
int event = parser.getEventType();
while (XmlPullParser.END_DOCUMENT != event)
{
switch (event)
{
case XmlPullParser.START_TAG:
if ("contact".equals(parser.getName()))
{
contact = new Contacts();
contact.id = Integer.valueOf(parser.getAttributeValue(0));
}
if ("name".equals(parser.getName()))
{
contact.name = parser.nextText();
}
if ("image".equals(parser.getName()))
{
contact.path = parser.getAttributeValue(0);
}
break;
case XmlPullParser.END_TAG:
if ("contact".equals(parser.getName()))
{
contacts.add(contact);
contact = null;
}
break;
}
event = parser.next();
}
if (contacts.size() != 0)
{
Message msg = Message.obtain();
msg.what = 1;
msg.obj = contacts;
System.out.println(contacts);// 用于测试是否下载成功
handler.sendMessage(msg);// 发送adapter,让他去更新data的值
} else
{
handler.sendEmptyMessage(-1);// 失败发送-1
}
}
/**
* 实现从网络下载图片并且保存本地,当本地存在该图片则直接读取
*
* @param path
* 图片的路径
* @param cache
* 缓存二栋目录
* @return
* @throws Exception
*/
public static Uri getImage(String path, File cache) throws Exception
{
File localFile = new File(cache, MD5.getMD5(path) + path.substring(path.lastIndexOf(".")));// MD
// 5加密
if (localFile.exists())
{
return Uri.fromFile(localFile);
} else
{
HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if (200 == conn.getResponseCode())
{
System.out.println("11111");
InputStream inputStream = conn.getInputStream();
FileOutputStream fos = new FileOutputStream(localFile);
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1)
{
fos.write(buffer, 0, len);
}
fos.close();
inputStream.close();
return Uri.fromFile(localFile);
}
}
return null;
}
}domai类
public class Contacts
{
public int id;
public String name;
public String path;
public Contacts (){}
public Contacts (int id , String name , String path)
{
super();
this.id = id;
this.name = name;
this.path = path;
}
@Override
public String toString()
{
return "Contacts [id=" + id + ", name=" + name + ", path=" + path + "]";
}
}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();
}
}
关于布局:main.xml
<LinearLayout 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:orientation="vertical"
>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview"/>
</LinearLayout>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:id="@+id/imageview"
android:layout_width="50dp"
android:layout_height="50dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
android:textSize="20sp" />
</LinearLayout><uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />最后得到的结果截图:
基于Android4.0ListView从网络获取图片文字资源显示
标签:android4.0之后使用listvi handler机制 自定义适配器 多线程下载
原文地址:http://blog.csdn.net/liweijie_chengxuyuan/article/details/45114037