上篇文章: java HttpURLConnection实现简单的网络请求,用java实现了简单的网络请求。我们看看在Android的主线程上做网络请求,会发生什么?
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getTextFromHttp();
}
private void getTextFromHttp() {
try {
// 根据地址创建URL对象(网络访问的url)
URL url = new URL("https://publicobject.com/helloworld.txt");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(5000);
urlConnection.setConnectTimeout(5000);
urlConnection.connect();
if (urlConnection.getResponseCode() == 200) {
InputStream is = urlConnection.getInputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int len = 0;
byte buffer[] = new byte[1024];
// 按照缓冲区的大小,循环读取
while ((len = is.read(buffer)) != -1) {
// 根据读取的长度写入到os对象中
os.write(buffer, 0, len);
}
is.close();
// os.close(); //无需关闭
urlConnection.disconnect();
String result = new String(os.toByteArray());
System.out.println(result);
} else {
System.out.println("------------------连接失败-----------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行下,报错了:
W/System.err: android.os.NetworkOnMainThreadException
W/System.err: at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
W/System.err: at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:86)
W/System.err: at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:74)
W/System.err: at java.net.InetAddress.getAllByName(InetAddress.java:752)
W/System.err: at com.android.okhttp.internal.Network$1.resolveInetAddresses(Network.java:29)
W/System.err: at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:187)
W/System.err: at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:156)
W/System.err: at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:98)
W/System.err: at com.android.okhttp.internal.http.HttpEngine.createNextConnection(HttpEngine.java:345)
W/System.err: at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:328)
W/System.err: at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:246)
W/System.err: at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:457)
W/System.err: at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:126)
W/System.err: at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:89)
W/System.err: at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java)
W/System.err: at com.exp.cpdemo.MainActivity.getTextFromHttp(MainActivity.java:41)
W/System.err: at com.exp.cpdemo.MainActivity.onCreate(MainActivity.java:31)
W/System.err: at android.app.Activity.performCreate(Activity.java:6760)
W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2619)
W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727)
W/System.err: at android.app.ActivityThread.-wrap12(ActivityThread.java)
W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1478)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
W/System.err: at android.os.Looper.loop(Looper.java:154)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6121)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
/frameworks/base/core/java/android/os/StrictMode.java
public final class StrictMode {
......
private static class AndroidBlockGuardPolicy implements BlockGuard.Policy {
private int mPolicyMask;
public AndroidBlockGuardPolicy(final int policyMask) {
mPolicyMask = policyMask;
}
......
public void onNetwork() {
if ((mPolicyMask & DETECT_NETWORK) == 0) {
return;
}
if ((mPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
//在这里抛出了异常
throw new NetworkOnMainThreadException();
}
......
}
......
}
......
}
W/System.err: at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java)
W/System.err: at com.exp.cpdemo.MainActivity.getTextFromHttp(MainActivity.java:41)
W/System.err: at com.exp.cpdemo.MainActivity.onCreate(MainActivity.java:31)
从日志中,可以看到 urlConnection.connect();
执行后,就跳到了HttpsURLConnectionImpl.connect()
方法。HttpsURLConnectionImpl是什么?为什么会跳到HttpsURLConnectionImpl里面?
URL url = new URL("https://publicobject.com/helloworld.txt");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
...
urlConnection.connect();
HttpURLConnection是一个抽象类,并且里面没有connect()方法。
/libcore/luni/src/main/java/java/net/HttpURLConnection.java
public abstract class HttpURLConnection extends URLConnection {
......
}
看下父类URLConnection里面有没有connect()方法。有,但是也是抽象方法。那这个connect()方法到底在哪里实现的?
public abstract class URLConnection {
...
public abstract void connect() throws IOException;
...
}
往前面看,对象一定是url.openConnection()
生成的。我们看看openConnection()方法源码。
/libcore/luni/src/main/java/java/net/URL.java
public final class URL implements Serializable {
...
transient URLStreamHandler streamHandler;
...
public URLConnection openConnection() throws IOException {
return streamHandler.openConnection(this);
}
}
streamHandler是 URLStreamHandler 的对象。继续看URLStreamHandler里面的openConnection()方法。
/libcore/luni/src/main/java/java/net/URLStreamHandler.java
public abstract class URLStreamHandler {
...
protected abstract URLConnection openConnection(URL u) throws IOException;
...
}
URLStreamHandler是一个抽象类。那它的实现类是谁?streamHandler是URL的成员变量,从URL类里面找找streamHandler的初始化。
//我们初始化用的是这个构造器
public URL(String spec) throws MalformedURLException {
this((URL) null, spec, null);
}
//这里入参,context为null,handler也为null
public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException {
if (spec == null) {
throw new MalformedURLException();
}
if (handler != null) {
streamHandler = handler;
}
spec = spec.trim();
protocol = UrlUtils.getSchemePrefix(spec);
int schemeSpecificPartStart = protocol != null ? (protocol.length() + 1) : 0;
...
if (context != null) {
...
} else if (protocol == null) {
throw new MalformedURLException("Protocol not found: " + spec);
}
if (streamHandler == null) {
//这里是streamHandler被赋值的地方
setupStreamHandler();
if (streamHandler == null) {
throw new MalformedURLException("Unknown protocol: " + protocol);
}
}
...
}
void setupStreamHandler() {
...
// Fall back to a built-in stream handler if the user didn't supply one
if (protocol.equals("file")) {
streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
try {
String name = "com.android.okhttp.HttpHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("https")) { //从这里可以看到这个streamHandler是HttpsHandler
try {
String name = "com.android.okhttp.HttpsHandler";
streamHandler = (URLStreamHandler) Class.forName(name).newInstance();
} catch (Exception e) {
throw new AssertionError(e);
}
} else if (protocol.equals("jar")) {
streamHandler = new JarHandler();
}
if (streamHandler != null) {
streamHandlers.put(protocol, streamHandler);
}
}
原来streamHandler是HttpsHandler的对象。继续看HttpsHandler的openConnection()方法。
/external/okhttp/android/main/java/com/squareup/okhttp/HttpsHandler.java
public final class HttpsHandler extends HttpHandler { ...... }
HttpsHandler 里面没有openConnection()方法,接着找他的父类HttpHandler。
public class HttpHandler extends URLStreamHandler {
......
@Override protected URLConnection openConnection(URL url) throws IOException {
return newOkUrlFactory(null /* proxy */).open(url);
}
...
protected OkUrlFactory newOkUrlFactory(Proxy proxy) {
OkUrlFactory okUrlFactory = createHttpOkUrlFactory(proxy);
// For HttpURLConnections created through java.net.URL Android uses a connection pool that
// is aware when the default network changes so that pooled connections are not re-used when
// the default network changes.
okUrlFactory.client().setConnectionPool(configAwareConnectionPool.get());
return okUrlFactory;
}
newOkUrlFactory()方法返回了OkUrlFactory对象。我们看OkUrlFactory的open()方法。
/external/okhttp/okhttp-urlconnection/src/main/java/com/squareup/okhttp/OkUrlFactory.java
public final class OkUrlFactory implements URLStreamHandlerFactory, Cloneable {
...
public HttpURLConnection open(URL url) {
return open(url, client.getProxy());
}
HttpURLConnection open(URL url, Proxy proxy) {
String protocol = url.getProtocol();
OkHttpClient copy = client.copyWithDefaults();
copy.setProxy(proxy);
if (protocol.equals("http")) return new HttpURLConnectionImpl(url, copy);
if (protocol.equals("https")) return new HttpsURLConnectionImpl(url, copy);
throw new IllegalArgumentException("Unexpected protocol: " + protocol);
}
...
}
终于,我们看到了HttpURLConnection在我们程序里的实现类HttpsURLConnectionImpl。
总结下:
URLConnection是抽象类,里面定义了抽象方法connect()。HttpURLConnection也是抽象类,虽然继承了URLConnection,但是并没有实现connect()方法。URLConnection有三个子类,它们都是抽象类。HttpURLConnection,HttpsURLConnection,JarURLConnection。
HttpsURLConnectionImpl中没有connect方法,去父类DelegatingHttpsURLConnection中看看。
/external/okhttp/okhttp-urlconnection/src/main/java/com/squareup/okhttp/internal/huc/HttpsURLConnectionImpl.java
public final class HttpsURLConnectionImpl extends DelegatingHttpsURLConnection {
...
//这个构造器,正是OkUrlFactory中使用的
public HttpsURLConnectionImpl(URL url, OkHttpClient client) {
//注意这里传递的是HttpURLConnectionImpl
//不是HttpsURLConnectionImpl
this(new HttpURLConnectionImpl(url, client));
}
public HttpsURLConnectionImpl(HttpURLConnectionImpl delegate) {
super(delegate); //这里调用了父类的构造器
this.delegate = delegate;
}
}
/external/okhttp/okhttp-urlconnection/src/main/java/com/squareup/okhttp/internal/huc/DelegatingHttpsURLConnection.java
abstract class DelegatingHttpsURLConnection extends HttpsURLConnection {
private final HttpURLConnection delegate;
public DelegatingHttpsURLConnection(HttpURLConnection delegate) {
super(delegate.getURL());
this.delegate = delegate;
}
......
@Override public void connect() throws IOException {
connected = true;
delegate.connect();
}
......
}
注意这里的delegate是HttpURLConnection对象,不是HttpsURLConnection对象。上面已经分析了,HttpURLConnection没有connect()方法。那这个delegate又是哪个类的对象?注意到delegate有private final
修饰,所以只可能在DelegatingHttpsURLConnection类中赋值,结合上面源码,就是在构造器中赋值的。看上面HttpsURLConnectionImpl的源码,可知这个delegate其实是HttpURLConnectionImpl对象。
所以,delegate.connect();
最终调用的是HttpURLConnectionImpl的connect()方法。真的能绕啊!
/external/okhttp/okhttp-urlconnection/src/main/java/com/squareup/okhttp/internal/huc/HttpURLConnectionImpl.java
public class HttpURLConnectionImpl extends HttpURLConnection {
...
@Override public final void connect() throws IOException {
initHttpEngine();
boolean success;
do {
success = execute(false);
} while (!success);
}
...
}
后面就是execute()方法了,有时间再分析。可参考HttpURLConnection源码解读