4.2.1 网络请求之HTTP
HTTP请求&响应:(常用的只有Post与Get,还有Head/put/delete/connect/options/trace)
Get&Post(建议用post规范参数传递方式,并没有什么更优秀,只是大家都这样社会更和谐。)
网络请求中我们常用键值对来传输参数(少部分API用json来传递,毕竟不是主流)。
通过上面的介绍,可以看出虽然Post与Get本意一个是表单提交一个是请求页面,但本质并没有什么区别。
- Get方式:在url中填写参数: http://xxxx.xx.com/xx.php?params1=value1¶ms2=value2
- Post方式:参数是经过编码放在请求体中的。编码包括x-www-form-urlencoded 与 form-data。
因为url是存在于请求行中的,所以Get与Post区别本质就是参数是放在请求行中还是放在请求体中
当然无论用哪种都能放在请求头中。一般在请求头中放一些发送端的常量。
表单提交中get和 post方式的区别有4点:
1) get是从服务器上获取数据,post是向服务器传送数据。
2) get是把参数数据队列加到提交表单的 ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTPpost机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。所以,get安全性非常低,post安全性较高。
3) get,服务器端用 Request.QueryString获取变量的值;post,服务器端用Request.Form获取提交的数据。
4) get 传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。
常见问题:
1. Get是明文,Post隐藏: 错,不用https全都是明文。
2. Get传递数据上限XXX:有限制的是url长度,不是Http。Http服务器部分有限制的设置一下即可。
3. Get中文需要编码: 是真的...要注意:URLEncoder.encode(params, "gbk");
请求是键值对,但返回数据我们常用Json。对于内存中的结构数据,肯定要用数据描述语言将对象序列化成文本,再用Http传递,接收端并从文本还原成结构数据。对象(服务器)<-->文本(Http传输)<-->对象(移动端) 。
服务器返回的数据大部分都是复杂的结构数据,所以Json最适合。另:要求传输性能的话用FlatBuffers。
HttpURLConnection( HttpClient 被废弃了)
1. 入门级
public class NetUtils {
public static String post(String url, String content) {
HttpURLConnection conn = null;
try {
URL mURL = new URL(url); // 创建一个URL对象
conn = (HttpURLConnection) mURL.openConnection(); .// 获取HttpURLConnection对象
conn.setRequestMethod("POST"); // 设置请求方法为post
conn.setReadTimeout(5000); // 设置读取超时为5秒
conn.setConnectTimeout(10000); .// 设置连接网络超时为10秒
conn.setDoOutput(true); .// 设置此方法,允许向服务器输出内容
String data = content; .// post请求的参数
OutputStream out = conn.getOutputStream(); // 获得一个输出流,向服务器写数据
out.write(data.getBytes()); // GET方式不需要
out.flush(); out.close();
int responseCode = conn.getResponseCode(); // 调用此方法就不必再使用.connect()方法
if (responseCode == 200) {
InputStream is = conn.getInputStream();
String response = getStringFromInputStream(is);
return response;
} else
throw new NetworkErrorException("response status is "+responseCode);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) conn.disconnect(); // 关闭连接
}
return null;
}
public static String get(String url) {
HttpURLConnection conn = null;
try {
URL mURL = new URL(url); // 利用string url构建URL对象
conn = (HttpURLConnection) mURL.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(10000);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
InputStream is = conn.getInputStream();
String response = getStringFromInputStream(is);
return response;
} else
throw new NetworkErrorException("response status is "+responseCode);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) conn.disconnect();
}
return null;
}
// 模板代码 必须熟练
private static String getStringFromInputStream(InputStream is) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1)
os.write(buffer, 0, len);
is.close();
String state = os.toString(); // 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码)
os.close();
return state;
}
}
注意网络权限: <uses-permission android:name="android.permission.INTERNET"/>
2. 初级
同步&异步
这2个概念仅存在于多线程编程中。
android中默认只有一个主线程,也叫UI线程。因为View绘制只能在这个线程内进行。所以如果你阻塞了(某些操作使这个线程在此处运行了N秒)这个线程,这期间View绘制将不能进行,UI就会卡。所以要极力避免在UI线程进行耗时操作。网络请求是一个典型耗时操作。
通过上面的Utils类进行网络请求只有一行代码:NetUtils.get("http://www.baidu.com"); //这行代码将执行几百毫秒。
如果你这样写:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String response = Utils.get("http://www.baidu.com");
}
就会死!!!
这就是同步方式。直接耗时操作阻塞线程直到数据接收完毕然后返回。Android不允许的。
异步方式:(在子线程进行耗时操作,完成后通过Handler将更新UI的操作发送到主线程执行。)
//在主线程new的Handler,就会在主线程进行后续处理。
private Handler handler = new Handler();
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
new Thread(new Runnable() {
@Override
public void run() {
final String response = NetUtils.get("http://www.baidu.com"); //从网络获取数据
handler.post(new Runnable() { //向Handler发送处理操作
@Override
public void run() {
textView.setText(response); //在UI线程更新UI
}
});
}
}).start();
}
但这样写好难看。异步通常伴随者他的好基友回调。
这是通过回调封装的AsynNetUtils类。
public class AsynNetUtils {
public interface Callback{
void onResponse(String response);
}
public static void get(final String url, final Callback callback){
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final String response = NetUtils.get(url);
handler.post(new Runnable() {
@Override
public void run() {
callback.onResponse(response);
}
});
}
}).start();
}
public static void post(final String url, final String content, final Callback callback){
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final String response = NetUtils.post(url,content);
handler.post(new Runnable() {
@Override
public void run() {
callback.onResponse(response);
}
});
}
}).start();
}
}
然后使用方法:
private TextView textView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.webview);
AsynNetUtils.get("http://www.baidu.com", new AsynNetUtils.Callback() {
@Override
public void onResponse(String response) {
textView.setText(response);
}
});
但是,愚蠢的地方有很多:
- 每次都new Thread,new Handler消耗过大
- 没有异常处理机制
- 没有缓存机制
- 没有完善的API(请求头,参数,编码,拦截器等)与调试模式
- 没有Https
3. 高级 OKHttp
加入HTTP缓存机制
缓存对于移动端是非常重要的存在。
- 减少请求次数,减小服务器压力.
- 本地数据读取速度更快,让页面不会空白几百毫秒。
- 在无网络的情况下提供数据。
- 1. 高级 OKHttp
加入HTTP缓存机制
缓存对于移动端是非常重要的存在。
- 减少请求次数,减小服务器压力.
- 本地数据读取速度更快,让页面不会空白几百毫秒。
- 在无网络的情况下提供数据。
缓存一般由服务器控制(通过某些方式可以本地控制缓存,比如向过滤器添加缓存控制信息)。通过在请求头添加几个字段,正式使用时按需求也许只包含其中部分字段。
客户端要根据这些信息储存这次请求信息。然后在客户端发起请求的时候要检查缓存。遵循下面步骤:
注意 服务器返回304意思是数据没有变动滚去读缓存信息。
现在Android网络方面的第三方库很多,volley,Retrofit,OKHttp等,各有各自的特点。不过再怎么封装Volley在功能拓展性上始终无法与OkHttp相比。Volley停止了更新,而OkHttp得到了官方的认可,并在不断优化。
OkHttp是一个高效的HTTP库:
- 支持 SPDY ,共享同一个 Socket 来处理同一个服务器的所有请求
- 如果 SPDY 不可用,则通过连接池来减少请求延时
- 无缝的支持GZIP来减少数据流量
- 缓存响应数据来减少重复的网络请求
SPDY(读作“SPeeDY”)是Google开发的基于TCP的应用层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验。SPDY并不是一种用于替代HTTP的协议,而是对HTTP协议的增强。新协议的功能包括数据流的多路复用、请求优先级以及HTTP报头压缩。谷歌表示,引入SPDY协议后,在实验室测试中页面加载速度比原先快64%。
OKHttp(com.squareup.okhttp)是Android版Http客户端,非常高效,会自动处理常见的网络问题,像二次连接、SSL的握手问题。如果你的应用程序中集成了OKHttp,Retrofit默认会使用OKHttp处理其他网络层请求。OkHttp支持Android 2.3及其以上版本。对于Java, JDK1.7以上。从Android4.4开始HttpURLConnection的底层实现采用的是okHttp。
在OKHttp,每次网络请求就是一个Request,我们在Request里填写我们需要的url,header等其他参数,再通过Request构造出Call,Call内部去请求参数,得到回复,并将结果告诉调用者。
详细使用步骤如下:
- 同步请求 excute()
HTTP GET
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder() .url(url) .build();
Response response = client.newCall(request).execute();
if (response.isSuccessful())
return response.body().string();
else
throw new IOException("Unexpected code " + response);
} // Request是OkHttp中访问的请求,Builder是辅助类。Response即OkHttp中的响应。
HTTP POST
1) POST提交Json数据
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(JSON, json);
Request request = new Request.Builder() .url(url) .post(body) .build(); // 对比GET,放入post数据
Response response = client.newCall(request).execute();
if (response.isSuccessful())
return response.body().string();
else
throw new IOException("Unexpected code " + response);
} //使用Request的post方法来提交请求体RequestBody
2) POST提交键值对
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody formBody = new FormEncodingBuilder()
.add("platform", "android")
.add("name", "bug")
.build();
Request request = new Request.Builder() .url(url) .post(formBody) .build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else
throw new IOException("Unexpected code " + response);
}
OkHttp官方文档并不建议我们创建多个OkHttpClient。如果有需要,可以使用clone方法,再进行自定义。
- 异步请求 enqueue()
我们通过Request.Builder传入url,然后直接execute执行得到Response,通过Response可以得到code,message等信息。这是通过同步的方式去操作网络请求,而android是不允许在UI线程做网络请求操作的,因此我们需要自己开启一个线程。当然,OKHttp也支持异步线程并且有回调返回,有了上面同步的基础,异步只要稍加改动即可
private void enqueue(){
Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt") .build();
client.newCall(request).enqueue(new Callback() { //就是在同步的基础上讲execute改成enqueue
public void onFailure(Request request, IOException e) { }
public void onResponse(Response response) throws IOException {
//NOT UI Thread
if(response.isSuccessful()){
System.out.println(response.code());
System.out.println(response.body().string());
}
}
});
}// execute改成enqueue,接口回调的代码是在非UI线程的,有更新UI的操作要用Handler或者其他方式。
响应缓存
为了缓存响应,你需要一个你可以读写的缓存目录,和缓存大小的限制。这个缓存目录应该是私有的,不信任的程序应不能读取缓存内容。
一个缓存目录同时拥有多个缓存访问是错误的。大多数程序只需要调用一次new OkHttp()
,在第一次调用时配置好缓存,然后其他地方只需要调用这个实例就可以了。否则两个缓存示例互相干扰,破坏响应缓存,而且有可能会导致程序崩溃。
private final OkHttpClient client;
public CacheResponse(File cacheDirectory) throws Exception {
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);
client = new OkHttpClient();
client.setCache(cache);
}
public void run() throws Exception {
Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
Response response1 = client.newCall(request).execute();
if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
String response1Body = response1.body().string();
System.out.println("Response 1 response: " + response1);
System.out.println("Response 1 cache response: " + response1.cacheResponse());
System.out.println("Response 1 network response: " + response1.networkResponse()); Response response2 = client.newCall(request).execute();
if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
String response2Body = response2.body().string();
System.out.println("Response 2 response: " + response2);
System.out.println("Response 2 cache response: " + response2.cacheResponse());
System.out.println("Response 2 network response: " + response2.networkResponse());
System.out.println("Response 2 equals Response 1 ? " + response1Body.equals(response2Body));
}
response1 的结果在networkresponse,代表是从网络请求加载过来的;而response2的networkresponse 就为null,而cacheresponse有数据。因为设置了缓存因此第二次请求时发现缓存里有就不再去走网络请求了。
但有时候,即使在有缓存的情况下我们依然需要去后台请求最新的资源(比如资源更新了)这个时候可以使用强制走网络来要求必须请求网络数据
public void execute() throws Exception {
Request request = new Request.Builder() .url("http://publicobject.com/helloworld.txt").build();
Response response1 = client.newCall(request).execute();
if (!response1.isSuccessful()) throw new IOException("Unexpected code " + response1);
String response1Body = response1.body().string();
System.out.println("Response 1 response: " + response1);
System.out.println("Response 1 cache response: " + response1.cacheResponse());
System.out.println("Response 1 network response: " + response1.networkResponse()); request = request.newBuilder().cacheControl(CacheControl.FORCE_NETWORK).build();
Response response2 = client.newCall(request).execute();
if (!response2.isSuccessful()) throw new IOException("Unexpected code " + response2);
String response2Body = response2.body().string();
System.out.println("Response 2 response: " + response2);
System.out.println("Response 2 cache response: " + response2.cacheResponse());
System.out.println("Response 2 network response: " + response2.networkResponse());
System.out.println("Response 2 equals Response 1? " + response1Body.equals(response2Body));
}
// response2的cache response为null,network response依然有数据。
同样的我们可以使用 FORCE_CACHE 强制只要使用缓存的数据,但如果请求必须从网络获取才有数据,但又使用了FORCE_CACHE 策略就会返回504错误。
HTTP和HTTPS
1. HTTP是一个属于应用层的面向对象的协议,使用80端口。HTTP协议的主要特点可概括如下:
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得服务器的程序规模小,因而通信速度很快。
- 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态:HTTP协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
2. HTTPS(基于SSL的HTTP协议)使用了HTTP协议,但使用不同于HTTP协议的默认端口,使用443端口,以及一个加密、身份验证层(HTTP与TCP之间),即HTTP下加入SSL层。
使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示:
- 客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
- Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
- 客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
- 客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
- Web服务器利用自己的私钥解密出会话密钥。
- Web服务器利用会话密钥加密与客户端之间的通信。
SSL介绍:
安全套接字(Secure Socket Layer,SSL)协议是Web浏览器与Web服务器之间安全交换信息的协议,提供两个基本的安全服务:鉴别与保密。
SSL介于应用层和TCP层之间。应用层数据不再直接传递给传输层,而是传递给SSL层,SSL层对从应用层收到的数据进行加密,并增加自己的SSL头。
SSL协议的三个特性
① 保密:在握手协议中定义了会话密钥后,所有的消息都被加密。
② 鉴别:可选的客户端认证,和强制的服务器端认证。
③ 完整性:传送的消息包括消息完整性检查(使用MAC)
4.2.1 网络请求之HTTP的更多相关文章
- Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求
上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...
- Android之三种网络请求解析数据(最佳案例)
AsyncTask解析数据 AsyncTask主要用来更新UI线程,比较耗时的操作可以在AsyncTask中使用. AsyncTask是个抽象类,使用时需要继承这个类,然后调用execute()方法. ...
- IOS开发之—— 在AFN基础上进行的网络请求的封装
网络请求的思路:如果请求成功的话AFN的responseObject就是解析好的. 1发送网络请求:get/post/或者别的 带上URL,需要传的参数 2判断后台网络状态码有没有请求成功: 3 请求 ...
- Android,适合Restful网络请求封装
借助volley.Gson类库. 优点 网络请求集中处理,返回值直接为预期的对象,不需要手动反序列,提高效率,使用时建立好model类即可. 使用效果 DataProess.Request(true, ...
- Android okHttp网络请求之Json解析
前言: 前面两篇文章介绍了基于okHttp的post.get请求,以及文件的上传下载,今天主要介绍一下如何和Json解析一起使用?如何才能提高开发效率? okHttp相关文章地址: Android o ...
- 阶段一:通过网络请求,获得并解析JSON数据(天气应用)
“阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 在上一篇阶段一:解析JSON中提到,最近在写一个很简单的天气预报应用.即使功能很简单,但我还是想把它做成一个相对完 ...
- NSURLSession网络请求
个人感觉在网上很难找到很简单的网络请求.或许是我才疏学浅 , 所有就有了下面这一段 , 虽然都是代码 , 但是全有注释 . //1/获取文件访问路径 NSString *path=@"ht ...
- 【Swift】Alamofile网络请求数据更新TableView的坑
写这篇BLOG前,有些话不得不提一下,就仅当发发恼骚吧... 今天下午为了一个Alamofire取得数据而更新TableView的问题,查了一下午的百度(360也是见鬼的一样),竟然没有一个简单明了的 ...
- 【WP8.1】HttpClient网络请求、进度以及终止
工作这么长时间,起初还是喜欢用面向程序过程的思路去写代码. 慢慢的才会用面向对象的思路分析.解决问题.也算是一点点进步吧. 最近在做一个下载音乐的功能.用到了HttpClient类. 于是就简单的写了 ...
- ios htttp网络请求cookie的读取与写入(NSHTTPCookieStorage)
当你访问一个网站时,NSURLRequest都会帮你主动记录下来你访问的站点设置的Cookie,如果 Cookie 存在的话,会把这些信息放在 NSHTTPCookieStorage 容器中共享,当你 ...
随机推荐
- IOS 关于扬声器和听话筒的设置 ----自己试验过的,可以达到扩音器和听筒播放的效果
今下午项目中使用到了 扬声器和听筒的设置,我项目中是这样的,有一个聊天设置,聊天设置有一个使用扬声器 播放声音的设置. 这个设置是,当你打开那个开关的话,你在聊天中都可以根据你的使用来任意的播放声音, ...
- JS中插入节点的方法appendChild和insertBefore的应用
1.appendChild() 方法:可以向节点的子节点列表的末尾添加新的子节点.比如:appendChild(newchild)括号里可以是创建的标签var newchild = document. ...
- X32,X64,X86 代表什意义
X32,X64,X86是什么意思 各代表什么:X86指32位,X64指64位,现在用户最多的是XP,但win7是趋势,发展很快,建议你装个win7 32位的系统,下载的话地方很多,官方安装原版和gho ...
- frame中隐藏横向滚动条
设置frame的scrolling="yes",在右侧页面的body里加入: style="overflow-x:hidden;" 如:<body st ...
- mongodb下载、安装、配置服务启动、及可视化工具下载、使用
MongoDB: 1.下载地址:http://www.mongodb.org/downloads(32位还是64位自行选择).我下载的是:mongodb-win32-x86_64-3.2.4-sign ...
- make: g77: Command not found
编译cblas时报错,这时,修改Makefile.in中的编译文件中的g77为gfortran
- [vivado系列]设置Xilinx Documention Navigator
版本:2015.1 ------------------------------------------ 这是一个很便利FPGA工程师的文档整理收纳神器. 针对个人使用上的习惯,进行简单的2项设置. ...
- php文章内容分页并生成相应的htm静态页面代码
代码如下: <?php $url='test.php?1=1'; $contents="fjka;fjsa;#page#批量生成分成文件并且加上分页代码"; $ptext = ...
- hibernate执行session.createQuery(hql)时hql若有参数则报错
项目从Jboss换位Tomcat服务器,打开如下Hql都报错: SELECT COUNT(*) FROM SystemUser WHERE STATUS != -1 解决方法:在Lib中加入antlr ...
- nginx反向代理编译异常
cc1: warnings being treated as errors /root/nginx_tcp_proxy_module/ngx_tcp.c: 在函数‘ngx_tcp_add_addrs’ ...