移动端网络框架--基于valley实现
来源:互联网 发布:中航工业301所待遇知乎 编辑:程序博客网 时间:2024/06/05 13:22
说明:
在开发Android项目时自己写的一个网络连接框架,基于valley框架的使用建立了一个支持多线程的、异步下载的、多数据格式的网络框架
valley简介:
在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,今年的Google I/O 2013上,Volley发布了。Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。
这是Volley名称的由来: a burst or emission of many things or a large amount at once
1.1. Volley引入的背景
在以前,我们可能面临如下很多麻烦的问题。
比如以前从网上下载图片的步骤可能是这样的流程:
在ListAdapter#getView()里开始图像的读取。
通过AsyncTask等机制使用HttpURLConnection从服务器去的图片资源
在AsyncTask#onPostExecute()里设置相应ImageView的属性。
而在Volley下,只需要一个函数即可,详细见后面的例子。
再比如,屏幕旋转的时候,有时候会导致再次从网络取得数据。为了避免这种不必要的网络访问,我们可能需要自己写很多针对各种情况的处理,比如cache什么的。
再有,比如ListView的时候,我们滚动过快,可能导致有些网络请求返回的时候,早已经滚过了当时的位置,根本没必要显示在list里了,虽然我们可以通过ViewHolder来保持url等来实现防止两次取得,但是那些已经没有必须要的数据,还是会浪费系统的各种资源。
1.2. Volley提供的功能
简单来说,它提供了如下的便利功能:
JSON,图像等的异步下载;
网络请求的排序(scheduling)
网络请求的优先级处理
缓存
多级别取消请求
和Activity和生命周期的联动(Activity结束时同时取消所有网络请求)
关键类介绍:
ConnectBase:
/**
* 安卓与后台连接类,本类中所有方法必须在多线程中执行
* (静态方法将影响封装与后期拓展,此处用普通方法)
*该类中包括了cookie的检查、超时连接的处理、网络检测等方法
*/`
@SuppressWarnings("deprecation")public class ConnectBase { private static final String DECODE_UNICODE = "\\\\u([0-9a-zA-Z]{4})"; private static String JSP_COOKIE = null;// 维持会话的cookie private Context context = null; /** * 使用旧cookie */ public ConnectBase(Context context) { this.context = context; JSP_COOKIE = ConnectTool.getCookie(context); } /** * 刷新cookie */ public ConnectBase(Context context, boolean refreash) { this.context = context; } /** * 以协定方式执行post连接后台,发送head与body并接收后台返回。 * * @param url 连接地址 * @param list 主体信息 * @return 后台返回的字符串信息 */ public String executePost(String url, ConnectList list) { if (!isNetworkEnable(context)) {// 网络不可用,直接返回null return null; } if (!url.startsWith("http")) { url = ServerURL.getIP() + url; } final int COONECT_TIME_OUT = 10000;// 设定连接超时10秒 final int READ_TIME_OUT = 10000;// 设定读取超时为10秒,仅仅用于登录 BufferedReader in = null; try { // 定义HttpClient,实例化Post方法 HttpClient client = new DefaultHttpClient(); HttpPost request = new HttpPost(url); // 设定超时,超时将以异常形式提示 client.getParams().setParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, COONECT_TIME_OUT);// 请求超时 client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, READ_TIME_OUT);// 读取超时 // 添加cookie信息 if (JSP_COOKIE != null && !JSP_COOKIE.equals("")) {// 若为null,则不添加,等待服务器返回 request.addHeader("Cookie", JSP_COOKIE); Log.e("EEEE", "EEEE cookie:" + JSP_COOKIE); } // 添加body信息 List<NameValuePair> body = list.getList(); UrlEncodedFormEntity formEntiry = new UrlEncodedFormEntity(body); request.setEntity(formEntiry); // 执行请求 HttpResponse response = client.execute(request); // cookie处理,维护会话 Header head = response.getFirstHeader("set-Cookie"); if (head != null) { JSP_COOKIE = head.getValue(); ConnectTool.saveCookie(context, JSP_COOKIE); Log.e("EEEE", "EEEE new-cookie:" + JSP_COOKIE); } // 接收返回 in = new BufferedReader(new InputStreamReader(response.getEntity() .getContent())); StringBuffer sb = new StringBuffer(""); String line = ""; while ((line = in.readLine()) != null) { sb.append(line + "\n"); } in.close(); String result = sb.toString(); int code = response.getStatusLine().getStatusCode(); Log.e("EEEE", "EEEE url:" + url); Log.e("EEEE", "EEEE code:" + code); Log.e("EEEE", "EEEE size:" + list.getList().size()); Log.e("EEEE", "EEEE result:" + result); if (code == 500 || code == 404) { return null; } result = URLDecoder.decode(result, "UTF-8"); result = URLDecoder.decode(result, "UTF-8"); result = result.trim(); if (result.equals("")) return null;// 后台返回""则返回null。 return result; } catch (Exception e) {// 很有可能是请求超时了 // e.printStackTrace(); if (e != null) Log.e("EEEE", "EEEE " + e.getMessage()); return null; } finally {// 这个在finally中很有必要 if (in != null) { try { in.close(); } catch (Exception e) { // e.printStackTrace(); } } } } // 解决\\u问题 public static String decode(String s) { Pattern reUnicode = Pattern.compile(DECODE_UNICODE); Matcher m = reUnicode.matcher(s); StringBuffer sb = new StringBuffer(s.length()); while (m.find()) { m.appendReplacement(sb, Character.toString((char) Integer.parseInt(m.group(1), 16))); } m.appendTail(sb); return sb.toString(); } // 解决\\u问题,简便方法 public static String decodeEasy(String s) { try {// json自带解析 return new JSONObject(s).toString(); // JSONObject.parse(s).toString();//更好,尤其是带引号的 } catch (JSONException e) { return s; } } // 检测网络是否可用 private static boolean isNetworkEnable(Context context) { ConnectivityManager cm = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo ni = cm.getActiveNetworkInfo(); return ni != null && ni.isConnectedOrConnecting(); }}
ConnectEasy
/**
*基于valley框架,实现前端与后台的连接
*前端可以向后台发出post请求,包括单请求和多请求的处理
*/
public class ConnectEasy extends AsyncTask<Void, Void, String> { private Context context; private String url; private ConnectList list; private ConnectDialog dialog; private ConnectListener listener; private static RequestQueue queue; public ConnectEasy(Context context, String url, ConnectListener listener) { this.context = context; this.url = url; this.listener = listener; list = new ConnectList(); dialog = new ConnectDialog(); if (listener != null) { list = listener.setParam(list); dialog = listener.showDialog(dialog); } if (list == null)// 防止listener返回错了 list = new ConnectList(); if (dialog == null) dialog = listener.showDialog(dialog); } @Override protected void onPreExecute() { if (dialog != null) { dialog.show(); } } @Override protected String doInBackground(Void... params) { ConnectBase con = new ConnectBase(context); return con.executePost(url, list); } @Override protected void onPostExecute(String result) { if (listener != null) listener.onResponse(result); if (dialog != null) dialog.hide(); } // ///////////////////////基于回调的方法/////////////////////// /** * 向指定网址发起post请求 * * @param context context * @param url 网址 * @param listener 监听回调 */ public static void POST(Context context, String url, ConnectListener listener) { //刷新URL url = ServerURL.getSignedURL(url); VOLLEY(context, url, listener);// //发起请求// ConnectEasy connect = new ConnectEasy(context, url, listener);// connect.execute(); } /** * 向指定网址发起post请求,强制使用原来的方法 * * @param context context * @param url 网址 * @param listener 监听回调 */ public static void POSTLOGIN(Context context, String url, ConnectListener listener) { //刷新URL url = ServerURL.getSignedURL(url); //发起请求 ConnectEasy connect = new ConnectEasy(context, url, listener); connect.execute(); } // ///////////////////////基于Volley的方法/////////////////////// public static void VOLLEY(final Context context, final String url, final ConnectListener listener) { if (queue == null) { queue = Volley.newRequestQueue(context); } final ConnectDialog dialog; final ConnectList list; if (listener == null) { dialog = null; list = new ConnectList(); } else { dialog = listener.showDialog(new ConnectDialog()); if (dialog != null) dialog.show(); ConnectList list_temp = listener.setParam(new ConnectList()); if (list_temp == null) list = new ConnectList(); else list = list_temp; } Request request = null; if (!list.hasFile()) { StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() { @Override public void onResponse(String response) { try {//编码处理 if (ServerURL.isTest()) { Log.e("EEE-VOLLEY-url", url + ""); if (list != null && list.getMap() != null) Log.e("EEE-VOLLEY-params", list.getMap().size() + ""); if (!TextUtils.isEmpty(response)) Log.e("EEE-VOLLEY-response1", response); } if (!TextUtils.isEmpty(response)) { response = new String(response.getBytes("ISO-8859-1"), "utf-8"); if (ServerURL.isTest()) { Log.e("EEE-VOLLEY-response2", response + ""); response = ConnectBase.decode(response);//便于查看(此方法更加稳定) Log.e("EEE-VOLLEY-response3", response + ""); } } } catch (Exception e) { if (ServerURL.isTest()) { Log.e("EEE-VOLLEY-error", "response other error"); if (e != null) Log.e("EEE-VOLLEY-error", e.getMessage() + ""); } } if (listener != null) listener.onResponse(response); if (dialog != null) dialog.hide(); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { if (ServerURL.isTest()) { Log.e("EEE-VOLLEY-error", "response error"); if (error != null) Log.e("EEE-VOLLEY-error", error.getMessage() + ""); } if (listener != null) listener.onResponse(""); if (dialog != null) dialog.hide(); } }) { @Override protected Map<String, String> getParams() { if (listener != null) { if (list != null) { return list.getMap(); } } return new HashMap<String, String>(); } @Override public Map<String, String> getHeaders() { Map<String, String> map = new HashMap<>(); String cookie = ConnectTool.getCookie(context); map.put("Cookie", cookie == null ? "" : cookie); if (ServerURL.isTest()) Log.e("EEE-VOLLEY-cookie", "" + cookie); return map; } }; request = stringRequest; } else { MultipartRequest multipartRequest = new MultipartRequest(url, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError volleyError) { if (listener != null) listener.onResponse(""); if (dialog != null) dialog.hide(); } }, new Response.Listener<String>() { @Override public void onResponse(String response) { try {//编码处理 if (ServerURL.isTest()) { Log.e("EEE-VOLLEY-url", url + ""); if (list != null && list.getMap() != null) Log.e("EEE-VOLLEY-params", list.getMap().size() + ""); if (!TextUtils.isEmpty(response)) Log.e("EEE-VOLLEY-response1", response + ""); } if (!TextUtils.isEmpty(response)) { response = new String(response.getBytes("ISO-8859-1"), "utf-8"); response = JSONObject.parse(response).toString(); if (ServerURL.isTest()) Log.e("EEE-VOLLEY-response2", response + ""); } } catch (Exception e) { if (ServerURL.isTest()) { Log.e("EEE-VOLLEY-error", "response other error"); if (e != null) Log.e("EEE-VOLLEY-error", e.getMessage() + ""); } } if (listener != null) listener.onResponse(response); if (dialog != null) dialog.hide(); } }, list.getListKey(), list.getListFile(), list.getMap()); request = multipartRequest; } request.setRetryPolicy( new DefaultRetryPolicy( 500000,//默认超时时间,应设置一个稍微大点儿的,例如本处的500000 DefaultRetryPolicy.DEFAULT_MAX_RETRIES,//默认最大尝试次数 DefaultRetryPolicy.DEFAULT_BACKOFF_MULT ) ); queue.add(request); }
ConnectList
/**
*处理服务端与前端之间多数据的传输,
*将多个数据或者文件以list的形式进行处理
*
*/
public class ConnectList { private List<NameValuePair> list = null; private Map<String, String> map = null; private List<String> list_key = null; private List<File> list_file = null; private boolean has_file = false; public ConnectList() { list = new ArrayList<>(); map = new HashMap<>(); list_key = new ArrayList<>(); list_file = new ArrayList<>(); } /** * 添加一个文件,服务器收到File类 */ public ConnectList put(String key, File file) { list_key.add(key); list_file.add(file); has_file = true; return this; } /** * 添加一个文件,服务器收到Set-File类 */ public ConnectList put(String key, List<File> files) { for (File file : files) put(key, file); return this; } /** * 添加一个键值对,是否强制encode,影响list,不影响map */ public ConnectList put(String key, String value, boolean should_encode) { map.put(key, value == null ? "" : value);//map直接put。 try { if (should_encode) value = URLEncoder.encode(value == null ? "" : value, "UTF-8"); } catch (Exception e) { return this; } NameValuePair item = new BasicNameValuePair(key, value); list.add(item); return this; } /** * 添加一个文件,服务器收到File类 * 防止null冲突 */ public ConnectList putFile(String key, File file) { return put(key, file); } /** * 添加一个键值对,String值 */ public ConnectList put(String key, String value) { return put(key, value, true); } /** * 添加一个键值对,Long值 */ public ConnectList put(String key, long value) { return put(key, value + ""); } public List<NameValuePair> getList() { return list; } public Map<String, String> getMap() { return map; } public List<String> getListKey() { return list_key; } public List<File> getListFile() { return list_file; } /** * 是否包含文件 */ public boolean hasFile() { return has_file; } // ///////////////////静态方法//////////////////////////////// /** * 直接获取网络数据类 * * @param key_value 键1,值1,键2,值2,……键n,值n * @return 网络数据类 */ public static ConnectList getSimpleList(String... key_value) { if (key_value.length % 2 == 1) return null; ConnectList list = new ConnectList(); for (int i = 0; i < key_value.length; i += 2) { list.put(key_value[i], key_value[i + 1]); } return list; }}
ConnectFile
/**
* 文件处理,使之可以通过json传输
*
*
*/
public class ConnectFile { /** * TODO:将以Base64方式编码的字符串解码为byte数组 * * @param encodeString * 待解码的字符串 * @return 解码后的byte数组,解码失败返回null */ public static byte[] decodeFile(String encodeString) { byte[] filebyte = null; try { filebyte = Base64Coder.decode(encodeString); } catch (Exception e) { filebyte = null; } return filebyte; } /** * TODO:将文件以Base64方式编码为字符串 * * @param filepath * 文件的绝对路径 * @return 编码后的字符串,编码失败返回null * */ public static String encodeFile(String filepath) { String result = ""; try { FileInputStream fis = new FileInputStream(filepath); byte[] filebyte = new byte[fis.available()]; fis.read(filebyte); fis.close(); result = new String(Base64Coder.encode(filebyte)); } catch (IOException e) { result = null; } return result; }}
ConnectListener
/**
*网络请求时以监听的方式来控制网络参数、网络执行情况等
*在移动端向服务端发起网络请求时需要注册监听器
*
*/
public interface ConnectListener { /** * 网络请求的参数 * * @param list 默认的参数列表(空表,直接put然后返回即可) * @return 添加参数后的参数列表 */ public ConnectList setParam(ConnectList list); /** * 是否显示忙碌对话框 * * @param dialog 默认的对话框(不显示,调用config将显示并在onResponse结束后自动隐藏) * @return 配置后的对话框 */ public ConnectDialog showDialog(ConnectDialog dialog); /** * 网络执行完毕后自动回调 * * @param response 服务器返回的数据,错误将返回null */ public void onResponse(String response);}
ConnectSign
/**
*网络签名,在网络传输时用于校对Android端与服务端的信息比对
*这里主要针对于两端服务时间的比对,用于矫正时间差
*
*/
public class ConnectSign { public static String KEY_TIME = "timestamp", KEY_SIGN = "signature"; private static long TIME_SPACE = 0; private static String SECRET_KEY = "qianxun"; /** * 获取签名的MD5 * * @param time 时间戳 * @return 时间戳+秘钥,取两次MD5 */ public static String getSignMD5(long time) { String all = time + SECRET_KEY; String result = getMD5(all); result = getMD5(result); return result; } /** * 处理时间差,安卓专用 * * @param time 服务器时间 */ public static void dealTimeSpace(long time) { TIME_SPACE = time - System.currentTimeMillis(); } public static void dealTimeSpace(String time) { if (TextUtils.isEmpty(time)) return; if(time.length()!=13) return; try { long time_long = Long.parseLong(time); dealTimeSpace(time_long); } catch (Exception e) { } } /** * 获取签名的URL后缀,安卓专用 * * @return 以"?"开头的URL后缀 */ public static String getSignURL() { String result = "?"; long time = getTimeSnap(); result += KEY_TIME + "=" + time; result += "&"; result += KEY_SIGN + "=" + getSignMD5(time); return result; } /** * 获取时间戳 * * @return 当前的服务器时间 */ private static long getTimeSnap() { return System.currentTimeMillis() + TIME_SPACE; } /** * 获取文本数据的MD5编码(注:安卓没有直接就按MD5的包) * * @param text 要编码的文本数据 * @return 数据的32位MD5字符串值 */ private static String getMD5(String text) {// 返回32位MD5数组 String result = ""; MessageDigest message = null; byte[] bytes = null; try { message = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { } bytes = message.digest(text.getBytes()); result = new String(toHexString(bytes)); return result; } /** * 将byte数组转换为Hex字符串,这其实是HttpClient里面的codec.jar中Hex类中的encodeHex方法 * (这里没有必要导入整个包,所以只拿出来这个方法) * * @param md 要转换的byte数组 * @return 转换后的字符串 */ private static String toHexString(byte[] md) { char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; int j = md.length; char str[] = new char[j * 2]; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[2 * i] = hexDigits[byte0 >>> 4 & 0xf]; str[i * 2 + 1] = hexDigits[byte0 & 0xf]; } return new String(str); }}
以上就是我所实现的一个Android端网络框架,上述是框架中主要的代码,供大家参考。
- 移动端网络框架--基于valley实现
- Android 移动网络框架
- 基于Scrapy框架下的Python网络爬虫的实现
- 基于A-Frame 框架实现的移动端VR视频播放(可感知手机重力感应)
- 基于Html5的移动端APP开发框架
- 基于阿里移动端积木框架Tangram自定义首页卡片
- 移动端网络调试 基于express的JsServerDemo
- 移动架构33_网络访问框架与数据库框架实现断点下载
- 移动网络游戏实现流程 -- 基于Cocos2d-x引擎和pomelo服务器框架
- ifanr2基于框架实现
- 基于移动平台的多媒体框架-系列
- 基于Android平台移动社交网络
- 打造基于人工智能的移动网络
- 基于Netty的RPC简单框架实现(四):Netty实现网络传输
- 基于Vue2.js全家桶的移动端AppDEMO实现
- 基于vue2全家桶实现的,仿移动端QQ
- 基于Vue2.js全家桶的移动端AppDEMO实现
- 基于vue2全家桶实现的,仿移动端QQ
- HTTP协议
- Python网络爬虫阶段总结
- 进程间通信--消息队列
- hibernate缓存机制详细分析(一级、二级、查询缓存,非常清晰明白)
- 分布式缓存技术redis学习系列(一)——redis简介以及linux上的安装
- 移动端网络框架--基于valley实现
- Popwindow在7.0系统上显示全屏
- 分布式缓存技术redis学习系列(二)——详细讲解redis数据结构(内存模型)以及常用命令
- 微信小程序:input标签的使用
- Educational Codeforces Round 21
- 多线程的基本用法和问题
- andriod service
- 排序算法之快速排序的C++实现
- js地址栏获取参数的方法,解决中文乱码问题,能支持中文参数