由于最近在做的一个游戏项目有一个排行榜,
排行榜是一个列表,界面大致如下:
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排行 人物名称 头像图片 分数
列表中有100条数据,列表下面有一个
控件显示游戏玩家自己的排名信息
需求如下:
每次进入排行榜界面,则将游戏玩家的
人物名称和分数提交到服务端,服务端
接收请求后对数据库中的数据进行排序,
取得前100名的数据,还有一条游戏玩家
的数据,总共101条数据,由于客户端
还需要下载头像图片,所以返回的数据
还有头像图片的下载地址,服务端将所有
的数据包装成一个Json数据返回给客户端
大致格式如下:
{"rank": [ {"person":"\u66f9\u64cd","index":1,"score":35852}, {"person":"\u66f9\u64cd","index":2,"score":32563}, {"person":"\u5b59\u6743","index":3,"score":10000}, {"person":"\u5218\u5907","index":4,"score":9638}, {"person":"\u5218\u5907","index":5,"score":8888}, {"person":"\u5b59\u6743","index":6,"score":8886}, {"person":"\u5218\u5907","index":7,"score":7865}, {"person":"\u5218\u5907","index":8,"score":6950}, {"person":"\u5218\u5907","index":9,"score":6548}, {"person":"\u5218\u5907","index":10,"score":6540}, {"person":"\u66f9\u64cd","index":11,"score":5288} ], "base":"(服务端地址)", "head": [ {"person":"\u66f9\u64cd","filename":"\/caocao\/20130718185726036.png", "size":12343}, {"person":"\u5b59\u6743","filename":"\/sunqun\/20130718185726046.png", "size":12343}, {"person":"\u5218\u5907","filename":"\/liubei\/20130718185726056.png", "size":12343} ] }
rank的每个对象包括:人物名称,排名,分数
head的每个对象包括:人物名称,头像图片名称
base为服务端地址
大致流程如下:
1.进入排行榜界面,将游戏玩家的数据发送到服务端
2.取得服务端返回的Json数据,解析出rank数组,head数组和base字符串
3.使用头像图片路径下载头像图片到本地
4.新建一个ResultMessage类,属性包括:排名,人物名称,本地头像图片地址,分数
5.在解析rank数组时实例化ResultMessage,添加到List中并返回出去
主要有三个类:LoadImage.java,ResultMessage.java,Upload.java
LoadImage.java用于下载头像图片,由于每次进入排行榜界面都会向
服务端发送请求,每次都会返回头像图片的下载地址,所以需要
做下判断本地是否已经有此图片存在,还有就是判断图片大小是否
正确,因为会有这样一种情况,在下载图片时突然网络断开,这时
头像图片没有下载完整,下次进入排行榜界面的时候又向服务端
发送请求,又下载头像图片,此时程序判断出本地已经有此图片
存在,所以不会再下载图片,但是图片不完整,无法正常使用,
所以除了判断本地是否有图片之外,还需要判断图片的大小是不是
跟服务端发过来的大小一样,只有图片存在,并且大小一样的时候
才不下载图片
具体代码如下:
package com.joye3g.http; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import org.apache.http.HttpStatus; public class LoadImage { private static final int SUCCES = 1;//下载成功 private static final int ERRO = -1;//下载失败 private static final int EXIST = 0;//文件已存在 private static int result = EXIST;//返回的下载结果 private String localPath;//本地路径 public LoadImage(String localPath) { this.localPath = localPath; } /** * @param urlPathPrefix 网址前缀 * @param filename 文件名 * @param size 文件大小 * @return * @throws IOException */ public int download(String urlPathPrefix, String filename, long size) throws IOException{ String filePath = localPath + File.separator + filename.substring(filename.lastIndexOf("/") + 1); //判断filePath路径下有没有此图片存在,大小是否相同,如果不存在,则发送Http请求下载图片,存在则不下载 if(isFileExist(filePath) && isSizeSame(filePath, size)){ return result = EXIST; }else{ //从URL中取得输入流 InputStream is = getHttpInputStream(urlPathPrefix + filename); //创建新文件 File file = createFile(filePath); //下载图片 if(is != null) downLoadImage(file, is); } return result; } /** * 下载图片 * @param file 文件 * @param is 从URL取得的输入流 */ private void downLoadImage(File file, InputStream is){ FileOutputStream fs = null; try { fs = new FileOutputStream(file); byte[] buffer = new byte[4 * 1024]; int len = 0; while((len = is.read(buffer)) != -1){ fs.write(buffer, 0, len); } fs.flush(); result = SUCCES; } catch (Exception e) { result = ERRO; e.printStackTrace(); }finally{ try { if(fs != null){ fs.close(); } if(is != null){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 根据URL取得输入流 * @param urlPath 网络路径 * @return * @throws IOException */ private InputStream getHttpInputStream(String urlPath) throws IOException{ URL url = new URL(urlPath); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); if(conn.getResponseCode() == HttpStatus.SC_OK) { return conn.getInputStream(); } return null; } /** * 判断文件是否已经存在 * @param fileName 文件路径 * @return */ private boolean isFileExist(String fileName){ File file = new File(fileName); return file.exists(); } /** * 在指定路径下创建新文件 * @param fileName 文件路径 * @return * @throws IOException */ private File createFile(String fileName) throws IOException{ File file = new File(fileName); if(!file.createNewFile()){ file.delete(); file.createNewFile(); } return file; } /** * 若文件已存在,判断文件大小是否正确 * @param filePath 图片路径 * @param size 文件大小 * @return */ private boolean isSizeSame(String filePath, long size){ File file = new File(filePath); return file.length() == size; } }
ResultMessage.java具体代码如下:
package com.joye3g.http; public class ResultMessage { private int index; private String name; private int score; private String imagePath; public ResultMessage(int index, String name, int score, String imagePath) { this.index = index; this.name = name; this.score = score; this.imagePath = imagePath; } public String getImagePath() { return imagePath; } public void setImagePath(String imagePath) { this.imagePath = imagePath; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } }
UpLoad.java用于向服务端发送请求并接受返回的数据,
对返回的数据进行包装再返回出去,首先在构造函数中
传入要上传到服务端的人物名称,分数和上下文对象,
上下文对象主要用于取得程序的安装路径和UUID,
使用方法很简单:
UpLoad upload = new UpLoad("曹操", 23456, getApplicationContext());
List<ResultMessage> messages = upload.post("服务端地址");
UpLoad.java具体代码如下:
package com.joye3g.http; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.zip.GZIPInputStream; import org.apache.http.HttpStatus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.telephony.TelephonyManager; /** * @author ZLQ * */ public class Upload { private static final String GZIP = "gzip"; private Context context; private JSONObject jsonData; /** * 构造函数 * @param name * 要上传的人物名称 * @param score * 要上传的分数 * @param context * 上下文对象,用于取得UUID和本地路径 */ public Upload(String name, int score, Context context) { jsonData = new JSONObject(); this.context = context; try { jsonData.put("person", name); jsonData.put("uploaduser", getUUID()); jsonData.put("score", score); } catch (JSONException e) { e.printStackTrace(); } } /** * 发送post请求 * @param url * URL路径 * @return 返回一个List<Message>链表 */ public List<ResultMessage> post(String url) { List<ResultMessage> messages = null; try { messages = sendHttpResponse(url); } catch (Exception e) { e.printStackTrace(); } return messages; } /** * 发送请求,返回结果 * @param url * URL路径 * @return 返回一个List<Message>链表 * @throws JSONException * @throws Exception */ private List<ResultMessage> sendHttpResponse(String url) throws IOException, JSONException { URL uri = new URL(url); HttpURLConnection conn = (HttpURLConnection) uri.openConnection(); conn.setRequestMethod("POST"); // Post方式 conn.setDoOutput(true);// 允许输出 conn.setRequestProperty("connection", "keep-alive"); // 客户端到服务器端的连接持续有效 conn.setRequestProperty("Content-Type", "application/x-javascript; charset=UTF-8"); conn.setRequestProperty("accept-encoding", "gzip,deflate");//设置 gzip的请求头 DataOutputStream outStream = new DataOutputStream(conn.getOutputStream()); outStream.write(jsonData.toString().getBytes());//将数据内容写入到流中 outStream.flush(); outStream.close(); return getReturnMessage(conn);//返回服务端返回的结果 } /** * 取得服务端返回的结果 * @param conn * @return * @throws IOException * @throws JSONException */ private List<ResultMessage> getReturnMessage(HttpURLConnection conn) throws IOException, JSONException { List<ResultMessage> messages = null; String content_encode = conn.getContentEncoding(); String content = null; if (conn.getResponseCode() == HttpStatus.SC_OK) { if (null != content_encode && !"".equals(content_encode) && content_encode.equals(GZIP)) { //对服务端返回的内容进行解压 content = unGzip(conn.getInputStream()); } JSONObject objMain = new JSONObject(content); JSONArray ranks = objMain.getJSONArray("rank");//取得rank数组 JSONArray heads = objMain.getJSONArray("head");//取得head数组 String base = objMain.getString("base");//取得地址前缀 LoadImage load = new LoadImage(getLocalPath()); Map<String, String> map = new HashMap<String, String>(); //取得heads数组中的对象 for (int i = 0; i < heads.length(); i++) { JSONObject head = heads.getJSONObject(i); String person = head.getString("person"); String filename = head.getString("filename"); long size = head.getLong("size"); map.put(person, filename); load.download(base, filename, size);//下载图片 } //取得除去最后一条的所有数据 messages = new ArrayList<ResultMessage>(); for (int i = 0; i < ranks.length() - 1; i++) { JSONObject rankObj = ranks.getJSONObject(i); messages.add(rank(rankObj, map)); } //取得最后一个数据 JSONObject rankObj = ranks.getJSONObject(ranks.length() - 1); messages.add(rank(rankObj, map)); } conn.disconnect(); return messages; } // 解析数据 private ResultMessage rank(JSONObject rank, Map<String, String> map) throws JSONException { int num = rank.getInt("index"); String person = rank.getString("person"); String filename = map.get(person); //拼接本地图片路径 String imagePath = getLocalPath() + File.separator + filename.substring(filename.lastIndexOf("/") + 1); int score = rank.getInt("score"); //实例化一个ResultMessage对象 ResultMessage message = new ResultMessage(num, person, score, imagePath); return message; } /** * 对服务器返回的内容进行解压并返回解压后的内容 * @param is * @return * @throws IOException * @throws UnsupportedEncodingException */ private static String unGzip(InputStream is) throws IOException, UnsupportedEncodingException { GZIPInputStream in = new GZIPInputStream(is); ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); int len = -1; byte[] buffer = new byte[1024]; while ((len = in.read(buffer)) != -1) { arrayOutputStream.write(buffer, 0, len); } in.close(); arrayOutputStream.close(); is.close(); return new String(arrayOutputStream.toByteArray(), "utf-8"); } /** * 返回UUID * @return */ private String getUUID() { final TelephonyManager tm = (TelephonyManager) context .getSystemService(Context.TELEPHONY_SERVICE); final String tmDevice, tmSerial, androidId; tmDevice = "" + tm.getDeviceId(); tmSerial = "" + tm.getSimSerialNumber(); androidId = "" + android.provider.Settings.Secure.getString( context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); UUID deviceUUID = new UUID(androidId.hashCode(), ((long) tmDevice.hashCode() << 32 | tmSerial.hashCode())); return deviceUUID.toString(); } /** * 返回应用程序的安装路径 * @return */ private String getLocalPath() { return context.getApplicationContext().getFilesDir().getAbsolutePath(); } }
返回的messages中的每个对象的格式为:
排名,人物名称,本地头像图片的路径,分数
小结:
UUID,是手机的唯一标识,服务端通过UUID判断是否是同个用户提交数据
本地路径,程序安装在手机中,用户有可能会将程序移动到其他地方,所以数据,
如图片,数据文件等等文件应该放在程序路径下,这样程序移动时数据会一起
移动,不会出现程序移动后之前的数据找不到
GZIPInputStream,数据在网络中传递,速度和流量是一个重要方面,所以服务端应该先
将数据进行压缩再发送到客户端,客户端接收后再进行解压缩,这样可以减少流量开支
除此之外,通过网络传输数据,最好是对数据进行加密,保证安全性和完整性