您的位置:首页 > 理论基础 > 计算机网络

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

2016-12-15 22:03 405 查看

OkHttp的基本使用

转载请注意:http://blog.csdn.net/wjzj000/article/details/53677706

本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…

https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)

https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)

之前写过一篇相关的内容,比较粗糙,但是简单粗暴:

http://blog.csdn.net/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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 开源 gson OkHttp