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

Android网络编程之HttpUrlConnection

2016-10-21 11:07 381 查看
相关知识点延伸:

JAVASE中HttpUrlConnection介绍和使用

Android Thread Pool(线程池)

上一篇介绍JavaSe中HttpUrlConnection使用情况,在Android中使用HttpUrlConnection还是存在一些差别的。

Android中HttpUrlConnection的使用:

1. 使用说明:

Secure Communication with HTTPS(使用HTTPS进行安全通信):

使用一个https方案的URL调用penConnection(),将会返回一个HttpsURLConnection,这允许覆盖默认的HostnameVerifier 和 SSLSocketFactory。

一个运用提供的SSLSocketFactory创建是通过一个SSLContext,
SSLSocketFactory 能够提供自定义的用于验证证书链的X509TrustManager
和自定义的用于提供客户端证书的X509TrustManager 。


Response Handling(相应处理):

HttpURLConnection 最多可以跟踪5个Http重定向。它将会跟随重定向,从一个起始服务器到其他服务器。
这种重定向的跟随实现,不会支持从Https服务器跳转到http的服务器重定向。反过来,也是一样的。


Posting Content(传递内容):

1. 上传数据到web服务器 ,输出的链接需配置setDoOutput(true)。
2. 为了最佳性能。
2. 1 若是知道这内容长度,可以调用setFixedLengthStreamingMode(int) 。
反之,调用setChunkedStreamingMode(int)。
2. 2 若是没有进行上一步设置操作,HttpURLConnection将会在内存中强制缓存整个请求的body,直到被传输完。
这种后果是导致浪费(可能消耗完)堆,增加迟缓时间。


Performance(性能):

内存性能:

这类返回的输出流和输入流不进行缓冲。大多数开发者应该使用BufferedOutputStream或者BufferedInputStream进行缓存。使用批量读取或者写入操作的使用者可以忽略缓冲。

传输大量数据到服务器或者从服务器中读取大量数据时,使用流的操作,同时应该进行限制内存中数据量的操作。

除非你需要一次性将内容存储到内存中,将它作为流处理,而不是将整个内容作为单独一个btye数据或者单独一个String进行存储。

处理时间:

为了减少延迟,这类进行多个请求/响应对的操作时,会重用相同的地城socket.因此,Http连接开着时间可能比所需时间,调用disconnect()可以将返回一个socket添加到连接的socket池中。

这种行为是可以被禁止的。通过设置http.keepAlive属性为false,在发送http请求前。

http.maxConnections属性可以用于控制与每个服务器保持多少个空闲连接。

默认情况:

默认情况下,HttpURLConnection 的实现请求,服务器将数据使用Gzip压缩后再返回,HttpUrlConnection会自动解压数据通过回调getInputStream().

在这种情况下,Content-Encoding 和Content-Length response headers 是清除的。

Gzip压缩可以被禁止。通过设置请求的header上接受的编码格式: urlConnection.setRequestProperty(“Accept-Encoding”, “identity”);

设置请求的标头上的Accept-Encoding属性禁止自动解压,且保留响应的标头。使用者必须根据响应的标头上的Content-Encoding来处理所需要的解压。

注意点:

getContentLength():返回传输的字节数,但是不能用于预测可以读出多少字节数。

getInputStream(): 获取压缩流。相反,读取这流直到耗尽,即read()返回-1.

Handling Network Sign-On:

某些wifi网络操作会阻止互联网访问,直到用户点击登录页面。

这种登录页面通常呈现是通过使用htttp重定向。可以使用getURL来测试连接是否意外重定向。

在连接操作的响应标头被接受后,这种测试才是有效的。可以调用getHeaderFields() or getInputStream()来触发响应的标头。这里的案例:检查响应是否意外重定向到其他主机:

HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
if (!url.getHost().equals(urlConnection.getURL().getHost())) {
// we were redirected! Kick the user out to the browser to sign on?
}
...
} finally {
urlConnection.disconnect();
}


HTTP Authentication(http验证):

HttpURLConnection支持 HTTP basic authentication。 使用 Authenticator 来设置 the VM-wide authentication handler:

Authenticator.setDefault(new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password.toCharArray());
}
});


除非与https配对,这种机制不是用户身份验证的安全机制。特别是,用户名,密码和请求和响应都是没有在加密情况下通过网络传输的。

Sessions with Cookies:

为了在客户端与服务器间建立和维护一个潜在的长期session,HttpURLConnection 包含一个可以扩展的coockie管理器。

使用CookieHandler 和CookieManager来开启VM范围中coockie 管理:

CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);


默认情况下,CookieManager只接受起始服务的coockie.还包括其他两个策略:ACCEPT_ALL and ACCEPT_NONE

实现CookiePolicy以定义自定义策略。

默认的CookieManager 会将接受到coockie保存到内存中,当VM退出时,会消除这些coockie. 实现CookieStore以定义自定义Cookie存储

除了由Http响应设置的Cookie之外,还可以通过编码方式来设置Cookie.要包含在Http 请求的标头中,Cookies必须要设置域和路径的属性

默认情况下,HttpCookie的新实例与只支持RFC 2965 cookies的服务器工作。许多web服务器仅支持旧的RFC 2109。

为了与大多数web服务器兼容,需将cookie版本设置为0。

一个案例:用法语来接收www.twitter.com

HttpCookie cookie = new HttpCookie("lang", "fr");
cookie.setDomain("twitter.com");
cookie.setPath("/");
cookie.setVersion(0);
cookieManager.getCookieStore().add(new URI("http://twitter.com/"), cookie);


HTTP Methods:

HttpURLConnection 默认的请求方式是get. 若是使用post请求,则应该调用setDoOutput(true) 。

改变请求方式(OPTIONS, HEAD, PUT, DELETE and TRACE),调用setRequestMethod(String)来设置.

Proxies(代理):

默认情况下,这类是直接连接起始服务器。它也能通过http或者socks代理连接。

要使用代理,在创建连接的时候调用URL.openConnection(Proxy)。

IPv6 Support:

这类包含IPv6的支持。对于具备IPv4和IPv6地址的主机,它将会尝试连接到每一个主机的地址,知道建立连接。

Avoiding Bugs In Earlier Releases:

在android 2.2之前,这里存在一些错误。特别是,在一个可读的InputSteam中调用clos()会危害连接池,应该禁止使用连接池来解决问题。

private void disableConnectionReuseIfNecessary() {
// Work around pre-Froyo bugs in HTTP connection reuse.
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
System.setProperty("http.keepAlive", "false");
}
}


每一个HttpURLConnection 实例可以用于一个请求/响应对。这个类的实例不是线程安全的。

android 官方关于HttpUrlConnection的链接:

https://developer.android.com/reference/java/net/HttpURLConnection.html

2. 其API和使用方式:请阅读JAVASE中HttpUrlConnection介绍和使用

3. 异步线程中使用案例:

在IntentService中长期执行网络任务,周期性读取服务器数据的案例。

案例思路: 周期性从服务器获取到信息,然后Gson解析Json数据,手机震动,Notification提醒,最后编辑信息标记为已读。

public class MessageIntentSerce extends IntentService {
public static final String TAG = MessageIntentSerce.class.getSimpleName();
private String token;
private String url1, url2;
public boolean isStop = false;
public NotificationManager notificationManager;
public Vibrator vibrator;
public static final int MESSAGENOTIFICATION_ID = 100;

public static Intent openMessageServce(Context context, Bundle bundle) {
Intent intent = new Intent(context, MessageIntentSerce.class);
intent.putExtras(bundle);
return intent;
}

private ThreadManager threadManager;

public MessageIntentSerce() {
super(TAG);
url1 = Constants.COOMPANYTEST + Constants.UPDATAMESSAG;
url2 = Constants.COOMPANYTEST + Constants.MESSAGEFINISH;
threadManager = ThreadManager.getInstances();
}

@Override
public void onCreate() {
super.onCreate();
LogController.i(TAG,"service onCreate");
//初始化相关配置
notificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
vibrator = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);
}

/**
* 后台线程,以工作队列线程进行
*
* @param intent
*/
@Override
protected void onHandleIntent(Intent intent) {
LogController.i(TAG,"service onHandleIntent");
Bundle bundle = intent.getExtras();
token = bundle.getString(MessageIntentSerce.TAG);
if (token == null) {
return;
}
//停止的标志
while (threadManager.getStart()) {
try {
getMessageFromServce();
Thread.sleep(20 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}

}

@Override//释放资源
public void onDestroy() {
LogController.i(TAG,"service destroy");
notificationManager.cancelAll();

vibrator.cancel();
super.onDestroy();
}

public void getMessageFromServce() {
try {
HttpURLConnection urlConnection = createConnection(url1);
String result = getStreamContent(urlConnection);
LogController.i(TAG, "messagelist" + result);
if (result == null) {
return;
}
JsonBean_UpdataMessage message = parseJson(result, JsonBean_UpdataMessage.class);
if (message != null && message.errcode == 0) {
if (message.replydata != null && message.replydata.messagelist != null
&& message.replydata.messagelist.size() > 0) {

for (JsonBean_UpdataMessage.MessageList.Message msg :
message.replydata.messagelist) {

notifi(msg.msubject, msg.mcontent);
updataMSG(msg.mpid);

}
vibrator.vibrate(1000);
}

}
} catch (Exception e) {
e.printStackTrace();
}

}

/**
* 创建httpUrlConnection对象,指定request的属性
*
* @param url
* @return
*/
public HttpURLConnection createConnection(String url) {
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setConnectTimeout(10 * 1000);
urlConnection.setReadTimeout(10 * 1000);
//数据编码格式,这里utf-8
urlConnection.setRequestProperty("Charset", "utf-8");
//添加cookie,cookie规则需开发者自定
urlConnection.setRequestProperty("Cookie", Constants.COOCKIE_KEY + token);
//设置返回结果的类型,这里是json
urlConnection.setRequestProperty("accept", "application/json");
//这里设置post传递的内容类型,这里json
urlConnection.setRequestProperty("Content-Type", "application/json");
//设置这个,避免android低版本报错误
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
System.setProperty("http.keepAlive", "false");
}

} catch (Exception e) {
e.printStackTrace();
}
return urlConnection;
}

/**
* 添加http参数
*
* @param urlConnection
* @param jsonObject
*/
public void addParameter(HttpURLConnection urlConnection, JSONObject jsonObject) {
try {
//设置post请求方式
urlConnection.setRequestMethod("POST");
//允许往流中写入数据
urlConnection.setDoOutput(true);
//借助BufferedOutputStream提高速度
OutputStream outputStream = new BufferedOutputStream(urlConnection.getOutputStream());
byte[] body = jsonObject.toString().getBytes("utf-8");
outputStream.write(body, 0, body.length);
//刷新数据到流中
outputStream.flush();
//关闭流
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取HttpUrlConnection的返回内容
*
* @param urlConnection
* @return
*/
public String getStreamContent(HttpURLConnection urlConnection) {
ByteArrayOutputStream byteArrayOutputStream = null;
BufferedInputStream bufferedInputStream = null;
String result = null;
try {
//开启客户端与Url所指向的资源的网络连接
urlConnection.connect();
if (200 == urlConnection.getResponseCode()) {//HTTP_OK 即200,连接成功的状态码
if (urlConnection.getContentLength() > 0) {
bufferedInputStream = new BufferedInputStream(urlConnection.getInputStream());
byteArrayOutputStream = new ByteArrayOutputStream();
//httpUrlConnection返回传输字节的长度,创建一个byte 数组。
byte[] b = new byte[urlConnection.getContentLength()];
int length;
while ((length = bufferedInputStream.read(b)) > 0) {
byteArrayOutputStream.write(b, 0, length);
}
result = byteArrayOutputStream.toString("utf-8");
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (byteArrayOutputStream != null) {
byteArrayOutputStream.close();
}
if(bufferedInputStream!=null){
bufferedInputStream.close();
}
if (urlConnection != null) {
//解除连接,释放网络资源
urlConnection.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}

}
return result;
}

/**
* 解析json数据,这里Gson解析
*
* @param json
* @param cls
* @param <T>
* @return
*/
public <T> T parseJson(String json, Class<T> cls) {//解析基本的json(json)
T t = null;
try {
Gson gson = new Gson();
t = gson.fromJson(json, cls);
return t;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public int i = 1;
//弹出Notification
public void notifi(String title, String content) {
notificationManager.notify(MESSAGENOTIFICATION_ID + (i++),
NotificationBuidler.createMessageNotifaction(MessageIntentSerce.this,
title, content));
}

public void updataMSG(int mid) {
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject();
jsonObject.put("mpid", mid);
HttpURLConnection httpURLConnection = createConnection(url2);
addParameter(httpURLConnection, jsonObject);
String result = getStreamContent(httpURLConnection);
LogController.i(TAG, "msgfinish" + result);
} catch (Exception e) {
e.printStackTrace();
}
}

}


4. Android模拟器连接本地电脑服务器的注意点:

在电脑模拟器上访问本地电脑服务器时,报错java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused

解决方式:

android模拟器是在虚拟内部运行,这里127.0.0.1或者localhost是模拟器自己的环回路径。
因此,应该使用http://10.0.2.2:8080/来访问。


StackOverFlow上的解决方式:

http://stackoverflow.com/questions/5495534/java-net-connectexception-localhost-127-0-0-18080-connection-refused
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: