Android从零开搞系列:网络框架系列(1)OkHttp+可测试的服务器URL+Gson分析(上)

OkHttp的基本使用

转载请注意:http://blog.youkuaiyun.com/wjzj000/article/details/53677706
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)

之前写过一篇相关的内容,比较粗糙,但是简单粗暴:
http://blog.youkuaiyun.com/wjzj000/article/details/52562674
从零系列,我自己真正开始记录总结技术的起点。这里边的分析都是我安安静静一点点看,一点点梳理记录的。希望给我带来帮助的同时,能传播一些爱与正义。


服务器绑定了域名

因此以前URL前面部分:http://120.27.4.196:8080/

需要更换成http://www.ohonor.xyz/


最简单的GET请求

  • 这里使用的URL是我自己写的一个简单的Servlet类:
  • http://120.27.4.196:8080/test/servlet/ShowServlet
  • 正常返回的数据是:[{“temperature”:”123”,”humidity”:”41234”}]
  • 这个服务器程序跑在我的阿里云服务器上,所以只要我还交着钱…这个URL随时可以拿来测试。

让我们来看一下请求代码

我知道这里肯定会有很多的问题和疑惑。
因为最开始的我,也是如此。
所以我会把我的最初的一些疑惑,穿插着写在其中。

        OkHttpClient okHttpClient=new OkHttpClient();
        /**
         * 很明显这里是一个建造者模式,所以我们只需要调用Builder这个静态内部
         * 类中的特定方法。比如url()这个方法,然后我们的参数就会被设置到Builder
         * 之中,然后通过调用build()方法,将Builder中的值赋值给Request,然后
         * 返回Request对象。
         */
        Request request=new Request.Builder()
                .url("http://120.27.4.196:8080/test/servlet/ShowServlet")
                //.header("User-Agent", "XXXXXXXXX")
                //.addHeader("Accept", "XXXXXXXXXX")
                /**
                 * 如上文所写,我们可以给Http请求添加头信息。
                 * header和addHeader的区别在于:
                 * header内部调用了清空头信息的方法,也就是说用于初始化。
                 * 而addHeader就是单纯的添加头信息。
                 */
                .build();
        Call call=okHttpClient.newCall(request);
        //enqueue()方法是在子线程进行的,因此回调之中我们要进行异步操作。
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                //请求失败的回调
            }
            @Override
            public void onResponse(final Response response) throws IOException {
                //请求成功的回调。
                /**
                 * 在这里我们可以通过返回值response来转换我们想要的值:
                 * 字符串,可以通过response.body().string()获取;
                 * 二进制字节数组,可以通过response.body().bytes();
                 * 输入流,可以通过response.body().byteStream()
                 */
                //这个方法是Activity中的方法
                /**
                 * 我们查看源码可以发现:
                 * if (Thread.currentThread() != mUiThread) {
                 *      mHandler.post(action);
                 * } else {
                 *     action.run();
                 * }
                 * 这里是通过Hanlder机制完成异步操作。
                 */
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            tv_content.setText(response.body().string());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        });

让我们看一下效果

其实没啥好看的,就是在TextView中打印这么一行
这里写图片描述


既然这里我们拿到的是JSON格式,所以关于Json的用法,怎么能少呢。不要着急,往下下下下下看。


疑惑:为什么通过访问URL能拿到数据?

这个问题曾经困扰过我很久。因为我在大学之前对计算机并没有什么兴趣。除了玩游戏以及和那些存在于高中物理复习资料文件夹中,德艺双馨的老师们交流以外。对电脑没有任何了解。

因此我在接触网络请求的时候,真的是一脸懵逼,感觉网络真的是so diao,犹如老树盘根倒挂蜡般冲击着我的灵魂。


扯的有点远,咱们交个闪现先回来。

通过代码我们可以看到,我们传递了一个URL,因此我们得到的数据一定是这个URL的作用。
URL全称:统一资源定位符。它就是浩瀚互联网计算机集群之中某个资源的id。通过它我们就能找到这个资源。而这个URL是我写的一个Servlet类。这个类的作用就是从数据库查询出符合条件的数据,然后输出:
[{“temperature”:”123”,”humidity”:”41234”}]这么简单的一句话。(为了更好的展示,又在数据库中加了2条数据。)
因此当我们访问这个URL的时候,我们拿到的就是这一句话。

这里必须要聊一聊服务器,关于服务器的概念也是一直困扰了我很长时间。直至后来我用我自己的理解方式,能够把这些网络现象解释给自己听。
我们想要访问互联网上的资源,实际上是进程进程的通讯。也就是说我们用某一个程序去访问另一个程序。而每个程序都对应着端口号。我们就是通过IP地址和端口号来确定想要访问的对象。上文那个链接就可以解释这个问题。120.27.4.196是我的阿里云服务器(这里的服务器其实就是一台计算机。只不过它专门用于提供服务)的IP。但是为什么没有端口号?之前是有的。8080,有些朋友可能知道这是Tomcat的端口号。没错。既然我写的是Servlet所以只能部署在Tomcat上。那么为什么这里有没有端口号呢,实际上我将Tomacat的端口号改成了80,而80端口号可以不用显示的写出来。(避免端口号冲突,又改回了8080)
那么我们通过浏览器访问这个URL,其实就是浏览器和Tomcat就行通讯。通过IP+端口号的方式找到了我的这台计算机(服务器)上运行的Tomacat服务器(我更倾向于叫它服务器程序)。而后面的/test/servlet/ShowServlet就是我部署在Tomcat特定目录上的java后台程序中的那个Servlet类。
而这个类的作用就是操作数据库进行增删改查,然后把访问者想要的数据返回给它。
我们平时说看到的后台技术,本质就是在做这个工作。像php,Node.js….等等…他们的就是一系列操作数据库的代码,然后运行在能够容纳他们的特定服务器程序上,比如Apache,Nginx。而我们的请求就是通过IP定位到特定的服务器(计算机),在通过端口号定位到具体的服务器程序。然后找到这个后台程序具体的路径,完成请求…就是这样。说了好多…连自己都不知道有没有用……


关于代码的疑问:这里为什么是GET请求,没有声明啊?

通过源码我们可以很直观的看到原因

    public Builder get() {
      return method("GET", null);
    }
    public Builder head() {
      return method("HEAD", null);
    }
    public Builder post(RequestBody body) {
      return method("POST", body);
    }
    //省略其他请求方式...

    //构造方法
    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }

我们可以通过Buidler中的方法来告诉Request我们使用什么方式请求。
在Builder的构造方法中,我们可以看到默认的方式是GET方式。


这里我们来接上文提到的Json问题

我们先看一看JSON的格式:

  • {“a”:”1”,”b”:”2”} 最简单的JSON格式。
  • { “aa”: [{ “name”: “Brett”, “age”:”1” },{ “name”: “Jason”, “age”:”2”}]} 数组的表示格式
  • 稍稍解释一下数组格式的使用方法。首先我们先通过aa拿到其中的数组也就是[]包含的内容,然后按正常的方式去解析,放在一个List之中。

代码实现

我们可以直接使用官方的API对JSON进行解析:
这里不知道大家注意没注意到,虽然我们服务器返回的数据很短,但是它却是一个数组类型!而解析数组类型和普通类型是不同的。


//解析数组
try {
     JSONArray jsonArray = new JSONArray(response.body().string());
     JSONObject jsonObject=jsonArray.getJSONObject(0);
     String strOne=jsonObject.getString("temperature");
     String strTwo=jsonObject.getString("humidity");
     tv_content.setText("从JSON中拿到的第一个数据:"+strOne+","+"从JSON中拿到的第二个数据:"+strTwo);
     } catch (JSONException e) {
     } catch (IOException e) {
            e.printStackTrace();
     }
//普通解析
JSONObject jsonObject=new JSONObject(response.body().string());
String strOne=jsonObject.getString("temperature");
String strTwo=jsonObject.getString("humidity");

这样写说实话比较的长,尤其是当我们的JSON涵盖的数据非常多的时候。
因此我们要祭出我们常用的JSON解析框架:Gson。


Gson

既然是拓展库,我们肯定要在Gradle中引用:

compile ‘com.google.code.gson:gson:2.8.0’

Gson gson=new Gson();
Type type=new TypeToken<List<GsonTestBean>>(){}.getType();
try {
    List<GsonTestBean> arrays=gson.fromJson(response.body().string(), type);
    tv_content.setText("从JSON中拿到的第一个数据:"+arrays.get(0).getTemperature()+","+"从JSON中拿到的第二个数据:"+arrays.get(0).getHumidity());
   } catch (IOException e) {
           e.printStackTrace();
}
//解析普通JSON
Gson gson=new Gson();
try {
    GsonTestBean gsonTestBean=
    gson.fromJson(response.body().string(),GsonTestBean.class);
    } catch (IOException e) {
            e.printStackTrace();
}

效果和上边是一样的,没啥好说的…

简单梳理一下:
正常解析普通JSON,只需要对应JSON格式中的键写好JavaBean即可。然后通过fromJson方法转好成对应的JavaBean对象。 我们想要将JavaBean转成JSON直接调用toJson(传入JavaBean的对象)
但是涉及到数组型,我们就要使用TypeToken。为什么?
其实比较好理解,因为fromJson方法中需要传递一个Class类型的参数。如果我们只是简单的解析,那么传一个简单的类这的确没有问题。因为这属于普通解析。
但是当我们传List就有问题了。因为这涉及到泛型擦除问题。在运行时,我们的泛型统一成了Object对象,所以我们无法在运行期间获知泛型参数的具体类型。因此我们的Gson就会一脸懵逼,没有类型所以没办法进行转换。因此这里使用了TypeToken就行了一些小小的转换。


尾声

先记录到此,以后遇到实用的就继续加上。上部分到此结束,更多用法请阅读下部分。

最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值