使用HttpClient时在活动中使用Cookie

时间:2011-04-27 10:27:02

标签: java android cookies httpclient

我正在尝试一个简单的应用程序来阅读网站的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

6 个答案:

答案 0 :(得分:23)

Java的内部HTTP客户端使用

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 ...这就是全部:

  1. 首先将SerializableCookie类复制到您的包中: SerializableCookie
  2. 查看以下示例:

    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/

干杯