由于最近在做的一个游戏项目有一个排行榜,
排行榜是一个列表,界面大致如下:
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排名 人物名称 头像图片 分数
排行 人物名称 头像图片 分数
列表中有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,数据在网络中传递,速度和流量是一个重要方面,所以服务端应该先
将数据进行压缩再发送到客户端,客户端接收后再进行解压缩,这样可以减少流量开支
除此之外,通过网络传输数据,最好是对数据进行加密,保证安全性和完整性