Kaedah permintaan HTTP Android: HttpURLConnection


Pengenalan kepada bahagian ini:

Apa yang kami pelajari dalam dua bahagian sebelumnya ialah beberapa perkara konseptual, protokol HTTP dan beberapa perkara dalam pengepala protokol, dan dalam bahagian ini kami Sudah tiba masanya untuk mengumpul kod dan bahagian ini adalah mengenai salah satu kaedah permintaan Http yang disediakan oleh Android: HttpURLConnection. Di samping itu, terdapat satu lagi jenis HttpClient, yang akan kita bincangkan di bahagian seterusnya! Walau bagaimanapun, apabila permintaan menjadi rumit, yang pertama boleh digunakan Ia sangat menyusahkan, dan yang terakhir sering digunakan oleh kami dalam penangkapan paket Java Ia adalah daripada Apache Lagipun, ia bukan anak Google sendiri, tetapi dalam versi 4.4 HttpURLConnection telah digantikan dengan OkHttp! Nah, mengikut peredaran masa, saya memutuskan untuk bercakap tentang perkara ini selepas bercakap tentang HttpClient. OkHttp! Secara amnya, secara amnya kami tidak menggunakan HttpURLConnection dan HttpClient dalam pembangunan sebenar, tetapi menggunakan orang lain untuk merangkumnya. Rangka kerja permintaan rangkaian pihak ketiga yang baik, seperti: Volley, android-async-http, loopj, dll., kerana operasi rangkaian melibatkan Asynchronous dan multi-threading sangat menyusahkan jika anda melakukannya sendiri, jadi untuk pembangunan sebenar, anda harus terus menggunakan pihak ketiga! ! Sudah tentu, saya juga akan belajar Tidak mengapa, selepas semua, pihak ketiga juga dibina di atas asas ini, dengan seni bina berkualiti tinggi dan pelbagai pengoptimuman! Okay, tanpa berlengah lagi, mari kita mulakan Kandungan bahagian ini!


1. Pengenalan kepada HttpURLConnection

Jawapan: Klien HTTP yang ringan dan pelbagai guna yang boleh digunakan untuk melaksanakan kebanyakan aplikasi. Walaupun API yang disediakan oleh HttpURLConnection agak mudah, ia juga memudahkan kita untuk menggunakannya Gunakan dan panjangkannya. Mewarisi kepada URLConnection, kelas abstrak, dan tidak boleh membuat seketika objek. Dengan memanggil openCollection() Kaedah untuk mendapatkan contoh objek, lalai adalah dengan pemampatan gzip;

  • Buat objek URL: URL URL = URL baharu(http://www.baidu.com);
  • Panggil openConnection( ) URL objek Untuk mendapatkan contoh objek HttpURLConnection: HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  • Tetapkan kaedah yang digunakan untuk permintaan HTTP: GET atau POST, atau kaedah permintaan lain seperti : PUTconn.setRequestMethod("GET");
  • Tetapkan tamat masa sambungan, bilangan milisaat untuk tamat masa baca dan beberapa pengepala mesej yang pelayan harap dapat dapatkan conn.setConnectTimeout(6*1000); conn.setReadTimeout(6 * 1000);
  • Panggil kaedah getInputStream() untuk mendapatkan aliran input yang dikembalikan oleh pelayan, dan kemudian baca aliran input InputStream in = conn .getInputStream();
  • Akhirnya panggil kaedah disconnect() untuk menutup sambungan HTTPconn.disconnect();

PS: Sebagai tambahan kepada perkara di atas, kadangkala kita juga perlu menilai kod respons, seperti 200: if(conn.getResponseCode() != 200) dan kemudian beberapa pemprosesan Juga, mungkin kadangkala kita Tidak perlu melepasi sebarang parameter, tetapi untuk mengakses halaman secara terus, kami boleh terus menggunakan: InputStream akhir dalam = URL baharu("url").openStream(); Kemudian baca strim terus, tetapi kaedah ini sesuai untuk akses terus ke halaman Pelaksanaan asas sebenarnya kembalikan openConnection().getInputStream(), dan kami masih belum dapat menetapkan beberapa Ia hanya permintaan, jadi anda perlu menimbang sendiri sama ada anda mahu menulisnya seperti ini!


3. Contoh penggunaan HttpURLConnection

Di sini kami menulis dua contoh penggunaan yang berbeza untuk permintaan GET dan POST Kami boleh conn.getInputStream() Apa yang diperolehi ialah aliran, jadi kita perlu menulis kelas untuk menukar aliran menjadi tatasusunan binari! Kelas alat adalah seperti berikut:

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

Sekarang kita boleh mula bermain dengan contoh kita!


1) HttpURLConnection menghantar contoh kod permintaan GET

Menjalankan pemaparan:

0.gif

Kod bahagian teras:

Reka letak: activity_main.xml

                            

Dapatkan kelas data: GetData.java:

rreee

MainActivity.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;
    }
}

Akhir sekali jangan lupa untuk menambah kebenaran rangkaian:

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;
    }
}

Nota

Tak perlu terangkan sebab guna handler~ Selain itu, kami menggunakan loadDataWithBaseURL webView dan bukannya LoadData untuk memuatkan kod html. Jika anda menggunakan LoadData dan perlu risau tentang masalah aksara Cina yang kacau-bilau, jadi... anda tidak perlu risau sangat dengan menggunakan loadDataWithBaseURL. Selain itu, sesetengah halaman mungkin memerlukan kami menyerahkan beberapa parameter, seperti akaun dan kata laluan: kami hanya perlu menyambung parameter yang sepadan ke hujung URL, seperti: http://192.168.191.1:8080/ComentServer/LoginServlet?passwd=123&name=Jack Kemudian bahagian pelayan getParamater("passwd") boleh mendapatkan parameter yang sepadan dengan jelas apabila kami meminta. , jadi kaedah GET tidak selamat! Perkara lain yang perlu diambil perhatian ialah Android tidak membenarkan operasi UI dalam urutan bukan UI bermula daripada 4.0


2) HttpURLConnection menghantar contoh kod permintaan POST

! Dengan GET, terdapat POST secara semula jadi HttpURLConnection yang kami perolehi melalui openConnection melaksanakan permintaan Dapatkan secara lalai. Oleh itu, apabila kami menggunakan POST untuk menyerahkan data, kami harus menetapkan parameter yang berkaitan terlebih dahulu: conn.setRequestMethod("POST"); Terdapat juga: conn.setDoOutput(true); Juga: conn.setUseCaches(false); Kaedah POST tidak boleh dicache dan mesti ditetapkan kepada palsu secara manual. Lihat kod untuk pelaksanaan khusus:

Menjalankan pemaparan:

2.gif

Kod teras:

PostUtils.java


PS: Kerana MyEclipse tidak dipasang pada komputer, dan kerana kekangan masa, saya tidak akan menulis demo lain demo dari Eclipse sebelumnya! Malah, ia cukup untuk melihat kod teras secara langsung~ Muat turun kod: Contoh HttpURLConnection.zip


4 Mengendalikan isu Kuki

Sebelum bercakap tentang perkara ini, kita perlu memahami dua konsep dahulu: <. 🎜>Sesi dan KukiKuki hanyalah satu bentuk mekanisme Sesi yang biasa Kami juga boleh menggunakan kaedah lain sebagai pengecam unik untuk pelanggan. Ini ditentukan oleh pelayan, dan satu-satunya perkara yang boleh membuktikannya ialah ID pelanggan! Selain kaedah ini, kami juga boleh menggunakan penulisan semula URL! Cara-cara untuk melakukannya! Oleh itu, jangan beritahu orang lain secara bodoh pada masa hadapan: Sesi bukan sekadar kuki!

Berikut ialah contoh untuk membantu anda memahami kuki ini: Xiaozhu memasukkan akaun dan kata laluan dan log masuk ke sistem pentadbiran akademik sekolah, dan kemudian berjaya mengakses maklumat jadual kelas. Kemudian jika anda menggunakan Chrome, tekan F12 untuk memasuki mod pembangunan: Pergi ke antara muka Sumber dan anda boleh melihat Kuki kami:

3.png

Selepas mengklik, kami dapat melihat kandungan yang disimpan di dalamnya , yang terdiri daripada nama domain tempat kuki itu berada; Direktori tempat kuki terletak (laluan) Asp.net lalai kepada /, iaitu direktori akar masa tamat tempoh:

Kita dapat melihat bahawa terdapat medan Kuki dalam pengepala permintaan:

4.png

Nah, sekarang kami mengosongkan kuki (atau tunggu beberapa minit), dan kemudian lawati pautan berikut:

5.png

Di kali ini, halaman secara automatik melompat kembali ke halaman log masuk! Sudah tentu, beberapa laman web lain mungkin muncul kotak dialog yang berkata Sesuatu seperti "log masuk tamat masa"!

Ringkaskan proses mudah log masuk permintaan Http: Biasanya apabila log masuk: pelayan mengembalikan Kuki melalui pengepala respons Set-Cookie, dan penyemak imbas menyimpan Kuki ini secara lalai. Kuki ini akan dibawa bersama anda apabila anda melawat halaman yang berkaitan kemudian, dan lawatan akan diselesaikan melalui pengepala permintaan Kuki Jika tiada kuki atau Jika kuki tamat tempoh, ia akan menggesa pengguna untuk tidak log masuk, log masuk telah tamat masa, dan maklumat seperti log masuk diperlukan untuk akses!

Dan apabila kami menggunakan HttpClient dan HttpURLConnection, kami sebenarnya mensimulasikan proses ini dan mendapatkan kuki selepas log masuk. Ambilnya untuk menghantar permintaan: Kod kunci adalah seperti berikut: Dapatkan Cookie: conn.getHeaderField("Set-Cookie");Bawa Cookie apabila meminta: conn.setRequestProperty("Cookie",cookie);

Selain itu, sebagai tambahan kepada cara menetapkan pengepala permintaan ini, kaedah kompromi lain boleh digunakan: Penulisan semula URL: Berdasarkan pautan permintaan asal, parameter seperti...&sessionid=xxxxx ditambah, dan kemudian pelayan menghuraikannya. hakim!

Di sini kami menggunakan bentuk rentetan JSON Apabila menerima permintaan, pelayan mengeluarkan kandungan dalam sesi dan kemudian membuat pertanyaan~


5 HttpURLConnection untuk menghantar permintaan PUT

Permintaan put mungkin agak asing bagi ramai rakan Lagipun, situasi yang biasa kita hadapi adalah GET dan POST. Xiaozhu pada mulanya tidak mengetahuinya, tetapi kemudian saya mendapati bahawa ia sebenarnya serupa dengan POST, dan kami hanya perlu mengubah suainya berdasarkan POST. Hanya pesan sesuatu dan ia sedia untuk digunakan! Dan HttpClient juga menyediakan kami dengan API HttpPut. Di bawah ialah kod permintaan yang ditulis dalam projek Xiaozhu sendiri:

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;
    }
}

Ringkasan bahagian ini:

Baiklah, bahagian ini adalah mengenai penggunaan HttpUrlConnection At titik ini, sebagai tambahan, kebanyakan bahagian HTTP berasal dari Xiaozhu Koleksi kecil yang saya tulis sebelum ini Http Communication untuk Android Jika anda telah membaca siri ini, anda boleh melangkau bahagian ini sama! Baiklah, itu sahaja, terima kasih~