移动端网络框架--基于valley实现

说明:

在开发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 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 list = null;
    private Map map = null;
    private List list_key = null;
    private List 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 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 getList() {
        return list;
    }

    public Map getMap() {
        return map;
    }

    public List getListKey() {
        return list_key;
    }

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

你可能感兴趣的:(android,valley框架,网络框架,移动)