使用 HttpClient 时跨 Activity 使用 Cookie
我正在尝试一个简单的应用程序来读取网站的 HTML,并将其转换为在 Android 中创建一个易于阅读的 UI(这是学习 android 的练习,而不是制作一个可用的应用程序)。我遇到的问题是在 Activity 之间保留用户会话,然后在调用后在 HttpClient
中使用该会话。
我想“正确”地做到这一点,推荐的方法似乎是使用 CookieManager 。然而我遇到了问题 - 我似乎找不到从 CookieManager
获取 Cookie
并在以后的 实例化中使用它的“正确”方法>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) {...}
}
下面是决定是让用户登录还是转到索引的启动活动。您可以从这段代码中看到 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
on < code>execute(getMethod) 没有使用 Cookie(在调试中仔细检查)来拉回页面。如果有人能填补我的知识空白,那就太好了。
提前致谢。
编辑
当注释代码添加回来时(将 httpClient.execute(getMethod)
方法更改为 httpClient.execute(getMethod, localContext)
)生成此跟踪跟踪 - 假设是因为我使用 Cookie
String
而不是填充属性 ClientContext.COOKIE_STORE
CookieStore:
*org.apache.http.client.protocol.RequestAddCookies.process(RequestAddCookies.java:88), org.apache.http.protocol.BasicHttpProcessor.process(BasicHttpProcessor.java:290), org.apache.http.protocol.HttpRequestExecutor.preProcess(HttpRequestExecutor.java:160), org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:401)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555), org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487),
com.testapp.site.name.IndexAction.onCreate(IndexAction.java:47),
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047),
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611),
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663),
android.app.ActivityThread.access$1500(ActivityThread.java:117),
android.app.ActivityThread$H.handleMessage(ActivityThread.java:931),
android.os.Handler.dispatchMessage(Handler.java:99),
android.os.Looper.loop(Looper.java:123),
android.app.ActivityThread.main(ActivityThread.java:3683),
java.lang.reflect.Method.invokeNative(Native Method),
java.lang.reflect.Method.invoke(Method.java:507),
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839),
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597),
dalvik.system.NativeStart.main(Native Method)*
I'm trying a simple app to read in the HTML of a website, and tranform it to create an easily readable UI in Android (This is an exersize in learning android, not to make a useable app). The problem I'm having is persisting a users session across Activities and then using the session in a HttpClient
once recalled.
I would like to do this "Correctly", the recommended approach seem to be to use CookieManager
. I've had problems with this however - I cannot seem to find the "Correct" way to take a Cookie
from the CookieManager
and use it in a later instantiation of HttpClient
in a seperate Activities.
When using a CookieManager
I can save the Cookie
and the Cookie
is then in scope in other Activities (See code snippet 2). I haven't found how to use this later (See code snippet 3) when requesting a page.
Enough talking, here is some code. First my login action and Cookie storage:
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) {...}
}
The startup Activity which decides wether to make the user login or go to the index is below. You can see from this code that the cookie is in scope and can be read:
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);
}
}
But from my code to read the Index page I'm hoping you can suggest what i'm missing:
@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) {...}
}
The HttpClient
on execute(getMethod)
isn't using the Cookie (double checked this in debug) to pull back the page. It would be great if someone could fill this hole in my knowledge.
Thanks in advance.
EDIT
When commented code is added back in (with the httpClient.execute(getMethod)
method change to httpClient.execute(getMethod, localContext)
) this strack trace is produced - Assumedly because i'm filling the attribute ClientContext.COOKIE_STORE
with a Cookie
String
rather than a CookieStore
:
*org.apache.http.client.protocol.RequestAddCookies.process(RequestAddCookies.java:88), org.apache.http.protocol.BasicHttpProcessor.process(BasicHttpProcessor.java:290), org.apache.http.protocol.HttpRequestExecutor.preProcess(HttpRequestExecutor.java:160), org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:401)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555), org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487),
com.testapp.site.name.IndexAction.onCreate(IndexAction.java:47),
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047),
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611),
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663),
android.app.ActivityThread.access$1500(ActivityThread.java:117),
android.app.ActivityThread$H.handleMessage(ActivityThread.java:931),
android.os.Handler.dispatchMessage(Handler.java:99),
android.os.Looper.loop(Looper.java:123),
android.app.ActivityThread.main(ActivityThread.java:3683),
java.lang.reflect.Method.invokeNative(Native Method),
java.lang.reflect.Method.invoke(Method.java:507),
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839),
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597),
dalvik.system.NativeStart.main(Native Method)*
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我也有同样的问题。由于我使用的是 Volley 而不是直接使用 HTTPClient,HTTPClient 的单例似乎不是正确的解决方案。但使用同样的想法,我只是将 CookieManager 保存在我的应用程序单例中。
因此,如果 app 是我的应用程序单例,那么在每个活动的 onCreate() 中,我添加以下内容:
为了使这一点更好,我的所有活动都是 MyAppActivity 的子类,因此我所要做的就是将其放在 MyAppActivity 的 onCreate() 中,然后我的所有活动都继承了这个功能。现在我的所有活动都使用相同的 cookie 管理器,并且我的 Web 服务会话由所有活动共享。简单而且效果很好。
I had the same problem. Since I am using Volley and not HTTPClient directly the Singleton for HTTPClient didn't seem like the correct solution. But using that same idea I simply saved the CookieManager in my application singleton.
So if app is my application singleton then in the onCreate() of each activity I add this:
To make this even better, all of my activities are subclasses of MyAppActivity so all I have to do is put it in the onCreate for MyAppActivity and then all of my activities inherit this functionality. Now all my activities are using the same cookie manager and my web service session is shared by all the activities. Simple and it works great.
我想最好的事情之一是将单例模式应用于您的 HTTPClient,这样您就只有一个实例!
I suppose one of best thing is apply Singleton Pattern to your HTTPClient so you just have only one instance!
cookie 存储的一个好的解决方案是遵循 ThreadLocal 模式,以避免随机存储 cookie。
这将有助于保持唯一的 CookieStore。有关此模式的更多说明,您可以点击此有用的链接。
http://veerasundar. com/blog/2010/11/java-thread-local-how-to-use-and-code-sample/
干杯
A good solution for the cookie storage would be to follow the ThreadLocal pattern in order to avoid storing cookies randomly.
This will help keeping a one and only CookieStore. For more explanation of this pattern, you can follow this useful link.
http://veerasundar.com/blog/2010/11/java-thread-local-how-to-use-and-code-sample/
Cheers
CookieManager
由 Java 的内部 HTTP 客户端使用。它与 Apache HttpClient 无关。在您的代码中,您总是为每个请求创建一个新的
HttpClient
实例,因此创建一个新的CookieStore
实例,显然,该实例会立即与存储在其中的所有 cookie 一起被垃圾收集。HttpClient
实例超出范围。您应该
(1) 对于所有逻辑相关的 HTTP 请求重复使用
HttpClient
的相同实例,并在所有逻辑相关的线程之间共享它(这是推荐的使用方式) Apache HttpClient)(2) 或者至少在逻辑相关的线程之间共享
CookieStore
的相同实例(3) 或者,如果您坚持使用
CookieManager
来存储您的所有 cookie,创建由CookieManager
支持的自定义CookieStore
实现CookieManager
is used by the Java's internal HTTP client. It has nothing to do with Apache HttpClient.In your code you always create for each request a new instance of
HttpClient
and therefore a newCookieStore
instance, which obviously gets garbage collected along with all cookies stored in as soon as thatHttpClient
instance goes out of scope.You should either
(1) Re-use the same instance of
HttpClient
for all logically related HTTP requests and share it between all logically related threads (which is the recommended way of using Apache HttpClient)(2) or, at the very least, share the same instance of
CookieStore
between logically related threads(3) or, if you insist on using
CookieManager
to store all your cookies, create a customCookieStore
implementation backed byCookieManager
(正如所承诺的解决方案。我仍然不喜欢它,并且觉得我错过了执行此操作的“正确”方法,但是它有效。)
您可以使用
CookieManager
使用以下代码注册您的 cookie(从而使这些 cookie 在应用程序之间可用):将 cookie 保存到 CookieManager 中:
检查指定域上的 cookie:
if(CookieManager.getInstance().getCookie(URI_FOR_DOMAIN)
重建 HttpClient 的值:
(As promised a solution to this. I still don't like it and feel like I'm missing out on the "Correct" way of doing this but, it works.)
You can use the
CookieManager
to register your cookies (and therefore make these cookies available between apps) with the following code:Saving cookies into the
CookieManager
:Checking for cookies on specified domain:
if(CookieManager.getInstance().getCookie(URI_FOR_DOMAIN)
To reconstruct values for HttpClient:
在我的应用程序服务器中想要使用具有相同 cookie 的相同会话...
经过几个小时的“谷歌搜索”和痛苦的头痛之后,我只是将 cookie 保存到 SharedPreference 中,或者只是放入一些对象并再次使用相同的 cookie 设置 DefaultHttpClient ... onDestroy 只是删除 SharedPreferences ... 这就是全部:
SerializedCookie
看下面的例子:
// 两种方法保存并加载cookie...
Google Luck
In my application server wants to use same session with same cookies...
After few hours of "googling" and painful headache I just saved cookies to SharedPreference or just put in some object and set DefaultHttpClient with same cookies again ... onDestroy just remove SharedPreferences ... that's all:
SerializableCookie
Look the following example:
// two methods to save and load cookies ...
Google Luck