Android HttpUrlConnection w 持久连接

Android HttpUrlConnection w Persistent Connection

场景

用户必须使用此应用登录服务器。稍后在应用程序中,我们需要根据服务器检查他们的登录状态。我们过去常常通过跟踪最后使用的 HttpClient 来做到这一点。最近我们切换到 HttpUrlConnection 但所谓的 持久连接 不起作用。

问题

我编写了这个测试应用程序来查看连接是否持久。我从两个 URL 返回 xml,但连接表现不一致。我怎样才能让它工作?

注意:如果您在浏览器中转到 登录 url,然后转到 GetUserInfo url,一切都会按预期进行在同一个浏览器中。

MainActivity.java

package com.mediajackagency.test;

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;

public class MainActivity extends AppCompatActivity {

    public Button signInBtn = null;
    public Button getUserInfoBtn = null;
    public TextView xmlTextView = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        this.xmlTextView = (TextView)findViewById(R.id.xmlTxtView);

        this.signInBtn = (Button)findViewById(R.id.signInBtn);
        this.signInBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AsyncTask task = new AsyncTask() {
                    @Override
                    protected Object doInBackground(Object[] params) {
                        String xml = loadUrl("https://www.fake.site/Login?userName=test&password=pass123");

                        return xml;
                    }

                    @Override
                    protected void onPostExecute(Object o) {
                        super.onPostExecute(o);
                        xmlTextView.setText(o.toString());
                    }
                };
                task.execute();
            }
        });

        this.getUserInfoBtn = (Button)findViewById(R.id.getUserInfoBtn);
        this.getUserInfoBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AsyncTask task = new AsyncTask() {
                    @Override
                    protected Object doInBackground(Object[] params) {
                        String xml = loadUrl("https://www.fake.site/GetCurrentUser");

                        return xml;
                    }

                    @Override
                    protected void onPostExecute(Object o) {
                        super.onPostExecute(o);
                        xmlTextView.setText(o.toString());
                    }
                };
                task.execute();
            }
        });
    }

    public String loadUrl(String url) {
        URI uri = MainActivity.encodeUrl(url);
        Log.i("XMLParser", "Get URL: " + url);

        String xml = null;
        URL link;
        BufferedReader reader = null;
        StringBuilder stringBuilder = null;
        InputStream is = null;
        HttpURLConnection connection = null;

        try {
            link = new URL(url);
            connection = (HttpURLConnection) link.openConnection();
            connection.setRequestMethod("GET");
            connection.connect();

            is = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(is));
            stringBuilder = new StringBuilder();

            String line = null;
            while ((line = reader.readLine()) != null)
            {
                stringBuilder.append(line + "\r");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    if(reader != null) reader.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                try {
                    if(is != null) is.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                try {
                    if(connection != null) connection.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        try {
            xml = stringBuilder.toString();
        } catch(Exception e) {
            e.printStackTrace();
        }
        return xml;
    }

    public static URI encodeUrl(String url) {
        URL urlObject;
        URI uri = null;
        try {
            urlObject = new URL(url);
            uri = new URI(urlObject.getProtocol(), urlObject.getUserInfo(), urlObject.getHost(),
                    urlObject.getPort(), urlObject.getPath(), urlObject.getQuery(), urlObject.getRef());

        } catch (Exception e) {
            e.printStackTrace();
        }
        return uri;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

用 Cookie 解决

经过数天/数小时的工作后,我发现与 HttpUrlConnection 的持久连接 (http.keepalive) 并不完全是:

1) 您需要确保 InputStreamHttpUrlConnection 已关闭,然后才能重新使用连接,即使如此 也可能并非总是如此可重复使用。

2) 打开的 TCP 连接可能会占用大量资源。

在找到并测试 的想法后,我决定走那条路,因为它从根本上说比我最初的想法更合理,性能也更好。