最近在做一款播放器,服务器为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 }
android音乐播放器+异步加载+缓存+图片加载+listview列表显示+tomcat服务器+xml解析,布布扣,bubuko.com