Использование файлов cookie в действии при использовании HttpClient

Я пытаюсь простое приложение читать в HTML-сайте и транстротировать его для создания легко читаемого пользовательского интерфейса в Android (это проявляется в обучении Android, а не в полезном приложении). Проблема, с которой я сталкиваюсь, заключается в сохранении сеанса пользователей через действия и затем использовании сеанса в 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) {...}
}

Активность запуска, которая решает, чтобы войти в систему пользователя или перейти к индексу, приведен ниже. Вы можете видеть из этого кода, что файл 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 execute(getMethod) не использует Cookie (дважды проверял это при отладке), чтобы оттянуть страницу. Было бы здорово, если бы кто-то мог заполнить эту дыру, насколько мне известно.

Спасибо заранее.

ИЗМЕНИТЬ

Когда код комментария добавлен обратно (с изменением метода httpClient.execute(getMethod) на httpClient.execute(getMethod, localContext)), создается эта трассировка strack - предположительно, потому что я заполняю атрибут ClientContext.COOKIE_STORE Cookie String, а не 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)*

Ответ 1

(Как обещал это решение. Мне все еще не нравится и чувствую, что я упускаю "правильный" способ сделать это, но это работает.)

Вы можете использовать CookieManager для регистрации ваших файлов 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

CookieManager используется внутренним HTTP-клиентом Java. Он не имеет ничего общего с Apache HttpClient.

В вашем коде вы всегда создаете для каждого запроса новый экземпляр HttpClient и, следовательно, новый экземпляр CookieStore, который, очевидно, получает мусор, собранный вместе со всеми куками, хранящимися, как только этот экземпляр HttpClient выходит из объем.

Вы должны либо

(1) Повторно использовать один и тот же экземпляр HttpClient для всех логически связанных HTTP-запросов и поделиться им между всеми логически связанными потоками (который является рекомендуемым способом использования Apache HttpClient)

(2) или, по крайней мере, совместно использовать один и тот же экземпляр CookieStore между логически связанными потоками

(3) или, если вы настаиваете на использовании CookieManager для хранения всех ваших файлов cookie, создать пользовательскую реализацию CookieStore, поддерживаемую CookieManager

Ответ 3

В моем приложении сервер хочет использовать тот же сеанс с одинаковыми кукисами... После нескольких часов "гуглинга" и болезненной головной боли я только что сохранил файлы cookie в SharedPreference или просто поместил в какой-то объект и установил DefaultHttpClient с тем же самым куки снова... onDestroy просто удалит SharedPreferences..., что все:

  • Первый экземпляр класса SerializableCookie для вашего пакета: SerializableCookie

Посмотрите следующий пример:

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

Ответ 4

У меня была та же проблема. Поскольку я использую Volley, а не HTTPClient напрямую, Singleton для HTTPClient не похоже на правильное решение. Но используя ту же самую идею, я просто сохранил CookieManager в своем приложении singleton. Поэтому, если приложение является моим приложением singleton, то в onCreate() каждого действия я добавляю следующее:

    if (app.cookieManager == null) {
        app.cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
    }
    CookieHandler.setDefault(app.cookieManager);

Чтобы сделать это еще лучше, все мои действия являются подклассами MyAppActivity, поэтому все, что мне нужно сделать, это поместить его в onCreate для MyAppActivity, а затем все мои действия наследуют эту функциональность. Теперь все мои действия используют один и тот же менеджер файлов cookie, и мой сеанс веб-службы разделяет все действия. Просто и отлично работает.

Ответ 5

Я полагаю, что одним из лучших является применение 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;
    }
}

Ответ 6

Хорошим решением для хранилища файлов cookie должно следовать шаблон ThreadLocal, чтобы избежать случайного хранения файлов cookie.

Это поможет сохранить один и только CookieStore. Для более подробного объяснения этого шаблона вы можете следовать этой полезной ссылке.

http://veerasundar.com/blog/2010/11/java-thread-local-how-to-use-and-code-sample/

Приветствия