Android網絡編程隨想錄(3)

        大多數Android的app都會使用HTTP協議來發送和接收數據。在Android開發中,一般使用兩種http客戶端:一個是Apache的HttpClient,另外一個是HttpURLConnection。這兩種HTTP客戶端API都支持HTTPS協議,流數據上傳和下載,配置超時,IPV6協議以及鏈接池等等。android

Apache HttpClient

        HttpClient的API衆多,而且bug少比較穩定。可是,HttpClient的API比較大,很難在保證兼容性的前提下去對其進行擴展。因此不少Android團隊並不太喜歡使用它。HttpClient是一個接口,裏面封裝了須要執行的http請求,身份驗證,鏈接管理等等,有三個主要的實現類:AbstractHttpClient, AndroidHttpClient, DefaultHttpClient。下面咱們來看一下AndroidHttpClient,它對DefaultHttpClient進行了改進使之更適合於Android開發。因而,AndroidHttpClient進行請求發送和響應接收步驟以下:服務器

  1. 建立HttpClient對象,經過靜態方法newInstance()方法來得到AndroidHttpClient對象。
  2. 建立對應的發送請求的對象,若是須要發送GET請求,則建立HttpGet對象,若是須要發送POST請求,則建立HttpPost對象。
  3. 對於發送請求的參數,GET和POST使用的方式不一樣,GET方式可使用拼接字符串的方式,把參數拼接在URL結尾;POST方式須要使用setEntity(HttpEntity entity)方法來設置請求參數。
  4. 調用HttpClient對象的execute(HttpUriRequest request)發送請求,執行該方法返回一個HttpResponse對象。
  5. 調用HttpResponse的對應方法獲取服務器的響應頭、響應內容等。

        一般狀況下,咱們並不在主線程中進行網絡請求操做,而是新開一個子線程來進行網絡操做,下面的代碼展現瞭如何利用AndroidHttpClient完成網絡登陸驗證的任務:網絡

public class LoginTask implements Runnable {

    private String username;
    private String password;

    public LoginTask(String username, String password) {
        // 初始化用戶名和密碼
        this.username = username;
        this.password = password;
    }

    @Override
    public void run() {
        // 設置訪問的Web站點
        String path = "http://xxxx/loginas.aspx";
        //設置Http請求參數
        Map<String, String> params = new HashMap<String, String>();
        params.put("username", username);
        params.put("password", password);

        String result = sendHttpClientPost(path, params, "utf-8");
        //把返回的接口輸出
        System.out.println(result);
    }
    /**
     * 發送Http請求到Web站點
     * @param path Web站點請求地址
     * @param map Http請求參數
     * @param encode 編碼格式
     * @return Web站點響應的字符串
     */
    private String sendHttpClientPost(String path,Map<String, String> map,String encode)
    {
        List<NameValuePair> list=new ArrayList<NameValuePair>();
        if(map!=null&&!map.isEmpty())
        {
            for(Map.Entry<String, String> entry:map.entrySet())
            {
                //解析Map傳遞的參數,使用一個鍵值對對象BasicNameValuePair保存。
                list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
        }
        try {
            //實現將請求 的參數封裝封裝到HttpEntity中。
            UrlEncodedFormEntity entity=new UrlEncodedFormEntity(list, encode);
            //使用HttpPost請求方式
            HttpPost httpPost=new HttpPost(path);
            //設置請求參數到Form中。
            httpPost.setEntity(entity);
            //實例化一個默認的Http客戶端,使用的是AndroidHttpClient
            HttpClient client=AndroidHttpClient.newInstance("");
            //執行請求,並得到響應數據
            HttpResponse httpResponse= client.execute(httpPost);
            //判斷是否請求成功,爲200時表示成功,其餘均問有問題。
            if(httpResponse.getStatusLine().getStatusCode()==200)
            {
                //經過HttpEntity得到響應流
                InputStream inputStream=httpResponse.getEntity().getContent();
                return changeInputStream(inputStream,encode);
            }
            
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        return "";
    }                    
    /**
     * 把Web站點返回的響應流轉換爲字符串格式
     * @param inputStream 響應流
     * @param encode 編碼格式
     * @return 轉換後的字符串
     */
    private  String changeInputStream(InputStream inputStream,
            String encode) { 
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] data = new byte[1024];
        int len = 0;
        String result="";
        if (inputStream != null) {
            try {
                while ((len = inputStream.read(data)) != -1) {
                    outputStream.write(data,0,len);                    
                }
                result=new String(outputStream.toByteArray(),encode);
                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

 

HttpURLConnection

        相比之下,HttpURLConnection就顯得更通用一些。它是一款多用途,輕量級的HTTP客戶端,適用於大多數的app客戶端。同時,HttpURLConnection簡單,便於擴展。可是坑爹的是,在Android2.2以前,HttpURLConnection有一些bug,例如在試圖關閉InputStream的時候會致使鏈接池失效。因此,推薦在2.3以後的版本中使用HttpURLConnection。app

        下面以請求百度首頁的logo爲例子,演示使用HttpURLConnection的GET方法完成網絡請求的任務:async

public class getImageTask{
    private static String URL_PATH = http://www.baidu.com/img/bd_logo1.png;
    /**
     * @param args
     */
    public static void main(String[] args) {
        // 調用方法獲取圖片並保存
        saveImageToDisk();
    }
    /**
     * 經過URL_PATH的地址訪問圖片並保存到本地
     */
    public static void saveImageToDisk()
    {
        InputStream inputStream= getInputStream();
        byte[] data=new byte[1024];
        int len=0;
        FileOutputStream fileOutputStream=null;
        try {
            //把圖片文件保存在本地F盤下
            fileOutputStream=new FileOutputStream("F:\\test.png");
            while((len=inputStream.read(data))!=-1) 
            {
                //向本地文件中寫入圖片流
                fileOutputStream.write(data,0,len);                
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally
        {
            //最後關閉流
            if(inputStream!=null)
            {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fileOutputStream!=null)
            {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 經過URL獲取圖片
     * @return URL地址圖片的輸入流。
     */
    public static InputStream getInputStream() {
        InputStream inputStream = null;
        HttpURLConnection httpURLConnection = null;

        try {
            //根據URL地址實例化一個URL對象,用於建立HttpURLConnection對象。
            URL url = new URL(URL_PATH);

            if (url != null) {
                //openConnection得到當前URL的鏈接
                httpURLConnection = (HttpURLConnection) url.openConnection();
                //設置3秒的響應超時
                httpURLConnection.setConnectTimeout(3000);
                //設置容許輸入
                httpURLConnection.setDoInput(true);
                //設置爲GET方式請求數據
                httpURLConnection.setRequestMethod("GET");
                //獲取鏈接響應碼,200爲成功,若是爲其餘,均表示有問題
                int responseCode=httpURLConnection.getResponseCode();
                if(responseCode==200)
                {
                    //getInputStream獲取服務端返回的數據流。
                    inputStream=httpURLConnection.getInputStream();
                }
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return inputStream;
    }

}

        若是使用POST方法,則須要設置請求參數。下面咱們使用POST方式,利用HttpURLConnection完成和上面同樣的登陸驗證功能:ide

public class LoginTask implements Runnable{

    private static String PATH = "http://xxxxx/loginas.aspx";
    private static URL url;

    private String username;
 private String password;
 public LoginTask(String username, String password) {
 // 初始化用戶名和密碼 
 this.username = username; 
 this.password = password; 
 }
    /**
     * 經過給定的請求參數和編碼格式,獲取服務器返回的數據
     * @param params 請求參數
     * @param encode 編碼格式
     * @return 得到的字符串
     */
    public static String sendPostMessage(Map<String, String> params,
            String encode) {
        StringBuffer buffer = new StringBuffer();
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, String> entry : params.entrySet()) {
                try {
                    buffer.append(entry.getKey())
                            .append("=")
                            .append(URLEncoder.encode(entry.getValue(), encode))
                            .append("&");//請求的參數之間使用&分割。
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }

            }
            buffer.deleteCharAt(buffer.length() - 1);
            System.out.println(buffer.toString());
            try {
                HttpURLConnection urlConnection = (HttpURLConnection) url
                        .openConnection();
                urlConnection.setConnectTimeout(3000);
                //設置容許輸入輸出
                urlConnection.setDoInput(true);
                urlConnection.setDoOutput(true);
                byte[] mydata = buffer.toString().getBytes();
                //設置請求報文頭,設定請求數據類型
                urlConnection.setRequestProperty("Content-Type",
                        "application/x-www-form-urlencoded");
                //設置請求數據長度
                urlConnection.setRequestProperty("Content-Length",
                        String.valueOf(mydata.length));
                //設置POST方式請求數據
                urlConnection.setRequestMethod("POST");
                OutputStream outputStream = urlConnection.getOutputStream();
                outputStream.write(mydata);
                int responseCode = urlConnection.getResponseCode();
                if (responseCode == 200) {
                    return changeInputStream(urlConnection.getInputStream(),
                            encode);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "";
    }

    /**
     * 把服務端返回的輸入流轉換成字符串格式
     * @param inputStream 服務器返回的輸入流
     * @param encode 編碼格式
     * @return 解析後的字符串
     */
    private static String changeInputStream(InputStream inputStream,
            String encode) { 
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] data = new byte[1024];
        int len = 0;
        String result="";
        if (inputStream != null) {
            try {
                while ((len = inputStream.read(data)) != -1) {
                    outputStream.write(data,0,len);                    
                }
                result=new String(outputStream.toByteArray(),encode);
                
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    @Override
    public void run() {
        //經過Map設置請求字符串。
 try { 
            url = new URL(PATH); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
         
         Map<String, String> params = new HashMap<String, String>();
        params.put("username", "admin");
        params.put("password", "123");        
        String result=sendPostMessage(params, "utf-8");
        System.out.println(result);
    }

}

 

 

總結

        那麼哪一個客戶端更好些呢?在2.2以前使用httpClient能夠避免一些bug。而在2.3以後的版本中使用HttpURLConnection是最好的選擇,簡單的API適合於Android開發。透明壓縮和應答數據的捕獲,減小了網絡的使用,提高了性能而且減小了電池的消耗。性能

        在隨後咱們介紹的開源庫中,volley在2.3以前的版本中使用了HttpClient,而在2.3以後使用了HttpURLConnection;android-async-http中封裝了HttpClient。this

相關文章
相關標籤/搜索