Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Cookies across Activities when using HttpClient

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)* 
like image 279
Graeme Avatar asked Apr 27 '11 10:04

Graeme


People also ask

Do we need to close HttpClient connection?

You do not need to explicitly close the HttpClient, however, (you may be doing this already but worth noting) you should ensure that connections are released after method execution. Edit: The ClientConnectionManager within the HttpClient is going to be responsible for maintaining the state of connections.

Which class stores the available cookies for the HttpClient class?

You can get the cookies added to a cookie store using getCookies() method of the asicCookieStore class.

What is closeable HttpClient?

CloseableHttpClient is the base class of the httpclient library, the one all implementations use. Other subclasses are for the most part deprecated. The HttpClient is an interface for this class and other classes. You should then use the CloseableHttpClient in your code, and create it using the HttpClientBuilder .


2 Answers

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 new CookieStore instance, which obviously gets garbage collected along with all cookies stored in as soon as that HttpClient 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 custom CookieStore implementation backed by CookieManager

like image 67
ok2c Avatar answered Sep 22 '22 22:09

ok2c


(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:

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(); 

Checking for cookies on specified domain: if(CookieManager.getInstance().getCookie(URI_FOR_DOMAIN)

To reconstruct values for 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)); } 
like image 27
Graeme Avatar answered Sep 21 '22 22:09

Graeme