我正在尝试一个简单的应用程序来阅读网站的HTML,并转换它以在Android中创建一个易于阅读的UI(这是一个学习android的exersize,而不是一个可用的应用程序)。我遇到的问题是在活动之间持久保存用户会话,然后在HttpClient
召回时使用会话。
我想“正确地”这样做,推荐的方法似乎是使用CookieManager
。我遇到了这方面的问题 - 我似乎找不到从Cookie
获取CookieManager
的“正确”方法,并在以后的单独HttpClient
实例化中使用它活动。
使用CookieManager
时,我可以保存Cookie
,然后Cookie
在其他活动的范围内(请参阅代码段2)。在请求页面时,我还没有找到如何使用它(请参阅代码片段3)。
足够说话,这里有一些代码。首先是我的登录操作和Cookie存储:
private OnClickListener loginActionListener = new OnClickListener()
{
public void onClick(View v)
{
EditText usernameTextView = (EditText) findViewById(R.id.Username);
EditText passwordTextView = (EditText) findViewById(R.id.Password);
String username = usernameTextView.getText().toString();
String password = passwordTextView.getText().toString();
try {
HttpPost postMethod = new HttpPost(URI);
HttpParams params = new BasicHttpParams();
params.setParameter("mode", "login");
params.setParameter("autologin", true);
params.setParameter("username", username);
params.setParameter("password", password);
postMethod.setParams(params);
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(postMethod);
List<Cookie> cookies = httpClient.getCookieStore().getCookies();
if(cookies != null)
{
for(Cookie cookie : cookies)
{
String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain();
CookieManager.getInstance().setCookie(cookie.getDomain(), cookieString);
}
}
CookieSyncManager.getInstance().sync();
Intent intent = new Intent(v.getContext(), IndexAction.class);
startActivity(intent);
} catch (Exception e) {...}
}
启动Activity决定是否让用户登录或转到索引,如下所示。您可以从此代码中看到cookie在范围内并且可以阅读:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CookieSyncManager.createInstance(this);
if(CookieManager.getInstance().getCookie(URI) == null)
{
Intent intent = new Intent(this, LoginAction.class);
startActivity(intent);
}
else
{
Intent intent = new Intent(this, IndexAction.class);
startActivity(intent);
}
}
但是从我的代码中读取索引页面,我希望你能提出我所缺少的内容:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CookieSyncManager.createInstance(this);
try
{
HttpGet getMethod = new HttpGet(URI_INDEX);
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 30000);
HttpConnectionParams.setSoTimeout(params, 30000);
// This code results in a ClassCastException, I'm assuming i've found a red herring with this solution.
// HttpContext localContext = new BasicHttpContext();
// localContext.setAttribute(ClientContext.COOKIE_STORE, CookieManager.getInstance().getCookie(URI));
DefaultHttpClient httpClient = new DefaultHttpClient(params);
HttpResponse response = httpClient.execute(getMethod);
if(response.getStatusLine().getStatusCode() > 299 && response.getStatusLine().getStatusCode() < 400)
{
// Not logged in doesn't give a redirect response. Very annoying.
}
final char[] buffer = new char[0x10000];
StringBuilder out = new StringBuilder();
Reader in = new InputStreamReader(response.getEntity().getContent(), "UTF-8");
int read = 0;
while (read>=0)
{
read = in.read(buffer, 0, buffer.length);
if (read>0) {
out.append(buffer, 0, read);
}
}
String returnString = out.toString();
} catch (ClientProtocolException e) {...}
}
HttpClient
上的execute(getMethod)
未使用Cookie(在调试中仔细检查)以撤回页面。如果有人能够在我的知识中填补这个漏洞,那将是很棒的。
提前致谢。
修改
当评论代码重新添加时(httpClient.execute(getMethod)
方法更改为httpClient.execute(getMethod, localContext)
),会生成此strack跟踪 - 假设我正在使用{{填充属性ClientContext.COOKIE_STORE
1}} Cookie
而不是String
:
CookieStore
答案 0 :(得分:23)
CookieManager
。它与Apache HttpClient无关。
在你的代码中,你总是为每个请求创建一个HttpClient
的新实例,因此创建一个新的CookieStore
实例,这个实例显然会随着{{1实例超出范围。
你应该
(1)对所有逻辑相关的HTTP请求重用同一个HttpClient
实例,并在所有逻辑相关的线程之间共享(这是使用Apache HttpClient的推荐方法)
(2)或者至少在逻辑上相关的线程之间共享HttpClient
的相同实例
(3)或者,如果您坚持使用CookieStore
存储所有Cookie,创建由CookieManager
支持的自定义CookieStore
实施
答案 1 :(得分:10)
(正如承诺的解决方案。我仍然不喜欢它,并且觉得我错过了“正确”这样做的方式,但是,它有效。)
您可以使用CookieManager
使用以下代码注册Cookie(并因此在应用之间提供这些Cookie):
将Cookie保存到CookieManager
:
List<Cookie> cookies = httpClient.getCookieStore().getCookies();
if(cookies != null)
{
for(Cookie cookie : cookies)
{
String cookieString = cookie.getName() + "=" + cookie.getValue() + "; domain=" + cookie.getDomain();
CookieManager.getInstance().setCookie(cookie.getDomain(), cookieString);
}
}
CookieSyncManager.getInstance().sync();
检查指定域名的Cookie:
if(CookieManager.getInstance().getCookie(URI_FOR_DOMAIN)
重建HttpClient的值:
DefaultHttpClient httpClient = new DefaultHttpClient(params);
String[] keyValueSets = CookieManager.getInstance().getCookie(URI_FOR_DOMAIN).split(";");
for(String cookie : keyValueSets)
{
String[] keyValue = cookie.split("=");
String key = keyValue[0];
String value = "";
if(keyValue.length>1) value = keyValue[1];
httpClient.getCookieStore().addCookie(new BasicClientCookie(key, value));
}
答案 2 :(得分:4)
在我的应用服务器中想要使用相同的会话和相同的cookie ... 经过几个小时的“谷歌搜索”和痛苦的头痛我刚刚将cookie保存到SharedPreference或只是放入一些对象并再次使用相同的cookie设置DefaultHttpClient ... onDestroy只删除SharedPreferences ...这就是全部:
查看以下示例:
public class NodeServerTask extends AsyncTask<Void, Void, String> {
private DefaultHttpClient client;
protected String doInBackground(Void... params) {
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> urlParameters = new ArrayList<NameValuePair>();
urlParameters.add(nameValuePair);
try {
httpPost.setEntity(new UrlEncodedFormEntity(urlParameters));
List<Cookie> cookies = loadSharedPreferencesCookie();
if (cookies!=null){
CookieStore cookieStore = new BasicCookieStore();
for (int i=0; i<cookies.size(); i++)
cookieStore.addCookie(cookies.get(i));
client.setCookieStore(cookieStore);
}
HttpResponse response = client.execute(httpPost);
cookies = client.getCookieStore().getCookies();
saveSharedPreferencesCookies(cookies);
//两种保存和加载cookie的方法......
private void saveSharedPreferencesCookies(List<Cookie> cookies) {
SerializableCookie[] serializableCookies = new SerializableCookie[cookies.size()];
for (int i=0;i<cookies.size();i++){
SerializableCookie serializableCookie = new SerializableCookie(cookies.get(i));
serializableCookies[i] = serializableCookie;
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
ObjectOutputStream objectOutput;
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
try {
objectOutput = new ObjectOutputStream(arrayOutputStream);
objectOutput.writeObject(serializableCookies);
byte[] data = arrayOutputStream.toByteArray();
objectOutput.close();
arrayOutputStream.close();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Base64OutputStream b64 = new Base64OutputStream(out, Base64.DEFAULT);
b64.write(data);
b64.close();
out.close();
editor.putString("cookies", new String(out.toByteArray()));
editor.apply();
} catch (IOException e) {
e.printStackTrace();
}
}
private List<Cookie> loadSharedPreferencesCookie() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
byte[] bytes = preferences.getString("cookies", "{}").getBytes();
if (bytes.length == 0 || bytes.length==2)
return null;
ByteArrayInputStream byteArray = new ByteArrayInputStream(bytes);
Base64InputStream base64InputStream = new Base64InputStream(byteArray, Base64.DEFAULT);
ObjectInputStream in;
List<Cookie> cookies = new ArrayList<Cookie>();
SerializableCookie[] serializableCookies;
try {
in = new ObjectInputStream(base64InputStream);
serializableCookies = (SerializableCookie[]) in.readObject();
for (int i=0;i<serializableCookies.length; i++){
Cookie cookie = serializableCookies[i].getCookie();
cookies.add(cookie);
}
return cookies;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
Google Luck
答案 3 :(得分:1)
我遇到了同样的问题。由于我直接使用Volley而不是HTTPClient,因此用于HTTPClient的Singleton似乎不是正确的解决方案。但是使用相同的想法我只是在我的应用程序单例中保存了CookieManager。 因此,如果app是我的应用程序单例,那么在每个活动的onCreate()中我添加:
if (app.cookieManager == null) {
app.cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
}
CookieHandler.setDefault(app.cookieManager);
为了使这更好,我的所有活动都是MyAppActivity的子类,因此我所要做的就是将它放在onCreate for MyAppActivity中,然后我的所有活动都继承了这个功能。现在我的所有活动都使用相同的cookie管理器,所有活动都共享我的Web服务会话。简单而且效果很好。
答案 4 :(得分:0)
我认为最好的事情之一是将Singleton Pattern应用于您的HTTPClient,因此您只有一个实例!
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
public class myHttpClient {
private static HttpClient mClient;
private myHttpClient() {};
public static HttpClient getInstance() {
if(mClient==null)
mClient = new DefaultHttpClient();
return mClient;
}
}
答案 5 :(得分:-1)
cookie存储的一个好方法是遵循ThreadLocal模式,以避免随机存储cookie。
这将有助于保留一个唯一的CookieStore。有关此模式的更多说明,您可以按照此有用链接进行操作。
http://veerasundar.com/blog/2010/11/java-thread-local-how-to-use-and-code-sample/
干杯