Android HttpUrlConnection w 持久连接
Android HttpUrlConnection w Persistent Connection
场景
用户必须使用此应用登录服务器。稍后在应用程序中,我们需要根据服务器检查他们的登录状态。我们过去常常通过跟踪最后使用的 HttpClient
来做到这一点。最近我们切换到 HttpUrlConnection
但所谓的 持久连接 不起作用。
问题
我编写了这个测试应用程序来查看连接是否持久。我从两个 URL 返回 xml,但连接表现不一致。我怎样才能让它工作?
注意:如果您在浏览器中转到 登录 url,然后转到 GetUserInfo
url,一切都会按预期进行在同一个浏览器中。
MainActivity.java
package com.mediajackagency.test;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
public Button signInBtn = null;
public Button getUserInfoBtn = null;
public TextView xmlTextView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
this.xmlTextView = (TextView)findViewById(R.id.xmlTxtView);
this.signInBtn = (Button)findViewById(R.id.signInBtn);
this.signInBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AsyncTask task = new AsyncTask() {
@Override
protected Object doInBackground(Object[] params) {
String xml = loadUrl("https://www.fake.site/Login?userName=test&password=pass123");
return xml;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
xmlTextView.setText(o.toString());
}
};
task.execute();
}
});
this.getUserInfoBtn = (Button)findViewById(R.id.getUserInfoBtn);
this.getUserInfoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AsyncTask task = new AsyncTask() {
@Override
protected Object doInBackground(Object[] params) {
String xml = loadUrl("https://www.fake.site/GetCurrentUser");
return xml;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
xmlTextView.setText(o.toString());
}
};
task.execute();
}
});
}
public String loadUrl(String url) {
URI uri = MainActivity.encodeUrl(url);
Log.i("XMLParser", "Get URL: " + url);
String xml = null;
URL link;
BufferedReader reader = null;
StringBuilder stringBuilder = null;
InputStream is = null;
HttpURLConnection connection = null;
try {
link = new URL(url);
connection = (HttpURLConnection) link.openConnection();
connection.setRequestMethod("GET");
connection.connect();
is = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(is));
stringBuilder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
stringBuilder.append(line + "\r");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
if(reader != null) reader.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
if(is != null) is.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
if(connection != null) connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
try {
xml = stringBuilder.toString();
} catch(Exception e) {
e.printStackTrace();
}
return xml;
}
public static URI encodeUrl(String url) {
URL urlObject;
URI uri = null;
try {
urlObject = new URL(url);
uri = new URI(urlObject.getProtocol(), urlObject.getUserInfo(), urlObject.getHost(),
urlObject.getPort(), urlObject.getPath(), urlObject.getQuery(), urlObject.getRef());
} catch (Exception e) {
e.printStackTrace();
}
return uri;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
用 Cookie 解决
经过数天/数小时的工作后,我发现与 HttpUrlConnection
的持久连接 (http.keepalive
) 并不完全是:
1) 您需要确保 InputStream
和 HttpUrlConnection
已关闭,然后才能重新使用连接,即使如此 也可能并非总是如此可重复使用。
2) 打开的 TCP 连接可能会占用大量资源。
在找到并测试 的想法后,我决定走那条路,因为它从根本上说比我最初的想法更合理,性能也更好。
场景
用户必须使用此应用登录服务器。稍后在应用程序中,我们需要根据服务器检查他们的登录状态。我们过去常常通过跟踪最后使用的 HttpClient
来做到这一点。最近我们切换到 HttpUrlConnection
但所谓的 持久连接 不起作用。
问题
我编写了这个测试应用程序来查看连接是否持久。我从两个 URL 返回 xml,但连接表现不一致。我怎样才能让它工作?
注意:如果您在浏览器中转到 登录 url,然后转到 GetUserInfo
url,一切都会按预期进行在同一个浏览器中。
MainActivity.java
package com.mediajackagency.test;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
public Button signInBtn = null;
public Button getUserInfoBtn = null;
public TextView xmlTextView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
this.xmlTextView = (TextView)findViewById(R.id.xmlTxtView);
this.signInBtn = (Button)findViewById(R.id.signInBtn);
this.signInBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AsyncTask task = new AsyncTask() {
@Override
protected Object doInBackground(Object[] params) {
String xml = loadUrl("https://www.fake.site/Login?userName=test&password=pass123");
return xml;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
xmlTextView.setText(o.toString());
}
};
task.execute();
}
});
this.getUserInfoBtn = (Button)findViewById(R.id.getUserInfoBtn);
this.getUserInfoBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AsyncTask task = new AsyncTask() {
@Override
protected Object doInBackground(Object[] params) {
String xml = loadUrl("https://www.fake.site/GetCurrentUser");
return xml;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
xmlTextView.setText(o.toString());
}
};
task.execute();
}
});
}
public String loadUrl(String url) {
URI uri = MainActivity.encodeUrl(url);
Log.i("XMLParser", "Get URL: " + url);
String xml = null;
URL link;
BufferedReader reader = null;
StringBuilder stringBuilder = null;
InputStream is = null;
HttpURLConnection connection = null;
try {
link = new URL(url);
connection = (HttpURLConnection) link.openConnection();
connection.setRequestMethod("GET");
connection.connect();
is = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(is));
stringBuilder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
stringBuilder.append(line + "\r");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
if(reader != null) reader.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
if(is != null) is.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
if(connection != null) connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
try {
xml = stringBuilder.toString();
} catch(Exception e) {
e.printStackTrace();
}
return xml;
}
public static URI encodeUrl(String url) {
URL urlObject;
URI uri = null;
try {
urlObject = new URL(url);
uri = new URI(urlObject.getProtocol(), urlObject.getUserInfo(), urlObject.getHost(),
urlObject.getPort(), urlObject.getPath(), urlObject.getQuery(), urlObject.getRef());
} catch (Exception e) {
e.printStackTrace();
}
return uri;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
用 Cookie 解决
经过数天/数小时的工作后,我发现与 HttpUrlConnection
的持久连接 (http.keepalive
) 并不完全是:
1) 您需要确保 InputStream
和 HttpUrlConnection
已关闭,然后才能重新使用连接,即使如此 也可能并非总是如此可重复使用。
2) 打开的 TCP 连接可能会占用大量资源。
在找到并测试 的想法后,我决定走那条路,因为它从根本上说比我最初的想法更合理,性能也更好。