Android HTTPリクエストメソッド: HttpURLConnection


このセクションの紹介:

前の 2 つのセクションでは、HTTP プロトコルとプロトコル ヘッダーのいくつかの概念について説明しましたが、このセクションでは コードを積み上げていきます。このセクションでは、Android が提供する Http リクエスト メソッドの 1 つである HttpURLConnection について説明します。 これに加えて、別の種類の HttpClient があります。これについては次のセクションで説明します。ただし、リクエストが複雑になった場合は前者を使用できます。 これは非常に面倒で、後者は Java パケット キャプチャでよく使用されます。結局のところ、これは Google の息子ではなく、バージョン 4.4 からのものです。 HttpURLConnection は OkHttp! に置き換えられました。さて、時代の流れに合わせて、HttpClient の話をした後でこの話をすることにしました。 わかりました!ちなみに、実際の開発ではHttpURLConnectionとHttpClientは一般的には使用せず、他のものを使ってカプセル化します。 ネットワーク操作には Volley、android-async-http、loopj などの優れたサードパーティ ネットワーク リクエスト フレームワークが必要です。 非同期やマルチスレッドは自分でやると非常に面倒なので、実際の開発はサードパーティを直接使うべきです! !もちろん私も勉強します 結局のところ、サードパーティもこれらの基盤に基づいて構築されており、高品質のアーキテクチャとさまざまな最適化が施されているため、問題ありません。さて、早速始めましょう このセクションの内容!


1. HttpURLConnection の概要

回答: HTTP 操作に使用でき、ほとんどのアプリケーションに適した多目的の軽量 HTTP クライアントです。 HttpURLConnection が提供する API は比較的シンプルですが、これにより使いやすくなります。 使って拡張してください。抽象クラスである URLConnection を継承し、オブジェクトを直接インスタンス化することはできません。 openCollection() を呼び出すことにより このメソッドは、デフォルトで gzip で圧縮されたオブジェクト インスタンスを取得します。


2. HttpURLConnection を使用する手順は次のとおりです。
  • URL オブジェクトを作成します: URL url = 新しい URL(http://www.baidu.com);
  • URL オブジェクトの openConnection() を呼び出して HttpURLConnection オブジェクト インスタンスを取得します: HttpURLConnection conn = (HttpURLConnection ) url .openConnection();
  • HTTP リクエストに使用されるメソッドを設定します: GET または POST、またはその他のリクエスト メソッド: PUTconn.setRequestMethod("GET");
  • 接続タイムアウトを設定し、タイムアウトのミリ秒数、およびサーバーが取得を希望するいくつかのメッセージ ヘッダーconn.setConnectTimeout(6*1000);conn.setReadTimeout(6 * 1000);
  • getInputStream() メソッドを呼び出して、サーバーから返された入力ストリームを読み取り、入力ストリームを読み取りますInputStream in = conn.getInputStream();
  • 最後に、disconnect() メソッドを呼び出して HTTP 接続をオフにしますconn.disconnect();

追記: 上記に加えて、200 などのレスポンスコードを判断する必要がある場合もあります。 if(conn.getResponseCode() != 200) といくつかの処理を行うこともあります。 パラメータを渡す必要はありませんが、ページに直接アクセスするには、次のものを直接使用できます。 最終的な入力ストリーム = 新しい URL("url").openStream(); 次にストリームを直接読み取りますが、このメソッドは実際にはページへの直接アクセスに適しています。 openConnection().getInputStream() を返しますが、まだいくつかを設定できません あくまで要望なので、こう書きたいかどうかは自分で判断してください!


3.HttpURLConnection の使用例

ここでは主に GET リクエストと POST リクエストの 2 つの異なる使用例を書きます。conn.getInputStream() を実行できます。 取得されるのはストリームなので、ストリームをバイナリ配列に変換するクラスを記述する必要があります。ツール クラスは次のとおりです:

StreamTool.java:

/**
 * Created by Jay on 2015/9/7 0007.
 */
public class StreamTool {
    //从流中读取数据
    public static byte[] read(InputStream inStream) throws Exception{
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = inStream.read(buffer)) != -1)
        {
            outStream.write(buffer,0,len);
        }
        inStream.close();
        return outStream.toByteArray();
    }
}

これで、例を使って遊んでみましょう。


1) HttpURLConnection が GET リクエストを送信するコード例

レンダリングの実行:

0.gif

コア部分コード:

レイアウト: activity_main.xml

                            

データクラスの取得: GetData.java :

/**
 * Created by Jay on 2015/9/7 0007.
 */
public class GetData {
    // 定义一个获取网络图片数据的方法:
    public static byte[] getImage(String path) throws Exception {
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 设置连接超时为5秒
        conn.setConnectTimeout(5000);
        // 设置请求类型为Get类型
        conn.setRequestMethod("GET");
        // 判断请求Url是否成功
        if (conn.getResponseCode() != 200) {
            throw new RuntimeException("请求url失败");
        }
        InputStream inStream = conn.getInputStream();
        byte[] bt = StreamTool.read(inStream);
        inStream.close();
        return bt;
    }

    // 获取网页的html源代码
    public static String getHtml(String path) throws Exception {
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
        if (conn.getResponseCode() == 200) {
            InputStream in = conn.getInputStream();
            byte[] data = StreamTool.read(in);
            String html = new String(data, "UTF-8");
            return html;
        }
        return null;
    }
}

MainActivity.java:

public class MainActivity extends AppCompatActivity {

    private TextView txtMenu, txtshow;
    private ImageView imgPic;
    private WebView webView;
    private ScrollView scroll;
    private Bitmap bitmap;
    private String detail = "";
    private boolean flag = false;
    private final static String PIC_URL = "http://ww2.sinaimg.cn/large/7a8aed7bgw1evshgr5z3oj20hs0qo0vq.jpg";
    private final static String HTML_URL = "http://www.baidu.com";

    // 用于刷新界面
    private Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case 0x001:
                    hideAllWidget();
                    imgPic.setVisibility(View.VISIBLE);
                    imgPic.setImageBitmap(bitmap);
                    Toast.makeText(MainActivity.this, "图片加载完毕", Toast.LENGTH_SHORT).show();
                    break;
                case 0x002:
                    hideAllWidget();
                    scroll.setVisibility(View.VISIBLE);
                    txtshow.setText(detail);
                    Toast.makeText(MainActivity.this, "HTML代码加载完毕", Toast.LENGTH_SHORT).show();
                    break;
                case 0x003:
                    hideAllWidget();
                    webView.setVisibility(View.VISIBLE);
                    webView.loadDataWithBaseURL("", detail, "text/html", "UTF-8", "");
                    Toast.makeText(MainActivity.this, "网页加载完毕", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }

        ;
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setViews();
    }

    private void setViews() {
        txtMenu = (TextView) findViewById(R.id.txtMenu);
        txtshow = (TextView) findViewById(R.id.txtshow);
        imgPic = (ImageView) findViewById(R.id.imgPic);
        webView = (WebView) findViewById(R.id.webView);
        scroll = (ScrollView) findViewById(R.id.scroll);
        registerForContextMenu(txtMenu);
    }

    // 定义一个隐藏所有控件的方法:
    private void hideAllWidget() {
        imgPic.setVisibility(View.GONE);
        scroll.setVisibility(View.GONE);
        webView.setVisibility(View.GONE);
    }

    @Override
    // 重写上下文菜单的创建方法
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        MenuInflater inflator = new MenuInflater(this);
        inflator.inflate(R.menu.menus, menu);
        super.onCreateContextMenu(menu, v, menuInfo);
    }

    // 上下文菜单被点击是触发该方法
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.one:
                new Thread() {
                    public void run() {
                        try {
                            byte[] data = GetData.getImage(PIC_URL);
                            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        handler.sendEmptyMessage(0x001);
                    }

                    ;
                }.start();
                break;
            case R.id.two:
                new Thread() {
                    public void run() {
                        try {
                            detail = GetData.getHtml(HTML_URL);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        handler.sendEmptyMessage(0x002);
                    };
                }.start();
                break;
            case R.id.three:
                if (detail.equals("")) {
                    Toast.makeText(MainActivity.this, "先请求HTML先嘛~", Toast.LENGTH_SHORT).show();
                } else {
                    handler.sendEmptyMessage(0x003);
                }
                break;
        }
        return true;
    }
}

最後にネットワーク権限を追加することを忘れないでください:


Notes:

ハンドラーを使用する理由は説明する必要はありません~ さらに、HTML コードをロードするときは、LoadData の代わりに webView のloadDataWithBaseURL を使用します。 LoadDataを使うと中国語の文字化けの問題が気になるので…loadDataWithBaseURLを使えばそこまで気にしなくて済みます。 さらに、一部のページでは、アカウントやパスワードなどのパラメータの送信が必要な場合があります。次のように、対応するパラメータを URL の末尾に接続するだけで済みます。 http://192.168.191.1:8080/CommentServer/LoginServlet?passwd=123&name=Jack 次に、サーバー側の getParamater("passwd") が対応するパラメーターを取得できるようになります。これらは、リクエスト時に明確に表示されます。 したがって、GET メソッドは安全ではありません。もう 1 つの注意点は、Android では 4.0 以降、非 UI スレッドでの UI 操作が許可されていないことです。


2) HttpURLConnection が POST リクエストを送信するコード例

openConnection を通じて取得する GET と POST があります。 HttpURLConnection は、デフォルトで Get リクエストを実行します。 したがって、POST を使用してデータを送信する場合は、事前に関連するパラメーターを設定する必要があります。 conn.setRequestMethod("POST"); conn.setDoOutput(true); conn.setDoInput(true); もあります。 また: conn.setUseCaches(false); POST メソッドはキャッシュできないため、手動で false に設定する必要があります。 特定の実装のコードを参照してください:

Running renderings:

2.gif

Core code:

PostUtils.java

public class PostUtils {
    public static String LOGIN_URL = "http://172.16.2.54:8080/HttpTest/ServletForPost";
    public static String LoginByPost(String number,String passwd)
    {
        String msg = "";
        try{
            HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection();
            //设置请求方式,请求超时信息
            conn.setRequestMethod("POST");
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(5000);
            //设置运行输入,输出:
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //Post方式不能缓存,需手动设置为false
            conn.setUseCaches(false);
            //我们请求的数据:
            String data = "passwd="+ URLEncoder.encode(passwd, "UTF-8")+
                    "&number="+ URLEncoder.encode(number, "UTF-8");
            //这里可以写一些请求头的东东...
            //获取输出流
            OutputStream out = conn.getOutputStream();
            out.write(data.getBytes());
            out.flush();
             if (conn.getResponseCode() == 200) {  
                    // 获取响应的输入流对象  
                    InputStream is = conn.getInputStream();  
                    // 创建字节输出流对象  
                    ByteArrayOutputStream message = new ByteArrayOutputStream();  
                    // 定义读取的长度  
                    int len = 0;  
                    // 定义缓冲区  
                    byte buffer[] = new byte[1024];  
                    // 按照缓冲区的大小,循环读取  
                    while ((len = is.read(buffer)) != -1) {  
                        // 根据读取的长度写入到os对象中  
                        message.write(buffer, 0, len);  
                    }  
                    // 释放资源  
                    is.close();  
                    message.close();  
                    // 返回字符串  
                    msg = new String(message.toByteArray());  
                    return msg;
             }
        }catch(Exception e){e.printStackTrace();}
        return msg;
    }
}

PS: MyEclipse はコンピューターにインストールされておらず、時間の制約があるため、デモは別に書きません。前の Eclipse デモを使用してください。 実際、コアコードを直接見るだけで十分です~ コードのダウンロード: HttpURLConnection example.zip


4. Cookie の問題の処理

これについて説明する前に、まず 2 つの概念を理解する必要があります: Session と CookieCookie はセッション メカニズムの一般的な形式にすぎません。他のメソッドをクライアントの一意の識別子として使用できます。 これはサーバーによって決定され、それを証明できる唯一のものはクライアント ID です。この方法に加えて、URL 書き換えを使用することもできます。 やり方もいろいろ!したがって、今後他の人に「セッションは単なるクッキーではない」などと愚かに言わないでください。

以下は、この Cookie を理解するのに役立つ例です。 Xiaozhu さんはアカウントとパスワードを入力して学校の教育システムにログインし、授業スケジュール情報にアクセスすることに成功しました。 Chrome を使用している場合は、F12 を押して開発モードに入ります。リソース インターフェイスに移動すると、Cookie が表示されます。 Cookie が存在する場所 (ドメイン)。 Cookie が配置されているディレクトリ (パス) Asp.net のデフォルトは、ルート ディレクトリです。Cookie のサイズ:

要求ヘッダーに Cookie フィールドがあることがわかります。

4.png

それでは、Cookie をクリアして (または数分待って)、次のリンクにアクセスしてください:

5.png

この時点で、ページは自動的にログイン ページに戻ります。もちろん、他の Web サイトによっては、次のようなダイアログ ボックスが表示される場合もあります。 「ログインタイムアウト」のようなものです。

HTTP リクエスト ログインの簡単なプロセスを要約します。 通常、ログイン時、サーバーは Set-Cookie 応答ヘッダーを通じて Cookie を返し、ブラウザーはデフォルトでこの Cookie を保存します。 この Cookie は、後で関連するページにアクセスするときにもたらされます。Cookie がない場合、または Cookie リクエスト ヘッダーを通じて訪問が完了します。 Cookie の有効期限が切れると、ユーザーはログインしていません、ログインがタイムアウトしました、アクセスするにはログインなどの情報が必要であるというメッセージが表示されます。

そして、HttpClient と HttpURLConnection を使用するとき、実際にこのプロセスをシミュレートし、ログイン後に Cookie を取得します。 それを取得してリクエストを送信します。 キーコードは以下の通りです: Cookieの取得: conn.getHeaderField("Set-Cookie"); リクエスト時にCookieを持ち込む: conn.setRequestProperty("Cookie", cookie);

さらに設定リクエストヘッダー この方法に加えて、別の妥協方法を使用することもできます: URL 書き換え: 元のリクエスト リンクに基づいて、...&sessionid=xxxxx のようなパラメータが追加され、サーバーはそれを解析します。 裁判官!

ここでは JSON 文字列の形式を使用します。リクエストを受信すると、サーバーはセッション内のコンテンツを取り出してクエリを作成します~


5. HttpURLConnection を使用して PUT リクエストを送信します

結局のところ、私たちが通常最もよく遭遇する状況は GET と POST です。 Xiaozhu は最初それを知りませんでしたが、後でそれが実際には POST に似ており、POST に基づいて変更するだけでよいことがわかりました。 何かを注文するだけで準備完了です。また、HttpClient は HttpPut API も提供します。 以下は Xiaozhu 自身のプロジェクトで書かれたリクエスト コードです:

public static String LoginByPut(Context mContext, String mobile, String password, int from, 
          String devid,String version_name, int remember_me) {
    String resp = "";
    try {
        HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection();
        conn.setRequestMethod("PUT");
        conn.setReadTimeout(5000);
        conn.setConnectTimeout(5000);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);

        String data = "mobile=" + mobile + "&password=" + password + "&from=" + from + "&devid=" + "devid"
                + "&version_name=" + "version_name" + "&remember_me=" + remember_me;
        ;
        // 获取输出流:
        OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
        writer.write(data);
        writer.flush();
        writer.close();

        // 获取相应流对象:
        InputStream in = conn.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        StringBuilder response = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null)
            response.append(line);
        SPUtils.put(mContext, "session", conn.getHeaderField("Set-Cookie"));
        // 资源释放:
        in.close();
        // 返回字符串
        Log.e("HEHE", response.toString());
        return response.toString();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}

このセクションの概要:

さて、このセクションの HttpUrlConnection の使用方法の紹介はここで終わります。また、HTTP セクションのほとんどは Xiaozhu pig からのものです。 以前に書いた小さなコレクション Android 用 Http Communication このシリーズを読んでいる場合は、このセクションのほとんどを読み飛ばしてください。 同じ!それでは、以上です、ありがとうございます~