|
最近在做一款播放器,服务器为tomcat,将音乐所有信息放在服务器端,客户端与服务器端之间的通信是同http协议进行的。首先把服务器端的数据部署完成,然后客户端
采用pull解析器将服务器端传过来的实体对象解析成music对象,再将需要显示的数据(包括文本和图片)利用自定义的适配器显示在listview上。现在,本人将文本通过异步加载的方式显示在listview上,而图片则通过线程+handler机制来加载和更新,并将图片作为缓存存到内存和文件。。。
具体实现代码如下:
实体类:Music.java
1 package com.zsw.entity;
2
3 public class Music {
4 /*对应属性id*/
5 private int id;
6 /*音乐名称*/
7 private String name;
8 /*音乐专辑*/
9 private String album;
10 /*歌手*/
11 private String singer;
12 /*作者*/
13 private String author;
14 /*作曲者*/
15 private String composer;
16 /*播放时长*/
17 private String duration;
18 /*相册图片路径*/
19 private String albumPicPath;
20 /*音乐文件路径*/
21 private String musicPath;
22 /*下载次数*/
23 private int downCount;
24 /*收藏次数*/
25 private int favCount;
26 public int getId() {
27 return id;
28 }
29 public void setId(int id) {
30 this.id = id;
31 }
32 public String getName() {
33 return name;
34 }
35 public void setName(String name) {
36 this.name = name;
37 }
38 public String getAlbum() {
39 return album;
40 }
41 public void setAlbum(String album) {
42 this.album = album;
43 }
44 public String getSinger() {
45 return singer;
46 }
47 public void setSinger(String singer) {
48 this.singer = singer;
49 }
50 public String getAuthor() {
51 return author;
52 }
53 public void setAuthor(String author) {
54 this.author = author;
55 }
56 public String getComposer() {
57 return composer;
58 }
59 public void setComposer(String composer) {
60 this.composer = composer;
61 }
62 public String getDuration() {
63 return duration;
64 }
65 public void setDuration(String duration) {
66 this.duration = duration;
67 }
68 public String getAlbumPicPath() {
69 return albumPicPath;
70 }
71 public void setAlbumPicPath(String albumPicPath) {
72 this.albumPicPath = albumPicPath;
73 }
74 public String getMusicPath() {
75 return musicPath;
76 }
77 public void setMusicPath(String musicPath) {
78 this.musicPath = musicPath;
79 }
80
81 public int getDownCount() {
82 return downCount;
83 }
84 public void setDownCount(int downCount) {
85 this.downCount = downCount;
86 }
87 public int getFavCount() {
88 return favCount;
89 }
90 public void setFavCount(int favCount) {
91 this.favCount = favCount;
92 }
93 @Override
94 public String toString() {
95 StringBuilder sb=new StringBuilder("音乐信息:\n");
96 sb.append("编号").append(id).append("\n");
97 sb.append("歌名").append(name).append("\n");
98 sb.append("歌手").append(singer).append("\n");
99 sb.append("专辑").append(album).append("\n");
100 sb.append("作词").append(author).append("\n");
101 sb.append("作曲").append(composer).append("\n");
102 sb.append("时长").append(duration).append("\n");
103 sb.append("下载次数").append(downCount).append("\n");
104 sb.append("收藏次数").append(favCount).append("\n");
105 return sb.toString();
106 }
107 }
工具类:
HttpUtils.java
1 package com.zsw.utils;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.List;
6 import org.apache.http.HttpEntity;
7 import org.apache.http.HttpResponse;
8 import org.apache.http.HttpStatus;
9 import org.apache.http.NameValuePair;
10 import org.apache.http.client.HttpClient;
11 import org.apache.http.client.entity.UrlEncodedFormEntity;
12 import org.apache.http.client.methods.HttpGet;
13 import org.apache.http.client.methods.HttpPost;
14 import org.apache.http.client.methods.HttpUriRequest;
15 import org.apache.http.impl.client.DefaultHttpClient;
16 import org.apache.http.params.CoreConnectionPNames;
17
18 public class HttpUtils {
19 public static final int METHOD_GET = 1;
20 public static final int METHOD_POST = 2;
21
22 /**
23 * 连接服务端指定的资源路径获取响应实体对象
24 *
25 * @param uri
26 * @param params
27 * @param method
28 * @return
29 * @throws IOException
30 */
31 public static HttpEntity getEntity(String uri, List<NameValuePair> params,
32 int method) throws IOException {
33 HttpEntity entity = null;
34 // 创建客户端对象
35 HttpClient client = new DefaultHttpClient();
36 client.getParams().setParameter(
37 CoreConnectionPNames.CONNECTION_TIMEOUT, 3000);
38 // 创建请求对象
39 HttpUriRequest request = null;
40 switch (method) {
41 case METHOD_GET:// get请求
42 StringBuilder sb = new StringBuilder(uri);
43 if (params != null && !params.isEmpty()) {
44 sb.append('?');
45 for (NameValuePair pair : params) {
46 sb.append(pair.getName()).append('=')
47 .append(pair.getValue()).append('&');
48 }
49 sb.deleteCharAt(sb.length() - 1);
50 }
51 request = new HttpGet(sb.toString());
52 break;
53 case METHOD_POST:// post请求
54 request = new HttpPost(uri);
55 if (params != null && !params.isEmpty()) {
56 UrlEncodedFormEntity reqEntity = new UrlEncodedFormEntity(
57 params);
58 ((HttpPost) request).setEntity(reqEntity);
59 }
60 break;
61 }
62 // 执行请求获得实体对象
63 HttpResponse response = client.execute(request);
64 if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
65 entity = response.getEntity();
66 }
67 // 返回响应实体
68 return entity;
69 }
70
71 /**
72 * 获取指定实体对象的 长度信息
73 *
74 * @param entity
75 * @return
76 */
77 public static long getLength(HttpEntity entity) {
78 if (entity != null)
79 return entity.getContentLength();
80 return 0;
81 }
82
83 /**
84 * 获取实体输入流
85 *
86 * @param entity
87 * @return
88 * @throws IOException
89 * @throws IllegalStateException
90 */
91 public static InputStream getStream(HttpEntity entity)
92 throws IllegalStateException, IOException {
93 if (entity != null) {
94 return entity.getContent();
95 }
96 return null;
97 }
98 }
BitmapUtils.java
1 package com.zsw.util;
2
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6
7 import android.graphics.Bitmap;
8 import android.graphics.Bitmap.CompressFormat;
9 import android.graphics.BitmapFactory;
10 import android.graphics.BitmapFactory.Options;
11
12 public class BitmapUtils {
13 /**
14 * 从指定文件路径加载位图对象
15 * @param path
16 * @return
17 */
18 public static Bitmap loadBitmap(String path){
19 return BitmapFactory.decodeFile(path);
20 }
21 public static Bitmap loadBitmap(byte[] data,int width,int height){
22 Bitmap bm=null;
23 // 创建加载选项对象(加载图片的时候进行的收缩操作,位图处理的时候用到的一个类)
24 Options opts=new Options();
25 // 设置仅加载边界信息,并存储在opts中。加载第一行也即图片的格式,尺寸等信息
26 opts.inJustDecodeBounds=true;
27 // 加载为位图尺寸信息
28 BitmapFactory.decodeByteArray(data, 0, data.length);
29 //设置伸缩比例
30 int x=opts.outWidth/width;
31 int y=opts.outHeight/height;
32 //为了伸缩不超过边界,从x,y中取出较大的数作为伸缩比例
33 opts.inSampleSize=x>y?x:y;
34 //取消仅加载边界信息设置(不设置成false的话永远只加载边界信息,加载不上图片)
35 opts.inJustDecodeBounds=false;
36 //加载位图
37 bm=BitmapFactory.decodeByteArray(data, 0, data.length);
38 return bm;
39 }
40 public static void save(Bitmap bm,File file) throws IOException{
41 if(bm!=null&&file!=null){
42 //如果文件目录不存在,则创建新目录
43 if(!file.getParentFile().exists()){
44 file.getParentFile().mkdirs();
45 }
46 if(!file.exists()){
47 file.createNewFile();
48 }
49 //保存文件
50 bm.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));
51 }
52 }
53 }
GlobalConsts.java
1 package com.zsw.util;
2 /**
3 * 全局变量
4 * @author zsw
5 *
6 */
7 public class GlobalConsts {
8 public static final String BASE_URL = "http://192.168.1.104:8080/musiconline/";
9 // public static final String BASE_URL = "http://172.16.3.182:8080/musiconline/";
10 }
业务类:
MusicXmlParse.java
1 package com.zsw.service;
2
3 import java.io.InputStream;
4 import java.io.InputStreamReader;
5 import java.util.ArrayList;
6 import org.xmlpull.v1.XmlPullParser;
7 import org.xmlpull.v1.XmlPullParserFactory;
8 import com.zsw.entity.Music;
9
10 public class MusicXmlParse {
11 public ArrayList<Music> parse(InputStream is) throws Exception {
12 ArrayList<Music> musics = null;
13 Music m = null;
14 // 创建解析器对象
15 // 利用解析器工厂创建解析器实例
16 XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
17 XmlPullParser parser = factory.newPullParser();
18 // 设置输入源
19 parser.setInput(new InputStreamReader(is));
20 // 获取当前事件类型
21 int eventType = parser.getEventType();
22 // 当事件类型不等于END_DOCUNEMNT时 循环解析
23 while (eventType != XmlPullParser.END_DOCUMENT) {
24 switch (eventType) {
25 case XmlPullParser.START_DOCUMENT:
26 musics = new ArrayList<Music>();
27 break;
28 case XmlPullParser.START_TAG:
29 // 获取标签名
30 String tagName = parser.getName();
31 if ("music".equals(tagName)) {
32 m = new Music();
33 m.setId(Integer.parseInt(parser.getAttributeValue(null,
34 "id")));
35 } else if ("name".equals(tagName)) {
36 m.setName(parser.nextText());
37 } else if ("singer".equals(tagName)) {
38 m.setSinger(parser.nextText());
39 } else if ("album".equals(tagName)) {
40 m.setAlbum(parser.nextText());
41 } else if ("author".equals(tagName)) {
42 m.setAuthor(parser.nextText());
43 } else if ("composer".equals(tagName)) {
44 m.setComposer(parser.nextText());
45 } else if ("albumpic".equals(tagName)) {
46 m.setAlbumPicPath(parser.nextText());
47 } else if ("musicpath".equals(tagName)) {
48 m.setMusicPath(parser.nextText());
49 } else if ("time".equals(tagName)) {
50 m.setDuration(parser.nextText());
51 } else if ("downCount".equals(tagName)) {
52 m.setDownCount(Integer.parseInt(parser.nextText()));
53 } else if ("favCount".equals(tagName)) {
54 m.setFavCount(Integer.parseInt(parser.nextText()));
55 }
56 break;
57 case XmlPullParser.END_TAG:
58 if("music".equals(parser.getName())){
59 musics.add(m);
60 }
61 break;
62 }
63 // 驱动到下一 事件
64 eventType=parser.next();
65 }
66 // System.out.println("-=-=-=-=-="+musics);
67 return musics;
68 }
69
70 }
MusicXmlparseService.java
1 package com.zsw.service;
2
3 import java.io.InputStream;
4 import java.util.ArrayList;
5 import org.apache.http.HttpEntity;
6 import com.zsw.MainActivity;
7 import com.zsw.entity.Music;
8 import com.zsw.util.HttpUtils;
9 import android.os.AsyncTask;
10 import android.widget.Toast;
11
12 public class MusicXmlParseService
13 extends AsyncTask<String, String, ArrayList<Music>>{
14 private MainActivity context;
15 public MusicXmlParseService(MainActivity context){
16 this.context=context;
17 }
18 /**
19 * 执行异步操作并返回music集合
20 */
21 @Override
22 protected ArrayList<Music> doInBackground(String... params) {
23 ArrayList<Music>musics=null;
24 try {
25 publishProgress("开始联网解析xml");
26 //获取实体对象
27 HttpEntity entity=HttpUtils.getEntity(params[0], null, HttpUtils.METHOD_GET);
28 InputStream is=HttpUtils.getStream(entity);
29 System.out.println("=-=-+++"+entity);
30 //解析xml数据流并放入到music集合中
31 musics=new MusicXmlParse().parse(is);
32 System.out.println("==========+"+musics.toString());
33 } catch (Exception e) {
34 e.printStackTrace();
35 }
36 return musics;
37 }
38 /**
39 * 当异步任务执行完成时
40 */
41 @Override
42 protected void onPostExecute(ArrayList<Music> result) {
43 context.updateListView(result);
44 publishProgress("解析结束");
45 }
46
47 @Override
48 protected void onProgressUpdate(String... values) {
49 Toast.makeText(context, values[0], Toast.LENGTH_LONG).show();
50 }
51
52
53 }
适配器类:
MusicAdapter.java
1 package com.zsw.adpter;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.lang.ref.SoftReference;
6 import java.util.ArrayList;
7 import java.util.HashMap;
8 import org.apache.http.HttpEntity;
9 import org.apache.http.util.EntityUtils;
10 import com.zsw.R;
11 import com.zsw.entity.Music;
12 import com.zsw.util.BitmapUtils;
13 import com.zsw.util.GlobalConsts;
14 import com.zsw.util.HttpUtils;
15 import android.content.Context;
16 import android.graphics.Bitmap;
17 import android.os.Handler;
18 import android.os.Message;
19 import android.view.LayoutInflater;
20 import android.view.View;
21 import android.view.ViewGroup;
22 import android.widget.BaseAdapter;
23 import android.widget.ImageView;
24 import android.widget.ListView;
25 import android.widget.TextView;
26
27 public class MusicAdapter extends BaseAdapter {
28 ArrayList<Music> musics;
29 // 显示listview上的数据
30 private LayoutInflater inflater;
31 private Context context;
32
33 public void setMusics(ArrayList<Music> musics) {
34 if (musics != null) {
35 this.musics = musics;
36 } else {
37 this.musics = new ArrayList<Music>();
38 }
39 }
40 //缓存 采用软引用放位图对象
41 HashMap<String, SoftReference<Bitmap>>caches;
42 // 任务集合
43 private ArrayList<ImageLoadTask> tasks;
44 // 开启工作线程
45 private Thread workThread;
46 // 利用handler更新界面
47 private Handler handler;
48 // 判断是否轮询
49 private Boolean isLoop;
50
51 public MusicAdapter(Context context, ArrayList<Music> musics,
52 final ListView lvMusics) {
53 setMusics(musics);
54 this.context = context;
55 this.inflater = LayoutInflater.from(context);
56 this.tasks = new ArrayList<MusicAdapter.ImageLoadTask>();
57 this.caches=new HashMap<String, SoftReference<Bitmap>>();
58 this.handler = new Handler() {
59 @Override
60 public void handleMessage(Message msg) {
61 // 获取加载完成的图片
62 ImageLoadTask task = (ImageLoadTask) msg.obj;
63 // 根据设置了tag的图片从listview中找到imageView
64 ImageView iv = (ImageView) lvMusics.findViewWithTag(task.path);
65 // 更新图片框
66 if (iv != null) {
67 if (task.bitmap != null) {
68 // 如果位图任务里的位图不为空,则加载到imageview中
69 iv.setImageBitmap(task.bitmap);
70 } else {
71 // 若为空则设置默认图片
72 iv.setImageResource(R.drawable.ic_launcher);
73 }
74 }
75
76 }
77 };
78 isLoop = true;
79 workThread = new Thread() {
80 @Override
81 public void run() {
82 // 开启死循环进行轮询
83 while (isLoop) {
84 // 轮询任务集合...如果任务集合不为空
85 while (!tasks.isEmpty()) {
86 // 获取第一条任务
87 ImageLoadTask task = tasks.remove(0);
88 try {
89 // 从服务器端获取实对象
90 HttpEntity entity = HttpUtils.getEntity(
91 GlobalConsts.BASE_URL + task.path, null,
92 HttpUtils.METHOD_GET);
93 // 将实体对象转化成字节数组
94 byte[] data = EntityUtils.toByteArray(entity);
95 System.out.println("data="+data);
96 // 利用位图工厂类将字节数组转化成位图
97 task.bitmap = BitmapUtils.loadBitmap(data, 48, 48);
98 // task.bitmap=BitmapFactory.decodeByteArray(
99 // data, 0, data.length);
100 //添加缓存
101 caches.put(task.path, new SoftReference<Bitmap>(task.bitmap));
102 //将缓存图片保存到指定文件中
103 File file=getFile(task.path);
104 BitmapUtils.save(task.bitmap, file);
105 // 利用handle转发消息到主线程
106 Message.obtain(handler, 0, task).sendToTarget();
107 } catch (IOException e) {
108 e.printStackTrace();
109 }
110 }
111 // 如果任务集合里没有任务,则线程等待
112 synchronized (this) {
113 try {
114 this.wait();
115 } catch (InterruptedException e) {
116 e.printStackTrace();
117 }
118 }
119 }
120 }
121 };
122 this.workThread.start();
123 }
124
125 /**
126 * 替换适配器中的数据集,并刷新listview
127 *
128 * @param musics
129 */
130 public void changeData(ArrayList<Music> musics) {
131 this.setMusics(musics);
132 this.notifyDataSetChanged();
133 }
134 private File getFile(String path){
135 return new File(context.getExternalCacheDir(), path);
136 }
137 @Override
138 public int getCount() {
139 return musics.size();
140 }
141
142 @Override
143 public Music getItem(int position) {
144 return musics.get(position);
145 }
146
147 @Override
148 public long getItemId(int position) {
149 return getItem(position).getId();
150 }
151
152 @Override
153 public View getView(int position, View convertView, ViewGroup parent) {
154 // 加载或复用item界面
155 ViewHolder holder = null;
156 if (convertView == null) {
157 convertView = inflater.inflate(R.layout.item_music, null);
158 holder = new ViewHolder();
159 holder.ivAlbum = (ImageView) convertView.findViewById(R.id.ivAlbum);
160 holder.tvName = (TextView) convertView.findViewById(R.id.tvName);
161 holder.tvDuration = (TextView) convertView
162 .findViewById(R.id.tvDuration);
163 holder.tvAlbum = (TextView) convertView.findViewById(R.id.tvAlbum);
164 holder.tvSinger = (TextView) convertView
165 .findViewById(R.id.tvArtist);
166 convertView.setTag(holder);
167 } else {
168 holder = (ViewHolder) convertView.getTag();
169 }
170 // 获取指定位置的数据
171 Music m = getItem(position);
172 // 将数据写入到item界面
173 holder.tvName.setText(m.getName());
174 holder.tvDuration.setText(m.getDuration());
175 holder.tvSinger.setText(m.getSinger());
176 holder.tvAlbum.setText(m.getAlbum());
177 // 加载专辑图片
178 // 首先加载专辑图片的路径
179 String path = m.getAlbumPicPath();
180 System.out.println("path=" + path);
181 //首先,在加载图片之前判断缓存中是否存在该图片路径
182 //若存在,下次加载时从缓存中取图片
183 if(caches.containsKey(path)){
184 //获取缓存图片
185 Bitmap bm=caches.get(path).get();
186 //若缓存图片不为空,则更新iamgeview,然后返回convertview
187 if(bm!=null){
188 holder.ivAlbum.setImageBitmap(bm);
189 return convertView;
190 }
191 }
192 //其次,再判断是否存在文件缓存
193 Bitmap bm=BitmapUtils.loadBitmap(getFile(path).getAbsolutePath());
194 if(bm!=null){
195 holder.ivAlbum.setImageBitmap(bm);
196 return convertView;
197 }
198 // 设置图片的tag标记
199 holder.ivAlbum.setTag(path);
200 // 创建图片加载任务
201 // holder.ivAlbum.setImageResource(R.drawable.ic_launcher);
202 ImageLoadTask task = new ImageLoadTask();
203 task.path = path;
204 // 如果任务集合中不存在该任务,则添加到任务集合中
205 if (!tasks.contains(task)) {
206 tasks.add(task);
207 // 添加任务后则消息队列(即任务集合)不为空,其通知线程启动
208 synchronized (workThread) {
209 workThread.notify();
210 }
211 }
212 // 返回item界面
213 return convertView;
214 }
215
216 class ViewHolder {
217 private ImageView ivAlbum;
218 private TextView tvName;
219 private TextView tvDuration;
220 private TextView tvSinger;
221 private TextView tvAlbum;
222 }
223
224 // 加载图片任务集合
225 class ImageLoadTask {
226 private Bitmap bitmap;
227 private String path;
228
229 /**
230 * 判断加载图片任务是否相同(以路径作为判断条件)
231 */
232 @Override
233 public boolean equals(Object o) {
234 ImageLoadTask task = (ImageLoadTask) o;
235 return this.path.equals(task.path);
236 }
237 }
238
239 }
主界面:
MainActivity.java
1 package com.zsw;
2
3 import java.util.ArrayList;
4
5 import com.zsw.adpter.MusicAdapter;
6 import com.zsw.entity.Music;
7 import com.zsw.service.MusicXmlParseService;
8 import com.zsw.util.GlobalConsts;
9
10 import android.os.Bundle;
11 import android.app.Activity;
12 import android.view.Menu;
13 import android.widget.ListView;
14
15 public class MainActivity extends Activity {
16 private ListView lvMusics;
17 private MusicAdapter adapter;
18 @Override
19 protected void onCreate(Bundle savedInstanceState) {
20 super.onCreate(savedInstanceState);
21 setContentView(R.layout.activity_main);
22 //c初始化listview,使用空集合
23 initView();
24 //启动异步任务,加载和解析xml
25 new MusicXmlParseService(this).execute(GlobalConsts.BASE_URL+"sounds.xml");
26 }
27 private void initView(){
28 //获取listview的引用
29 lvMusics=(ListView)findViewById(R.id.lvMusics);
30 //创建适配器
31 adapter=new MusicAdapter(this,null,lvMusics);
32 //设置设配器
33 lvMusics.setAdapter(adapter);
34 }
35 /**
36 * 刷新listView,并获取加载的数据
37 */
38 public void updateListView(ArrayList<Music>musics){
39 adapter.changeData(musics);
40 }
41 @Override
42 public boolean onCreateOptionsMenu(Menu menu) {
43 getMenuInflater().inflate(R.menu.main, menu);
44 return true;
45 }
46
47 }
|
|
|