在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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
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();
}
}
|
Activity中,注意以下几点,
1.初始化了一个缓存目录,这个目录最好是应用开启就去创建好,为手续缓存图片做准备,在这里把数据存放在SDCard上
2.要去服务器加载数据,这个耗时操作最好是去开启线程加载数据,加载完毕后去异步的更新UI线程,利用Handler机制能很好的解决这个问题,
3.最后退出应用的时候,要删掉缓存目录和目录里面的数据,避免给手机制造很多的垃圾文件
下面就是一个Service类了,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
public
class
ContactService {
/**
* 从服务器上获取数据
*/
public
List<Contact> getContactAll()
throws
Exception {
List<Contact> contacts =
null
;
String Parth =
"http://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类,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
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文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
contacts
>
<
contact
id
=
"1"
>
<
name
>张飞</
name
>
<
image
src
=
"http://192.168.1.103:8080/mymyweb/images/1.gif"
/>
</
contact
>
<
contact
id
=
"2"
>
<
name
>博文</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/2.gif"
/>
</
contact
>
<
contact
id
=
"3"
>
<
name
>张天佑</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/3.gif"
/>
</
contact
>
<
contact
id
=
"4"
>
<
name
>松德</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/4.gif"
/>
</
contact
>
<
contact
id
=
"5"
>
<
name
>赵薇</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/5.gif"
/>
</
contact
>
<
contact
id
=
"6"
>
<
name
>李静</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/6.gif"
/>
</
contact
>
<
contact
id
=
"7"
>
<
name
>李明</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/7.gif"
/>
</
contact
>
<
contact
id
=
"8"
>
<
name
>黎明</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/8.gif"
/>
</
contact
>
<
contact
id
=
"9"
>
<
name
>秦桧</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/9.gif"
/>
</
contact
>
<
contact
id
=
"10"
>
<
name
>朱德</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/10.gif"
/>
</
contact
>
<
contact
id
=
"11"
>
<
name
>冯巩</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/11.gif"
/>
</
contact
>
<
contact
id
=
"12"
>
<
name
>dylan</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/12.gif"
/>
</
contact
>
<
contact
id
=
"13"
>
<
name
>黄单</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/13.gif"
/>
</
contact
>
<
contact
id
=
"14"
>
<
name
>含蕊</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/14.gif"
/>
</
contact
>
<
contact
id
=
"15"
>
<
name
>欣琪</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/15.jpg"
/>
</
contact
>
<
contact
id
=
"16"
>
<
name
>李忠华</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/16.jpg"
/>
</
contact
>
<
contact
id
=
"17"
>
<
name
>方产员</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/17.jpg"
/>
</
contact
>
<
contact
id
=
"18"
>
<
name
>张光</
name
>
<
image
src
=
"http://192.168.1.103:8080/myweb/images/18.jpg"
/>
</
contact
>
</
contacts
>
|
本demo中为了安全起见,还对下载下来的图片的文件名进行了MD5加密,下面是MD5加密的代码,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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();
}
}
|
以上省略了Contact.java这个domain类,通过这个demo,可以看出Android中会经常需要进行异步任务的处理,所以我们会常常用到自己手动开启线程,handler机制,或者AsyncTask类等手段来保证应用的性能。
本篇博文是从本人iteye博客上搬过来的,发现第一次进OSC就喜欢这里了,感谢创始人为我们提供了这么好的一个平台!