魅蓝noye5游戏小助手熊猫金熊猫商城怎么没有了了怎么开启

yichenlian
阅读(1729)
最近研究微信的公众平台开发,需要和微信的服务器进行数据读取,简单研究了下jdk自带的HttpUrlConnection类(URLConnection的子类),简单实现了一下微信的access_token获取。
获取微信access_token的地址:
该地址是get方法请求即可,先贴代码,doGet:
private final String ACCESS_TOKEN_WEIXIN = &https://api./cgi-bin/token&;
public String doGet(String grantType, String appId, String secret){
String url = ACCESS_TOKEN_WEIXIN+&?grant_type=&+grantType+
&&appid=&+appId+&&secret=&+
HttpURLConnection conn =
InputStream is =
InputStreamReader reader =
BufferedReader br =
String str = &&;
URL weiUrl = new URL(url);
conn = (HttpURLConnection)weiUrl.openConnection();
conn.setRequestProperty(&connection&, &Keep-Alive&);
conn.connect();
is = conn.getInputStream();
reader = new InputStreamReader(is, &UTF-8&);
br = new BufferedReader(reader);
String readLine = &&;
while((readLine=br.readLine())!=null){
str+=readLine+&\n&;
} catch (Exception e) {
e.printStackTrace();
if(br!=null){
br.close();
if(conn!=null){
conn.disconnect();
}catch(Exception e1){
e1.printStackTrace();
我将InputStream流和InputStreamReader流都单独定义引用是为了测试流关闭的问题,最后只关闭最外层流,因为外层流关闭的时候就将包裹的流一并关闭了。见下面的BufferedReader类的close方法:
public void close() throws IOException {
synchronized (lock) {
if (in == null)
in.close();
}其中,in就是包裹的流。
下面贴post方法的代码,然后综合比较一下,代码:
public String doPost(String grantType, String appId, String secret){
HttpURLConnection conn =
InputStream is =
InputStreamReader reader =
BufferedReader br =
String str = &&;
URL url = new URL(ACCESS_TOKEN_WEIXIN);
conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod(&POST&);
conn.setDoOutput(true);//默认为false的,所以需要设置
conn.setUseCaches(false);
conn.setRequestProperty(&Content-Type&, &application/x-www-form-urlencoded&);
conn.connect();
OutputStream outStream = conn.getOutputStream();
DataOutputStream out = new DataOutputStream(outStream);
String params = &grant_type=&+grantType+
&&appid=&+appId+&&secret=&+
out.writeBytes(params);
out.close();
is = conn.getInputStream();
reader = new InputStreamReader(is, &UTF-8&);
br = new BufferedReader(reader);
String readLine = &&;
while((readLine=br.readLine())!=null){
str+=readLine+&\n&;
} catch (Exception e) {
e.printStackTrace();
if(br!=null){
br.close();
if(conn!=null){
conn.disconnect();
}catch(Exception e1){
e1.printStackTrace();
两相比较:
1.思路是一样的:首先建立链接对象(URL)-&由链接对象得到代理对象(HttpUrlConnection)-&设置一些链接参数-&启动链接通道(conn.connect();)-&进行输出\输入操作
2.get方法参数是带在链接上的,所以没有单独赋值操作,直接在启动链接通道后得到输入流即可得到返回值
3.post方法参数不带在链接上,需要单独赋值(赋值是使用输出流来进行的),需要将输出打开(conn.setDoOutput(true);)因为URLConnection默认输出是false;而input是默认打开的,故不用重复设置。
4.读取返回值方式一样。
在研究过程中,对于URLConnection怎样得到的InputStream比较好奇,查看源码为:
public InputStream getInputStream() throws IOException {
throw new UnknownServiceException(&protocol doesn't support input&);
所以很迷惑,是jdk1.7 。等下来研究出来再补上来。
-----------------------------------------------------------------
紧跟上面,对于jdk怎样实现的getInputStream和getOutputStream以及其他一些方法,经过研究,终于找到了。接下来释疑:
需要注意到:URL url = new URL(ACCESS_TOKEN_WEIXIN);
conn = (HttpURLConnection)url.openConnection();
这里得到HttpUrlConnection的时候是使用了强转得到的,故跟踪到URL类的openConnection方法内-&
public URLConnection openConnection() throws java.io.IOException {
return handler.openConnection(this);
这里的handler定义在URL类的上方-&
transient URLStreamH
然后去寻找handler的赋值方法getURLStreamHandler,发现如下代码-&
if (handler == null) {
String packagePrefixList =
packagePrefixList
= java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction(
protocolPathProp,&&));
if (packagePrefixList != &&) {
packagePrefixList += &|&;
// REMIND: decide whether to allow the &null& class prefix
// or not.
packagePrefixList += JDK_PACKAGE_PREFIX;
StringTokenizer packagePrefixIter =
new StringTokenizer(packagePrefixList, &|&);
while (handler == null &&
packagePrefixIter.hasMoreTokens()) {
String packagePrefix =
packagePrefixIter.nextToken().trim();
// do not try to instantiate the JDK gopher handler
// unless the system property had been explicitly set
if (protocol.equalsIgnoreCase(GOPHER) &&
packagePrefix.equals(JDK_PACKAGE_PREFIX) &&
!enableGopher) {
String clsName = packagePrefix + &.& + protocol +
&.Handler&;
Class cls =
cls = Class.forName(clsName);
其中JDK_PACKAGE_PREFIX在类中定义的是:
private static final String JDK_PACKAGE_PREFIX =
&sun.net.www.protocol&;再结合下面的
String clsName = packagePrefix + &.& + protocol +
&.Handler&;基本可以定位到handler类的实现类在sun.net.www.protocol.http.Handler。然后百度搜索之后找到源码(jdk下没有sun包的源码)-&
protected java.net.URLConnection openConnection(URL u)
throws IOException {
return openConnection(u, (Proxy)null);
protected java.net.URLConnection openConnection(URL u, Proxy p)
throws IOException {
return new HttpURLConnection(u, p, this);
可以看到返回值是HttpURLConnection,但是发现该类中并没有引入java.net.HttpURLConnection类,故猜测在Handler的同包下有一个同名类,果然发现该类,&-&
public class HttpURLConnection extends java.net.HttpURLConnection
这是关建行,该类继承了java.net.HttpURLConnection类。所以返回该类可以由java.net.HttpURLConnection类的父类URLConnection接到,然后强转成java.net.HttpURLConnection类。设计十分巧妙-&
public synchronized OutputStream getOutputStream() throws IOException {
if (!doOutput) {
throw new ProtocolException(&cannot write to a URLConnection&
+ & if doOutput=false - call setDoOutput(true)&);
if (method.equals(&GET&)) {
method = &POST&; // Backward compatibility
if (!&POST&.equals(method) && !&PUT&.equals(method) &&
&http&.equals(url.getProtocol())) {
throw new ProtocolException(&HTTP method & + method +
& doesn't support output&);
// if there's already an input stream open, throw an exception
if (inputStream != null) {
throw new ProtocolException(&Cannot write output after reading input.&);
if (!checkReuseConnection())
connect();......
这就是getOutputStream的实现地方了,正好doOutput属性的设置也在这里起到了作用。艾玛,终于找到实现的地方了,其他方法原理一致了。
稍微整理下:整个的类流转过程就是:sun.net.www.protocol.http.HttpURLConnection转化成java.net.URLConnection,然后强转成java.net.HttpURLConnection。
阅读排行榜在Android上用HttpURLConnection获取网页内容_Linux编程_Linux公社-Linux系统门户网站
你好,游客
在Android上用HttpURLConnection获取网页内容
来源:Linux社区&
作者:victoryckl
界面效果如下,在编辑框中输入网址,点击按钮后,获取编辑框中的网址,打开HttpURLConnection连接,并获取输入流,将返回的流保存为html文件,然后再用WebView将html文件显示出来。
主要代码GetHtml.java(完整代码GetHtml.zip下载在下面的地址)
下载在Linux公社的1号FTP服务器里,下载地址:
密码:www.muu.cc
在 2011年\8月\在上用HttpURLConnection获取网页内容
下载方法见
package&ckl.&&
import&java.io.F&&
import&java.io.FileOutputS&&
import&java.io.IOE&&
import&java.io.InputS&&
import&java.net.HttpURLC&&
import&java.net.MalformedURLE&&
import&java.net.URL;&&
import&android.app.A&&
import&android.os.B&&
import&android.util.L&&
import&android.view.V&&
import&android.view.View.OnClickL&&
import&android.webkit.WebV&&
import&android.widget.B&&
import&android.widget.EditT&&
public&class&GetHtml&extends&Activity&{&&
&&&&private&EditText&mEdit&=&null;&&
&&&&private&Button&mButton&=&null;&&
&&&&private&WebView&mWeb&=&null;&&
&&&&public&void&onCreate(Bundle&savedInstanceState)&{&&
&&&&&&&&super.onCreate(savedInstanceState);&&
&&&&&&&&setContentView(R.layout.main);&&
&&&&&&&&&&
&&&&&&&&mEdit&=&(EditText)findViewById(R.id.myEdit1);&&
&&&&&&&&mButton&=&(Button)findViewById(R.id.myButton1);&&
&&&&&&&&mWeb&=&(WebView)findViewById(R.id.myWeb1);&&
&&&&&&&&&&
&&&&&&&&mWeb.getSettings().setJavaScriptEnabled(true);&&
&&&&&&&&mWeb.getSettings().setPluginsEnabled(true);&&
&&&&&&&&&&
&&&&&&&&mButton.setOnClickListener(new&OnClickListener()&{&&
&&&&&&&&&&&&public&void&onClick(View&v)&{&&
&&&&&&&&&&&&&&&&String&strUrl&=&mEdit.getText().toString();&&
&&&&&&&&&&&&&&&&String&strFile&=&"/sdcard/test.html";&&
&&&&&&&&&&&&&&&&if&(!strUrl.startsWith("http://"))&{&&
&&&&&&&&&&&&&&&&&&&&strUrl&=&"http://"&+&strU&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&getStaticPageByBytes(strUrl,&strFile);&&
&&&&&&&&&&&&&&&&mWeb.loadUrl("file://"&+&strFile);&&
&&&&&&&&&&&&}&&
&&&&&&&&});&&
&&&&private&void&getStaticPageByBytes(String&surl,&String&strFile){&&
&&&&&&&&&&
&&&&&&&&Log.i("getStaticPageByBytes",&surl&+&",&"&+&strFile);&&
&&&&&&&&&&
&&&&&&&&HttpURLConnection&connection&=&null;&&
&&&&&&&&InputStream&is&=&null;&&
&&&&&&&&&&
&&&&&&&&File&file&=&new&File(strFile);&&
&&&&&&&&FileOutputStream&fos&=&null;&&
&&&&&&&&try&{&&
&&&&&&&&&&&&URL&url&=&new&URL(surl);&&
&&&&&&&&&&&&connection&=&(HttpURLConnection)url.openConnection();&&
&&&&&&&&&&&&&&
&&&&&&&&&&&&int&code&=&connection.getResponseCode();&&
&&&&&&&&&&&&if&(HttpURLConnection.HTTP_OK&==&code)&{&&
&&&&&&&&&&&&&&&&connection.connect();&&
&&&&&&&&&&&&&&&&is&=&connection.getInputStream();&&
&&&&&&&&&&&&&&&&fos&=&new&FileOutputStream(file);&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&int&i;&&
&&&&&&&&&&&&&&&&while((i&=&is.read())&!=&-1){&&
&&&&&&&&&&&&&&&&&&&&fos.write(i);&&&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&is.close();&&
&&&&&&&&&&&&&&&&fos.close();&&
&&&&&&&&&&&&}&&
&&&&&&&&}&catch&(MalformedURLException&e)&{&&
&&&&&&&&&&&&e.printStackTrace();&&
&&&&&&&&}&catch&(IOException&e)&{&&
&&&&&&&&&&&&e.printStackTrace();&&
&&&&&&&&}&finally&{&&
&&&&&&&&&&&&if&(connection&!=&null)&{&&
&&&&&&&&&&&&&&&&connection.disconnect();&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
相关资讯 & & &
& (10/10/:25)
& (11/01/:39)
& (03月08日)
& (07/10/:11)
& (06/21/:10)
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款
匿名 发表于 朋友,你好android网络框架OkHttp之get请求(源码初识)
OkHttp现在很火呀。于是上个星期就一直在学习OkHttp框架,虽然说起来已经有点晚上手了,貌似是2013年就推出了。但是现在它版本更加稳定了呀。这不,说着说着,OkHttp3.3版本在这几天又发布了。以下以OkHttp3.2版本为准,没办法,上个星期看的时候还是以3.2为最新版本的。首先,我们要先了解一些背景,OkHttp这个框架是有Square公司推出的,。如果想看API,。大概了解了OkHttp之后,我们应该知道OkHttp是一个网络框架,想想以前在开发中,网络框架一般用的是什么?很快我们就会想到刚学习android开发的时候接触的HttpURLConnection和Apache提供的HttpClient这两个类,然后就是后面推出的一些第三方网络框架,比如2013年google推出的Volley框架、android-async-http框架、2014年很火的Xutils、以及现在很多人用的Retrofit等等。这么多,到底选哪个?一开始我也晕。后来看了一些资料,似乎懂了一个概念:OkHttp是用来替换HttpURLConnection的,据说android4.4的HttpURLConnection就替换成了OkHttp。所以我们别拿OkHttp和这些网络框架比,这些网络框架也只是基于HttpURLConnection进行一些封装,使我们的代码更加简洁方便。懂了这点,我们应该就懂了为什么网上那么多OkHttp和Volley或者Retrofit等等这些框架结合使用了,其实是一个道理。那么我用的HttpUrlConnection或者HttpClient用的好好的,干嘛要用你的OkHttp?这里就来比较下HttpURLConnection和OkHttp。至于HttpClient嘛,android6.0已经把它的API废除了。用它还要引入org.apache.http.legacy.jar包,不值得,而且okhttp也已经提供了对应的okhttp-apache 模块。
HttpURLConnection和OkHttp的比较
HttpURLConnection有的API,OkHttp基本上都有(你有我有全都有呀,哈哈哈) HttpURLConnection和OkHttp都支持Https,流的上传和下载,超时,IP6、连接池等等
OkHttp比HttpURLConnection具有更好的同步异步请求、缓存机制,支持HttpDNS、重定向、Gzip压缩,平台适应性、很好的服务器IP的转换、直接Socket通信,支持拦截器等等。
看到这么多机制,是不是觉得很强大,通过Socket直接通信,以及很好的缓存机制,Gzip对于Http头部的压缩传输。自然对于网络请求这块使应用更加省流量、请求的更快。OkHttp对于Https和HttpDNS的支持,使得应用的网络通信安全性更高。当然说了它的好,现在也来说说它的
OkHttp不支持优先级请求 OkHttp不支持自签名证书 OkHttp header中不能传中文
虽然是不好的地方,但是OkHttp已经比较成熟了,网上解决这几个问题的资料也很多了。所以这些都不是问题。
一个简单的Get请求例子
这里我们就以经典的官网提供的Get请求的例子来学习下,说大概的代码。
先在manifest加个网络权限,养成良好习惯
然后在build.gradle文件的dependencies添加库如下:
dependencies {
compile 'com.squareup.okhttp3:okhttp:3.2.0'
compile 'com.squareup.okio:okio:1.7.0'
同步Get请求:
final OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
final Request request = new Request.Builder()
.url(&/helloworld.txt&)
.header(&User-Agent&,&OkHttp Example&)
new Thread(new Runnable() {
public void run() {
Response response = okHttpClient.newCall(request).execute();
Log.d(&zgx&,&response=====&+response.body().string());
response.body().close();
} catch (IOException e) {
e.printStackTrace();
}).start();
这个例子网上说烂了,没啥说的,来看下结果
很漂亮的样子。呵呵
异步Get请求:
修改上面部分代码,Call类调用enqueue方法。代码如下:
new Thread(new Runnable() {
public void run() {
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
public void onFailure(Call call, IOException e) {
Log.d(&zgx&,&response=====&+e.getMessage());
public void onResponse(Call call, Response response) throws IOException {
Log.d(&zgx&,&response=====&+response.body().string());
response.body().close();
}).start();
运行结果和上面一样。只是多了一个CallBack
其实还有Post请求,文件上传下载,图片加载,拦截器的使用,支持session的保持这里就先不说了。以后有时间再学习下。下面就是简单的来看下他的源码,只是以个人理解来分析下,看源码前先来了解一些基本的知识。
一些基本的知识
Http是一种基于TCP/IP连接的一套网络通信协议,它是一种一应一答的请求,它分为Get和Post请求,Get请求获取得是静态页面,它可以把参数放在URL字符串后面。而Post请求就不同了,它是把参数放在Http请求的正文的。
Get请求我们会这样请求:
private void HttpURLConnection_Get(){
//通过openConnection 连接
URL url = new java.net.URL(URL);
urlConn=(HttpURLConnection)url.openConnection();
//设置输入和输出流
urlConn.setDoOutput(true);
urlConn.setDoInput(true);
//关闭连接
urlConn.disconnect();
}catch(Exception e){
resultData = &连接超时&;
然后把获取到的urlConn连接的数据通过IO流把读取出来:
InputStreamReader in = new InputStreamReader(urlConn.getInputStream());
BufferedReader buffer = new BufferedReader(in);
String inputLine =
while (((inputLine = buffer.readLine()) != null)){
resultData += inputLine + &\n&;
System.out.println(resultData);
in.close();
Post请求则会这样:
private void HttpURLConnection_Post(){
//通过openConnection 连接
URL url = new java.net.URL(URL_Post);
urlConn=(HttpURLConnection)url.openConnection();
//设置输入和输出流
urlConn.setDoOutput(true);
urlConn.setDoInput(true);
urlConn.setRequestMethod(&POST&);
urlConn.setUseCaches(false);
// 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的
urlConn.setRequestProperty(&Content-Type&,&application/x-www-form-urlencoded&);
// 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,
// 要注意的是connection.getOutputStream会隐含的进行connect。
urlConn.connect();
//DataOutputStream流
DataOutputStream out = new DataOutputStream(urlConn.getOutputStream());
//要上传的参数
String content = &par=& + URLEncoder.encode(&ylx_Post+中正&, &UTF_8&);
//将要上传的内容写入流中
out.writeBytes(content);
//刷新、关闭
out.flush();
out.close();
}catch(Exception e){
resultData = &连接超时&;
然后同上把获取到的urlConn连接的数据通过IO流把读取出来,大概的代码就是这样。
HTTP加入SSL即是HTTPS,它安全性更高,HTTP使用得端口号是80,而HTTPS使用的端口号是443。HTTP协议以明文方式发送内容,不提供任何方式的数据。HTTPS协议一般需要到CA申请证书,当然也可以自签名证书,和12306一样。这里就说下HTTPS的核心SSL和TLS。
首先我们来看一张简单的模型图
从这张图我们可以看出,最左边为经典的ISO7层模型图,右边我们可以看到有一个SSL层,它又叫做安全套捷字层,它分为SSL记录协议和SSL握手协议。SSL位于传输层和应用层之间,其中SSL记录 层协议位于传输层协议之上,而SSL握手协议又在SSL记录协议之上。SSL记录协议可以为高层协议进行加密,压缩,封装等功能,而SSL握手协议进行的是身份认证,协商加密算法、交换加密密钥等。其中TLS和SSL类似,它建立在SSL3.0协议之上。主要的不同在于他们的加密算法不同,其他功能作用类似。想要详情看他们的区别,请看这篇文章。
基础基本上讲完了,现在就来说说OkHttp涉及到的一些知识了。支持SPDY协议和HTTP2.0协议,同步和异步请求,拦截机制,请求和响应的逻辑处理,缓存机制,重连和重定向机制,连接池,Gzip压缩,安全性,平台适应性等等。下面我们就来通过源码来一步步的学习。
源码分析:
支持哪些协议
既然是与服务器之间的通信协议,我们应该会想到Protocol这个类,这个类是干嘛的呢?它主要是配置与远程服务器的通信协议。既然这样,那我们去源码找下这个类。其实在OkHttpClient源码里面第一个属性我们就可以看到
private static final List DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
再进去Protocol类
public enum Protocol {
HTTP_1_0(&http/1.0&),
HTTP_1_1(&http/1.1&),
SPDY_3(&spdy/3.1&),
HTTP_2(&h2&);
//省略部分代码
进入这个类,我们发现,这是一个枚举类,它定义了一些和远程服务器通信的协议名称,如上面四种。
然后回到OkHttpClient这个类,跟踪protocols这个属性,我们会找到这个方法:
public Builder protocols(List protocols) {
protocols = Util.immutableList(protocols);
if (!protocols.contains(Protocol.HTTP_1_1)) {
throw new IllegalArgumentException(&protocols doesn't contain http/1.1: & + protocols);
if (protocols.contains(Protocol.HTTP_1_0)) {
throw new IllegalArgumentException(&protocols must not contain http/1.0: & + protocols);
if (protocols.contains(null)) {
throw new IllegalArgumentException(&protocols must not contain null&);
this.protocols = Util.immutableList(protocols);
public Builder connectionSpecs(List connectionSpecs) {
this.connectionSpecs = Util.immutableList(connectionSpecs);
我们会发现,OkHttp是支持http/1.1版本的,但是不支持http/1.0版本的协议,支持h2协议,以及spdy/3.1协议。而且协议名称不能为null。既然支持h2,就说明服务端支持 ALPN的,它将可以协商到协商到 HTTP/2。这个很好呀,好在哪里呢,我们可以看下这篇文章,?
同步和异步请求
这个其实上面已经说到过了,OkHttp可以通过调用execute()实现同步请求,然后通过enqueue()方法可以实现异步请求。从上面的请求代码我们可以知道,它是通过okHttpClient.newCall(request)得到一个Call对象来调用的。那现在我们就先来看下Call这个类。
public interface Call {
//初始化这个请求并且返回这个请求
Request request();
//同步方法
Response execute() throws IOE
//异步方法
void enqueue(Callback responseCallback);
//取消请求,完成的请求则不能取消
void cancel();
//省略部分代码
interface Factory {
Call newCall(Request request);
它是一个接口类,里面包含同步方法和异步方法,我们还可以看到定义了一个内部接口Factory ,实现这个接口,通过newCall方法返回Call对象,也就是上面我们调用的OkHttpClient.newCall(Request request),因为OkHttpClient对象已经实现这个接口。那么我们就回到OkHttpClient对象里面的newCall(Request request)方法里面。
public Call newCall(Request request) {
return new RealCall(this, request);
创建了一个RealCall对象,那么同步异步方法肯定就在这里面实现了,继续来看看,RealCall实现了Call接口,果然是在这里,接下来看同步和异步方法。
同步方法:
public Response execute() throws IOException {
//同步操作,如果这个请求已经请求完了,则直接抛异常返回
synchronized (this) {
if (executed) throw new IllegalStateException(&Already Executed&);
executed =
//通过dispatcher类来实现同步请求
client.dispatcher().executed(this);
//拦截器,下文再说
Response result = getResponseWithInterceptorChain(false);
if (result == null) throw new IOException(&Canceled&);
} finally {
client.dispatcher().finished(this);
先不管Dispatcher类,先来看看异步请求方法:
void enqueue(Callback responseCallback, boolean forWebSocket) {
//同步操作,如果这个请求已经请求完了,则直接抛异常返回,省略这里代码
//然后是发出异步请求,也是通过Dispatcher类。而且这里增加了一个callback
client.dispatcher().enqueue(new AsyncCall(responseCallback, forWebSocket));
还是等下来看Dispatcher类,先来看下AsyncCall接口,实现了NamedRunnable这个线程,这个线程会把当前&OkHttp &+请求url设置为当前线程名。接下来就是看下核心的execute抽象方法的实现
@Override protected void execute() {
boolean signalledCallback =
//也是会来到拦截器里面,下文说
Response response = getResponseWithInterceptorChain(forWebSocket);
//如果这个请求已经取消,则直接调用callback的失败接口。
if (canceled) {
signalledCallback =
responseCallback.onFailure(RealCall.this, new IOException(&Canceled&));
//直接调用callback的成功接口。
signalledCallback =
responseCallback.onResponse(RealCall.this, response);
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
logger., &Callback failure for & + toLoggableString(), e);
//异常,调用失败接口
responseCallback.onFailure(RealCall.this, e);
} finally {
//异常,调用完成接口
client.dispatcher().finished(this);
其实主要还是回到拦截器里面,这里先不说拦截器,其他的也就是callback的逻辑处理。好了,现在就来说Dispatcher这个类了。上面我们说了同步和异步方法里面会去调用Dispatcher类的executed(this)和enqueue(AsyncCall call)方法。先来看下Dispatcher类的executed(this)
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
synchronized void finished(Call call) {
if (!runningSyncCalls.remove(call)) throw new AssertionError(&Call wasn't in-flight!&);
Dispatcher类通过一个同步队列runningSyncCalls会帮我保存同步的请求Call。然后在完成请求之后再finished里面remove掉,其实runningSyncCalls的作用就是统计当前有多少个同步请求,其他作用还没发现。
再来看下enqueue(AsyncCall call)方法:
synchronized void enqueue(AsyncCall call) {
//当前异步请求量小于请求量的最大值64,并且请求同一个host服务器地址小于5的条件下
if (runningAsyncCalls.size() & maxRequests && runningCallsForHost(call) & maxRequestsPerHost) {
//把请求的AsyncCall记录到正在执行的请求队列runningAsyncCalls,并且通过线程池去执行这个请求。
runningAsyncCalls.add(call);
executorService().execute(call);
/*超过最大请求量则添加到后备队列里面,等前面请求完成的时候,也就是调用finished(AsyncCall call)的时候。通过promoteCalls方法把readyAsyncCalls的请求添加到runningAsyncCalls执行*/
readyAsyncCalls.add(call);
把readyAsyncCalls的请求添加到runningAsyncCalls执行方法如下
private void promoteCalls() {
if (runningAsyncCalls.size() &= maxRequests) // Already running max capacity.
if (readyAsyncCalls.isEmpty()) // No ready calls to promote.
for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
if (runningCallsForHost(call) & maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
if (runningAsyncCalls.size() &= maxRequests) // Reached max capacity.
上面的代码应该都看的懂。
拦截器机制
先说说拦截器是用来干嘛的吧。六个字:监控,修改,添加。可以监控请求和响应日志,可以修改http请求信息,比如将域名替换为ip地址,将请求头中添加host属性。可以添加我们应用中的一些公共参数,比如设备id、版本号等等。
先来个官网提供的监控请求和响应日志吧
class LoggingInterceptors implements Interceptor{
private final Logger logger = Logger.getLogger(LoggingInterceptors.class.getName());
public Response intercept(Chain chain) throws IOException {
long t1 = System.nanoTime();
Request request = chain.request();
(String.format(&Sending request %s on %s%n%s&,
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
(String.format(&Received response for %s in %.1fms%n%s&,
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
然后创建OkHttp的时候这样创建就好了
new OkHttpClient()
.newBuilder()
.addNetworkInterceptor(new LoggingInterceptors())
代码就不解释了,网上很多这个例子。
再简单说下添加公共参数的例子。
class LoggingInterceptors implements Interceptor{
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
//添加请求头,可以添加多个参数,或者在original.body()http的body里面添加参数
Request.Builder requestBuilder = original.newBuilder()
.addHeader(&device&, Build.DEVICE)
.method(original.method(),original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
上面只是一个简单的参考,可以参考?,我们也可以和源码里面一样通过Builder的形式,向外公开一些add方法。
其实网上也挺多这方面的例子的,拦截器还是很好用的,下面我们就来学下OkHttp源码里面的拦截器
通过上面例子可以知道chain.proceed(request)直接然后的是http的响应请求,那么现在就来这个方法看下吧。
public interface Interceptor {
Response intercept(Chain chain) throws IOE
interface Chain {
Request request();
Response proceed(Request request) throws IOE
Connection connection();
我们可以看到有proceed这个方法。有两个拦截器实现这个接口,一个是应用拦截器ApplicationInterceptorChain,一个是网络拦截器NetworkInterceptorChain。那么怎么理解这两种拦截器呢,这里我们先来debug下。
先来debug下addInterceptor这个方法,也就是应用拦截器。
vcjnzbyjrM7Sw8e/ydLUv7TPwrX308Nwcm9jZWVko6ijqbe9t6i1xLXYt73Wu9PQ0ru49qOs0rK+zcrHQXBwbGljYXRpb25JbnRlcmNlcHRvckNoYWluoaO2+EFwcGxpY2F0aW9uSW50ZXJjZXB0b3JDaGFpbrXEcHJvY2VlZLe9t6jA78PmtffTw2ludGVyY2VwdLe9t6ijrLb4TmV0d29ya0ludGVyY2VwdG9yQ2hhaW61xHByb2NlZWS3vbeow7vX36Os1eLSssrHzqrKssO0yc/D5sD919O08tOh0ru0zrXE1K3S8qGjyOe5+7K70MW1xLuwo6y/ydLUyKVOZXR3b3JrSW50ZXJjZXB0b3JDaGFpbrXEcHJvY2VlZLbPteO198rUz8KjrMv8yseyu9ffxMfA77XEoaO908/CwLTO0sPHvs3AtL+0z8IuYWRkTmV0d29ya0ludGVyY2VwdG9yKKOpo6zAtLX3ytTPwqGjPC9wPg0KPHA+PGltZyBhbHQ9"这里写图片描述" src="/uploadfile/Collfiles/66.png" title="\" />
这里有两个地方调用了proceed(),说明了ApplicationInterceptorChain的proceed方法里面也调用intercept方法,可以去ApplicationInterceptorChain的proceed断点调试下,它还是会走那里。这也是上面例子打印两次的原因
所以可以把它理解为应用拦截器,是http发送请求的时候进行一次拦截,它不会走网络进行请求的。而网络拦截器我们可以把它看做是服务器响应的时候进行的一次拦截,它会走网络请求的。可以结合下图来理解
来看下应用拦截器ApplicationInterceptorChain的process方法
@Override public Response proceed(Request request) throws IOException {
//如果拦截链里面还有拦截器,则调用这里,也就是递归处理
if (index & client.interceptors().size()) {
Interceptor.Chain chain = new ApplicationInterceptorChain(index + 1, request, forWebSocket);
Interceptor interceptor = client.interceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
if (interceptedResponse == null) {
throw new NullPointerException(&application interceptor & + interceptor
+ & returned null&);
return interceptedR
//如果没有拦截器,则继续进行http请求
return getResponse(request, forWebSocket);
继续来看下网络拦截器NetworkInterceptorChain的proceed方法。
@Override public Response proceed(Request request) throws IOException {
//统计proceed调用次数
if (index & 0) {
Interceptor caller = client.networkInterceptors().get(index - 1);
Address address = connection().route().address();
//每次递归都会去判断url是否包含host和port。
if (!request.url().host().equals(address.url().host())
|| request.url().port() != address.url().port()) {
throw new IllegalStateException(&network interceptor & + caller
+ & must retain the same host and port&);
// Confirm that this is the interceptor's first call to chain.proceed().
if (calls & 1) {
throw new IllegalStateException(&network interceptor & + caller
+ & must call proceed() exactly once&);
//递归NetworkInterceptorChain,直到拦截链里面没有拦截器
if (index & client.networkInterceptors().size()) {
NetworkInterceptorChain chain = new NetworkInterceptorChain(index + 1, request);
Interceptor interceptor = client.networkInterceptors().get(index);
Response interceptedResponse = interceptor.intercept(chain);
// Confirm that the interceptor made the required call to chain.proceed().
if (chain.calls != 1) {
throw new IllegalStateException(&network interceptor & + interceptor
+ & must call proceed() exactly once&);
if (interceptedResponse == null) {
throw new NullPointerException(&network interceptor & + interceptor
+ & returned null&);
return interceptedR
//把request写入http头部里面
httpStream.writeRequestHeaders(request);
//更新的networkRequest,可能拦截已经请求更新
networkRequest =
//这里是对post请求进行的一些body写入
if (permitsRequestBody(request) && request.body() != null) {
Sink requestBodyOut = httpStream.createRequestBody(request, request.body().contentLength());
BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
request.body().writeTo(bufferedRequestBody);
bufferedRequestBody.close();
//通过io包进行一些io流操作
Response response = readNetworkResponse();
int code = response.code();
if ((code == 204 || code == 205) && response.body().contentLength() & 0) {
throw new ProtocolException(
&HTTP & + code + & had non-zero Content-Length: & + response.body().contentLength());
其实拦截器还有个stetho工具,集成它使用拦截器获取日志就更方便了。集成教程网上有很多。
请求和响应的逻辑处理
其实上面我们在proceed方法里面已经提到了这块,它是通过getResponse()方法返回Response的,其实发送网络请求和得到返回的响应都是在这里面进行逻辑处理的,如果中途取消请求的时候,getResponse()返回null
Response getResponse(Request request, boolean forWebSocket) throws IOException {
//...省略前面代码
//发送请求
engine.sendRequest();
//回复的响应
engine.readResponse();
releaseConnection =
//...省略后面代码
从上面可以知道,请求和响应的逻辑处理是在HttpEngine对象的,接下来就来看下HttpEngine对象
这里是发送一个请求方法
public void sendRequest() throws RequestException, RouteException, IOException {
//...省略部分代码
//通过Request.Builder设置Request的配置信息,然后返回配置好的Request 对象
Request request = networkRequest(userRequest);
//...省略部分代码
//将request 传入缓存的处理类里面进行一些缓存处理,然后返回networkRequest ,其实和request 一样
networkRequest = cacheStrategy.networkR
//...省略部分代码
//真正的通过socket通信发送请求出去
httpStream = connect();
httpStream.setHttpEngine(this);
//如果是post或者带有body的请求方式,执行下面部分写出body
if (writeRequestHeaders()) {
long contentLength = OkHeaders.contentLength(request);
if (bufferRequestBody) {
if (contentLength & Integer.MAX_VALUE) {
throw new IllegalStateException(&Use setFixedLengthStreamingMode() or &
+ &setChunkedStreamingMode() for requests larger than 2 GiB.&);
if (contentLength != -1) {
// Buffer a request body of a known length.
httpStream.writeRequestHeaders(networkRequest);
requestBodyOut = new RetryableSink((int) contentLength);
// Buffer a request body of an unknown length. Don't write request headers until the
otherwise we can't set the Content-Length header correctly.
requestBodyOut = new RetryableSink();
httpStream.writeRequestHeaders(networkRequest);
requestBodyOut = httpStream.createRequestBody(networkRequest, contentLength);
} finally {
// If we're crashing on I/O or otherwise, don't leak the cache body.
if (!success && cacheCandidate != null) {
closeQuietly(cacheCandidate.body());
读取响应的方法
public void readResponse() throws IOException {
//...省略部分代码
if (forWebSocket) {
httpStream.writeRequestHeaders(networkRequest);
networkResponse = readNetworkResponse();
} else if (!callerWritesRequestBody) {
networkResponse = new NetworkInterceptorChain(0, networkRequest).proceed(networkRequest);
// Emit the request body's buffer so that everything is in requestBodyOut.
if (bufferedRequestBody != null && bufferedRequestBody.buffer().size() & 0) {
bufferedRequestBody.emit();
//...省略部分代码
//其实真正的我们还是通过这个方式来获取响应数据的
networkResponse = readNetworkResponse();
//...省略部分代码
接下来就来看看readNetworkResponse这个方法
private Response readNetworkResponse() throws IOException {
httpStream.finishRequest();
//这里通过io流去读取响应的数据
Response networkResponse = httpStream.readResponseHeaders()
.request(networkRequest)
.handshake(streamAllocation.connection().handshake())
.header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
.header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
if (!forWebSocket) {
networkResponse = networkResponse.newBuilder()
.body(httpStream.openResponseBody(networkResponse))
if (&close&.equalsIgnoreCase(networkResponse.request().header(&Connection&))
|| &close&.equalsIgnoreCase(networkResponse.header(&Connection&))) {
streamAllocation.noNewStreams();
return networkR
其实发送请求这块还有很多源码的处理,其他的就先不看了,基本上是一些io流的处理了。
先来看看OkHttp的缓存怎么设置
int cacheSize = 10 * 1024 * 1024; // 10 MiB
//cacheDirectory保存缓存的目录,cacheSize缓存空间的大小
Cache cache = new Cache(context.getCacheDir(), cacheSize);
final OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.cache(cache)
这样我们就设置好了。下面我们就来分析下源码,其实在上面发送请求和读取响应方法里面已经有缓存处理的逻辑。回到sendRequest()方法
public void sendRequest() throws RequestException, RouteException, IOException {
//...省略部分代码
/*Internal是一个抽象类,定义了很多个抽象类,其中就有setCache(OkHttpClient.Builder builder, InternalCache internalCache)这个方法,然后.internalCache(client)其实它会去调用OkHttpClient里的static块里的Internal的internalCache方法,返回一个InternalCache*/
InternalCache responseCache = Internal.instance.internalCache(client);
//Cache类获取缓存里面的响应数据
Response cacheCandidate = responseCache != null
? responseCache.get(request)
long now = System.currentTimeMillis();
//创建CacheStrategy.Factory对象,进行缓存配置
cacheStrategy = new CacheStrategy.Factory(now, request, cacheCandidate).get();
//传入的网络连接
networkRequest = cacheStrategy.networkR
//cacheCandidate 传入CacheStrategy后得到的缓存的响应数据
cacheResponse = cacheStrategy.cacheR
if (responseCache != null) {
//记录当前请求是网络发起还是缓存发起
responseCache.trackResponse(cacheStrategy);
//如果传入CacheStrategy不可用并且cacheResponse 为null,结束所有请求连接资源
if (cacheCandidate != null && cacheResponse == null) {
closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
// 如果网络连接被禁止访问并且缓存为null的时候
if (networkRequest == null && cacheResponse == null) {
userResponse = new Response.Builder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.protocol(Protocol.HTTP_1_1)
.code(504)
.message(&Unsatisfiable Request (only-if-cached)&)
.body(EMPTY_BODY)
// 如果没有网络的情况下,这时候缓存是不为null的,所以这里就去获取缓存里面的数据
if (networkRequest == null) {
userResponse = cacheResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.cacheResponse(stripBody(cacheResponse))
userResponse = unzip(userResponse);
//...省略部分代码
上面的返回的InternalCache 接口定义在了Cache这个类里面,我们可以看到OkHttp使用的缓存是DiskLruCache,详细缓存处理就不说了。
public void readResponse() throws IOException {
//...省略部分代码
if (cacheResponse != null) {
//检查缓存是否可用,如果可用。那么就用当前缓存的Response,关闭网络连接,释放连接。
if (validate(cacheResponse, networkResponse)) {
userResponse = cacheResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.headers(combine(cacheResponse.headers(), networkResponse.headers()))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
networkResponse.body().close();
releaseStreamAllocation();
// headers去掉Content-Encoding之后更新缓存
InternalCache responseCache = Internal.instance.internalCache(client);
responseCache.trackConditionalCacheHit();
responseCache.update(cacheResponse, stripBody(userResponse));
userResponse = unzip(userResponse);
closeQuietly(cacheResponse.body());
userResponse = networkResponse.newBuilder()
.request(userRequest)
.priorResponse(stripBody(priorResponse))
.cacheResponse(stripBody(cacheResponse))
.networkResponse(stripBody(networkResponse))
if (hasBody(userResponse)) {
maybeCache();
userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
其实缓存这块还不是特别理解,由于篇幅比较长了,而且这篇是初识篇,其实还有很多没去学习,比如重连和重定向机制,连接池,Gzip压缩,安全性,平台适应性,cookie的保持等等的知识,下次抽时间再来学习下了。

我要回帖

更多关于 熊猫屁王为什么没有了 的文章

 

随机推荐