Часто приложениям Android приходится обмениваться информацией с удаленным сервером. Самый простой способ — использовать протокол HTTP в качестве базы для передачи информации. Существует несколько сценариев, в которых протокол HTTP очень полезен, например загрузка изображения с удаленного сервера или загрузка некоторых двоичных данных на сервер. Android-приложение выполняет запрос GET или POST для отправки данных. В этом посте мы хотим проанализировать, как использовать HttpURLConnection для связи с удаленным сервером. Мы рассмотрим три основные темы:
- GET и POST запросы
- Скачать данные с сервера
- Загрузить данные на сервер с помощью MultipartRequest
В качестве сервера мы будем использовать три простых сервлета, работающих внутри Tomcat 7.0. Мы не будем рассказывать, как создать сервлет с использованием API 3.0, но скоро будет доступен исходный код.
GET и POST запросы
GET и POST запросы являются базовыми блоками в протоколе HTTP. Чтобы сделать такого рода запросы, нам нужно сначала открыть соединение с удаленным сервером:
| 1 2 3 4 5 | HttpURLConnection con = (HttpURLConnection) ( newURL(url)).openConnection();con.setRequestMethod("POST");con.setDoInput(true);con.setDoOutput(true);con.connect(); | 
В первой строке мы получаем HttpURLConnection, в то время как в строке 2 мы устанавливаем метод и в конце подключаемся к серверу.
Как только мы открыли соединение, мы можем записать его, используя OutputStream.
| 1 | con.getOutputStream().write( ("name="+ name).getBytes()); | 
Как мы уже знаем, параметры записываются с использованием пары ключ-значение.
Последний шаг — чтение ответа с использованием InputStream:
| 1 2 3 4 5 | InputStream is = con.getInputStream();byte[] b = newbyte[1024];while( is.read(b) != -1)  buffer.append(newString(b));con.disconnect(); | 
Сейчас все очень просто, но мы должны помнить одну вещь: создание HTTP-соединения — это трудоемкая операция, которая может потребовать много времени, поэтому мы не можем запустить ее в главном потоке, иначе мы можем получить проблему ANR. Для ее решения мы можем использовать AsyncTask.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | privateclassSendHttpRequestTask extendsAsyncTask<String, Void, String>{  @Override  protectedString doInBackground(String... params) {   String url = params[0];   String name = params[1];   String data = sendHttpRequest(url, name);   returndata;  }  @Override  protectedvoidonPostExecute(String result) {   edtResp.setText(result);   item.setActionView(null);     }} | 
Запустив приложение мы получаем:
Как мы видим, мы публикуем имя на сервере, и он отвечает классическим «Hello….». На стороне сервера мы можем проверить, что сервер правильно получил наш параметр post:
Скачать данные с сервера
Один из наиболее распространенных сценариев — это когда приложение Android загружает некоторые данные с удаленного сервера. Мы можем предположить, что мы хотим загрузить изображение с сервера. В этом случае мы всегда должны использовать AsyncTask для завершения нашей операции, код показан ниже:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | publicbyte[] downloadImage(String imgName) {    ByteArrayOutputStream baos = newByteArrayOutputStream();    try{        System.out.println("URL ["+url+"] - Name ["+imgName+"]");        HttpURLConnection con = (HttpURLConnection) ( newURL(url)).openConnection();        con.setRequestMethod("POST");        con.setDoInput(true);        con.setDoOutput(true);        con.connect();        con.getOutputStream().write( ("name="+ imgName).getBytes());        InputStream is = con.getInputStream();        byte[] b = newbyte[1024];        while( is.read(b) != -1)            baos.write(b);        con.disconnect();    }    catch(Throwable t) {        t.printStackTrace();    }    returnbaos.toByteArray();} | 
Этот метод вызывается так:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | privateclassSendHttpRequestTask extendsAsyncTask<String, Void, byte[]> {    @Override    protectedbyte[] doInBackground(String... params) {        String url = params[0];        String name = params[1];        HttpClient client = newHttpClient(url);        byte[] data = client.downloadImage(name);        returndata;    }    @Override    protectedvoidonPostExecute(byte[] result) {        Bitmap img = BitmapFactory.decodeByteArray(result, 0, result.length);        imgView.setImageBitmap(img);        item.setActionView(null);    }} | 
Запустив приложение мы имеем:
Загрузить данные на сервер с помощью MultipartRequest
Это самая сложная часть в обработке http-соединения. Собственно HttpURLConnection не обрабатывает этот тип запроса. Может случиться, что приложение Android должно загрузить некоторые двоичные данные на сервер. Может быть, приложение должно загрузить изображение, например. В этом случае запрос становится более сложным, потому что «нормального» запроса недостаточно. Мы должны создать MultipartRequest.
MultipartRequest — это запрос, который выполняется различными частями, такими как параметры и двоичные данные. Как мы можем обработать этот запрос?
Итак, первый шаг — это открыть соединение, информирующее сервер, что мы хотим отправить некоторую двоичную информацию:
| 01 02 03 04 05 06 07 08 09 10 | publicvoidconnectForMultipart() throwsException {    con = (HttpURLConnection) ( newURL(url)).openConnection();    con.setRequestMethod("POST");    con.setDoInput(true);    con.setDoOutput(true);    con.setRequestProperty("Connection", "Keep-Alive");    con.setRequestProperty("Content-Type", "multipart/form-data; boundary="+ boundary);    con.connect();    os = con.getOutputStream();} | 
В строках 6 и 7 мы указываем тип контента запроса и другое поле, называемое границей . Это поле представляет собой последовательность символов, используемую для разделения различных частей.
Для каждой части, которую мы хотим добавить, нам нужно указать, является ли она текстовой частью, такой как параметр post, или это файл (то есть двоичные данные).
| 01 02 03 04 05 06 07 08 09 10 11 | publicvoidaddFormPart(String paramName, String value) throwsException {  writeParamData(paramName, value);}privatevoidwriteParamData(String paramName, String value) throwsException {    os.write( (delimiter + boundary + "\r\n").getBytes());    os.write( "Content-Type: text/plain\r\n".getBytes());    os.write( ("Content-Disposition: form-data; name=\""+ paramName + "\"\r\n").getBytes());;    os.write( ("\r\n"+ value + "\r\n").getBytes());} | 
где
| 1 2 | privateString delimiter = "--";privateString boundary =  "SwA"+Long.toString(System.currentTimeMillis())+"SwA"; | 
Чтобы добавить часть файла, мы можем использовать:
| 01 02 03 04 05 06 07 08 09 10 11 | publicvoidaddFilePart(String paramName, String fileName, byte[] data) throwsException {    os.write( (delimiter + boundary + "\r\n").getBytes());    os.write( ("Content-Disposition: form-data; name=\""+ paramName +  "\"; filename=\""+ fileName + "\"\r\n").getBytes());    os.write( ("Content-Type: application/octet-stream\r\n").getBytes());    os.write( ("Content-Transfer-Encoding: binary\r\n").getBytes());    os.write("\r\n".getBytes());    os.write(data);    os.write("\r\n".getBytes());} | 
Итак, в нашем приложении мы имеем:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | privateclassSendHttpRequestTask extendsAsyncTask<String, Void, String> {    @Override    protectedString doInBackground(String... params) {        String url = params[0];        String param1 = params[1];        String param2 = params[2];        Bitmap b = BitmapFactory.decodeResource(UploadActivity.this.getResources(), R.drawable.logo);        ByteArrayOutputStream baos = newByteArrayOutputStream();        b.compress(CompressFormat.PNG, 0, baos);        try{            HttpClient client = newHttpClient(url);            client.connectForMultipart();            client.addFormPart("param1", param1);            client.addFormPart("param2", param2);            client.addFilePart("file", "logo.png", baos.toByteArray());            client.finishMultipart();            String data = client.getResponse();        }        catch(Throwable t) {            t.printStackTrace();        }        returnnull;    }    @Override    protectedvoidonPostExecute(String data) {                    item.setActionView(null);    }} | 
Запустив его мы имеем:
Исходный код @ github .





