移动端网络框架--基于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端网络框架,上述是框架中主要的代码,供大家参考。

阅读全文
0 0
原创粉丝点击