本章目的

在上一章中,我们利用Hibernate Tools完成了Android Model层的建立,依赖Hibernate Tools的强大功能,自动生成了Model层。在本章,我们将继续我们的项目,在Android端完成Http一个访问类,并完成整个请求、处理 Response、反序列化Json的工作。那么首先我们来看下Android的Http访问。

Http, POST or GET?

在 开始正式设计Http访问类之前,我们必须先了解一些关于Http协议的必要知识。在Http1.1规范中,一共定义了8种方法,在这里与我们有关的是 Get、post两种方法,对大多数程序员来说,大致的意思我们都明白,但能完整理解两种方法的区别和联系的,就不那么容易了。这里作者不去从协议本身去 探讨二者区别,只从应用层来讲:

GET:向服务器的特定资源发出请求,这种方法要求所有传递的参数只能通过URL的QueryString,由于URL长度大小最大有2KB限制,所以一般只能传递简单的参数。

POST:向服务器发送数据,这种方法可以将参数包含在请求体中,可以用来传输大量数据,如上传文件等。

如果你想了解HTTP协议的更多细节,这篇文章写的很详细,值得参考:http://www.cnblogs.com/skynet/archive/2010/05/18/1738301.html

Android的Http访问类

http访问类类的部分代码参考了开源的 https://github.com/yusuke

首先我们来看下Android中进行httprequest的全过程

1、根据URL生成成java.net.HttpURLConnection对象

2、设置HttpURLConnection允许返回值

3、设置HttpURLConnection的Header数据(Header数据保存在 Hashmap<String,String> requestHeaders中,从Configration静态类取得)

4、根据不同类型的httpMethod设置HttpURLConnection 的 RequestMethod

-----------至此HttpURLConnection生成完毕----------------

5、通过调用HttpURLConnection.getInputStream()方法实现Http连接

6、将服务器发回来的数据生成为Response对象(此对象由我们封装,表示服务器返回的数据)

7、通过HttpURLConnection.getResponseCode()的值判断此次Http请求是否成功

8、如果成功,HttpURLConnection.getInputStream()即为服务器返回的http流

-----------至此Request请求完毕----------

通过上面的流程,我们可以设计我们的http访问类,最核心的是下面几个类

HttpClientP:处理Http请求,包括重要的httprequest()方法以及设置header等方法

Response:HttpRequest的返回值,包括asDocument(), asJsonArray(), asString()等与取结果流有关方法,以及 getStatusCode()等与取连接状态、头变量相关的方法

Configuration:保存Properties的静态类,设置连接超时时间、重试次数等

下面给出http访问类的主要方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class HttpClientP implements java.io.Serializable {
    private static final int OK = 200;// OK: Success!
 
    public Response httpRequest(String url, PostParameter[] postParams,
            boolean authenticated, String httpMethod) throws NException {
        int retriedCount;
        int retry = retryCount + 1;
        Response res = null;
        for (retriedCount = 0; retriedCount < retry; retriedCount++) {
            int responseCode = -1;
            try {
                HttpURLConnection con = null;
                OutputStream osw = null;
                try {
                    con = getConnection(url);
                    con.setDoInput(true);
                    setHeaders(url, postParams, con, false, httpMethod);
                    if (null != postParams || "POST".equals(httpMethod)) {
                        con.setRequestMethod("POST");
                        con.setRequestProperty("Content-Type",
                                "application/x-www-form-urlencoded");
                        con.setDoOutput(true);
                        String postParam = "";
                        if (postParams != null) {
                            postParam = encodeParameters(postParams);
                        }
                        log("Post Params: ", postParam);
                        byte[] bytes = postParam.getBytes("UTF-8");
 
                        con.setRequestProperty("Content-Length",
                                Integer.toString(bytes.length));
 
                        osw = con.getOutputStream();
                        osw.write(bytes);
                        osw.flush();
                        osw.close();
                    } else if ("DELETE".equals(httpMethod)) {
                        con.setRequestMethod("DELETE");
                    } else {
                        con.setRequestMethod("GET");
                    }
                     
                     
                     
                    res = new Response(con);
                    responseCode = con.getResponseCode();
                     
                    if (DEBUG) {
                        log("Response: ");
                        Map<String, List<String>> responseHeaders = con
                                .getHeaderFields();
                        for (String key : responseHeaders.keySet()) {
                            List<String> values = responseHeaders.get(key);
                            for (String value : values) {
                                if (null != key) {
                                    log(key + ": " + value);
                                } else {
                                    log(value);
                                }
                            }
                        }
                    }
                    if (responseCode != OK) {
                        if (responseCode < INTERNAL_SERVER_ERROR
                                || retriedCount == retryCount) {
                            throw new NException(getCause(responseCode)
                                    + "\n" + res.asString(), responseCode);
                        }
                        // will retry if the status code is
                        // INTERNAL_SERVER_ERROR
                    } else {
                        break;
                    }
                } finally {
                    try {
                        osw.close();
                    } catch (Exception ignore) {
                    }
                }
            } catch (IOException ioe) {
                // connection timeout or read timeout
                if (retriedCount == retryCount) {
                    throw new NException(ioe.getMessage(), ioe,
                            responseCode);
                }
            }
            try {
                if (DEBUG && null != res) {
                    res.asString();
                }
                log("Sleeping " + retryIntervalMillis
                        + " millisecs for next retry.");
                Thread.sleep(retryIntervalMillis);
            } catch (InterruptedException ignore) {
                // nothing to do
            }
        }
        return res;
    }
 
 
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Response {
 
    private int statusCode;
    private Document responseAsDocument = null;
    private String responseAsString = null;
    private InputStream is;
    private HttpURLConnection con;
    private boolean streamConsumed = false;
 
   public Response()  {
         
    }
    public Response(HttpURLConnection con) throws IOException {
        this.con = con;
        this.statusCode = con.getResponseCode();
        if(null == (is = con.getErrorStream())){
            is = con.getInputStream();
        }
        if (null != is && "gzip".equals(con.getContentEncoding())) {
            // the response is gzipped
            is = new GZIPInputStream(is);
        }
    }
}

服务器端JSON序列化

前文已经说过,服务器采用Newtonsoft.JSON来序列化LINQ数据,这里,我们稍微将LinqToJson扩展一下,以适合Android端的GSON反序列化方法。先看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static string LinqToJson(object o)
        {
            string rtn = "";
            //设置Json序列化格式
            JsonSerializer js = new JsonSerializer();
            //JSON中的Key名称采用驼峰命名法,且首字母小写
            js.ContractResolver = new CamelCasePropertyNamesContractResolver();
             
            //设置JSON Date类型转换格式
            Newtonsoft.Json.Converters.IsoDateTimeConverter timeConverter = new Newtonsoft.Json.Converters.IsoDateTimeConverter();
            timeConverter.DateTimeFormat = "yyyy'-'MM'-'dd' 'HH':'mm':'ss";
            js.Converters.Add(timeConverter);
 
            //转换为为Json Array
            rtn = JArray.FromObject(o, js).ToString();
            return rtn;       
        }
 
与 之前的版本相比,我们主要设置了JSON序列化格式,由于hibernate Tools生成的JavaBean类均采用驼峰命名,且首字母小写,所以要在Newtonsoft.json中设置一个JsonSerializer来实 现匹配。另外,Date类型是必须要重新处理的数据类型。
1
测试一下:
1
2
3
4
5
6
7
8
9
public partial class Default : System.Web.UI.Page
   {
       protected void Page_Load(object sender, EventArgs e)
       {
           EmployeeEntity ee = new EmployeeEntity(1);
           lt_rtn.Text = ee.toJson();
 
       }
   }

数据库中的字段,可以看到已经转换字段名为首字母小写了:

客户端Android采用GSON反序列化

有了客户端的Http访问类,我们就可以来访问服务器了,我们采用下面的步骤来进行:

1、新建一个HashMap,包含发送到服务器的QueryString参数。在将来的使用中,通过向HashMap添加K-V对来实现添加QueryString参数

2、将此HashMap转换为一个加密的字符串

3、使用http.get()方法与服务器连接

4、如果出现Exception则进入全局Exception对象处理

5、连接正常的话,反序列化结果为对应对象

6、连接至此完成

现在我们暂时先使用一个Activity来测试这个连接过程,将所有流程都写入OnCreate()中,暂不考虑其他有更有逻辑的封装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    private String baseURL = Configuration.getServer();
    protected HttpClientP http = new HttpClientP();
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        TextView tvEmpName = (TextView) this.findViewById(R.id.empName);
        //设置QueryString参数的HashMap
        HashMap param = new HashMap();
        param.put("cmd", "test");
         
        //将此HashMap转换为加密字符串
        String parmstr = URLParamUtils.toURLParam(param);
        String paramstrall = baseURL+ "default.aspx?a=" + parmstr;
         
        try {
            //使用Http.get()连接 返回JsonArray
            JSONArray json = get(paramstrall,null, true).asJSONArray();
            String jsonfirst = json.get(0).toString();
 
            //新建Gson对象并设置与服务器发来相同格式的Date类型
            Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
             
            //反序列化Json数据为 Employees类型
            Employees emp = gson.fromJson(jsonfirst, Employees.class);
             
            //测试Employees数据
            tvEmpName.setText(emp.getFirstName());
             
        } catch (NException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    protected Response get(String url, PostParameter[] params,
            boolean authenticate) throws NException  {
 
        if (null != params && params.length > 0) {
            url += "&" + HttpClientP.encodeParameters(params);
        }
        return http.get(url, authenticate);
    }
 
}

结果如下

小结与扩展

这一章我们主要解决了移动客户端与服务器使用HTTP访问的问题,我们了解了http访问的整个过程,完成了客户端的HTTP访问类,在最后的例子中我们使用了http.get()方法来与服务器交互,当然我们的访问类也支持post方法。

在接下来的一章,我们将继续扩展HTTP访问的客户端与服务器端,主要解决传输加密、参数等问题。

用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(四)——Android端Http访问类(转)的更多相关文章

  1. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(四)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(四) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...

  2. android菜鸟学习笔记20----Android数据存储(四))Android数据库操作

    Android内置了一个名为SQLite的关系型数据库,这是一款轻量型的数据库,操作十分简便.SQLite与别的数据库不同的是,它没有数据类型.可以保存任何类型的数据到你所想要保存的任何表的任何列中. ...

  3. Android 学习笔记之AndBase框架学习(四) 使用封装好的函数实现单,多线程任务

    PS:Force Is Meaningless Without Skill 学习内容: 1.使用AndBase实现单线程任务... 2.使用AndBase实现多线程任务...   AndBase内部封 ...

  4. SSM整合 完美支持RESTful(Jsp和客户端<android ios...>)

    一 RESTful简介 RESTful是一种网络应用程序的设计风格和开发方式 它结构清晰 符合标准 易于理解 扩展方便 REST 即Representational State Transfer的缩写 ...

  5. [.NET] 一步步打造一个简单的 MVC 网站 - BooksStore(一)

    一步步打造一个简单的 MVC 网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 简介 主 ...

  6. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(二)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(二) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 前: ...

  7. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(三)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(三) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...

  8. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(一)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...

  9. Android 媒体存储服务(一)

    Android 媒体存储服务 本文介绍如何在 Android 中,开发者的 APP 如何使用媒体存储服务(包含MediaScanner.MediaProvider以及媒体信息解析等部分),包括如何把 ...

随机推荐

  1. CCF2014032窗口(C语言)

    问题描述 在某图形操作系统中,有 N 个窗口,每个窗口都是一个两边与坐标轴分别平行的矩形区域.窗口的边界上的点也属于该窗口.窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的 ...

  2. Jmeter NonGUI模式

    一般情况下我们都是在NonGUI模式下运行jmeter.这样做有两个好处 节省系统资源,能够产生更大的负载 可以通过命令行参数对测试场景进行更精细的配置 示例 创建luzhi.jmx脚本 jmeter ...

  3. python 全栈开发,Day65(MySQL练习题,参考答案)

    一.MySQL练习题 一.表关系 请创建如下表,并创建相关约束 二.操作表 1.自行创建测试数据 2.查询“生物”课程比“物理”课程成绩高的所有学生的学号.ps:针对的是自己的生物成绩比物理成绩高,再 ...

  4. Linux学习笔记:使用shell脚本实现ftp的自动上传下载

    在 Linux 下可以利用 Shell 实现 ftp 文件的自动上传和下载,封装至 crontab 更可实现定时调度. 1.ftp自动登录批量下载文件 ##### 从ftp服务器上的/home/dat ...

  5. AOJ 0525 Osenbei【穷竭搜索】

    AOJ 0525 题意: 有一个烤饼器可以烤r行c列的煎饼,煎饼可以正面朝上(用1表示)也可以背面朝上(用0表示).一次可将同一行或同一列的煎饼全部翻转.现在需要把尽可能多的煎饼翻成正面朝上,问最多能 ...

  6. (canvas)两小球碰撞后的速度问题研究

    这两天在研究canvas碰撞 先把小球开始运动的图拿出来 参考了一下别的的代码,在两个小球碰撞处理上,我觉得不完善 怎么样处理才算完善呢,当然是要用高中物理学的动量守恒了和机械能守恒了 机械能守恒我其 ...

  7. 【Java】 剑指offer(2) 不修改数组找出重复的数字

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少 ...

  8. 090实战 Hadoop离线项目介绍(不包括程序)

    一:项目场景 1.需求分析 根据用户行为数据进行程序的处理,得到结果保存到关系型数据库中 需要收集用户(系统使用者)在不同客户端上产生的用户行为数据,最终保存到hdfs上 需要明确收集字段的相关信息, ...

  9. How to cast List<Object> to List<MyClass> Object集合转换成实体集合

    List<Object> list = getList(); return (List<Customer>) list; Compiler says: cannot cast  ...

  10. 《Android进阶之光》--ButterKnife

    No1: 添加依赖库 Project的build.gradle文件添加 buildscript{ ... dependencies{ ... classpath 'com.neenbedankt.gr ...