通常,Android应用程序必须与远程服务器交换信息。 最简单的方法是使用HTTP协议作为基础来传输信息。 HTTP协议在许多情况下非常有用,例如从远程服务器下载图像或将一些二进制数据上传到服务器。 Android应用执行GET或POST请求以发送数据。 在本文中,我们要分析如何使用HttpURLConnection与远程服务器进行通信。 我们将涵盖三个主要主题:
- GET和POST请求
- 从服务器下载数据
- 使用MultipartRequest将数据上传到服务器
作为服务器,我们将使用在Tomcat 7.0内运行的三个简单Servlet。 我们不会介绍如何使用API 3.0创建Servlet,但是源代码将很快可用。
GET和POST请求
GET和POST请求是HTTP协议的基础块。 要发出这种请求,我们首先需要打开与删除服务器的连接:
- HttpURLConnection con = (HttpURLConnection) ( new URL(url)).openConnection();
- con.setRequestMethod("POST");
- con.setDoInput(true);
- con.setDoOutput(true);
- con.connect();
在第一行中,我们获得HttpURLConnection,而在第二行中,我们设置方法,最后,我们连接到服务器。
打开连接后,我们可以使用OutputStream在其上进行写操作。
con.getOutputStream().write( ("name=" + name).getBytes());
众所周知,参数是使用键值对编写的。
最后一步是使用InputStream读取响应:
- InputStream is = con.getInputStream();
- byte[] b = new byte[1024];
- while ( is.read(b) != -1)
- buffer.append(new String(b));
- con.disconnect();
到目前为止,一切都非常简单,但是我们必须记住一件事:建立HTTP连接是一项耗时的操作,可能需要很长时间,因此我们无法在主线程中运行它,否则会遇到ANR问题。 为了解决这个问题,我们可以使用AsyncTask。
- private class SendHttpRequestTask extends AsyncTask<String, Void, String>{
-
- @Override
- protected String doInBackground(String... params) {
- String url = params[0];
- String name = params[1];
- String data = sendHttpRequest(url, name);
- return data;
- }
-
- @Override
- protected void onPostExecute(String result) {
- edtResp.setText(result);
- item.setActionView(null);
- }
- }
运行我们得到的应用程序:
如我们所见,我们在服务器上发布了一个名称,并以经典的“ Hello…。”作为响应。 在服务器端,我们可以检查服务器是否正确接收了我们的post参数:
从服务器下载数据
最常见的情况之一是Android应用程序必须从远程服务器下载一些数据。 我们可以假设我们要从服务器下载图像。 在这种情况下,我们必须始终使用AsyncTask来完成操作,代码如下所示:
- public byte[] downloadImage(String imgName) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try {
- System.out.println("URL ["+url+"] - Name ["+imgName+"]");
-
- HttpURLConnection con = (HttpURLConnection) ( new URL(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 = new byte[1024];
-
- while ( is.read(b) != -1)
- baos.write(b);
-
- con.disconnect();
- }
- catch(Throwable t) {
- t.printStackTrace();
- }
-
- return baos.toByteArray();
- }
以这种方式调用此方法:
- private class SendHttpRequestTask extends AsyncTask<String, Void, byte[]> {
-
- @Override
- protected byte[] doInBackground(String... params) {
- String url = params[0];
- String name = params[1];
-
- HttpClient client = new HttpClient(url);
- byte[] data = client.downloadImage(name);
-
- return data;
- }
-
- @Override
- protected void onPostExecute(byte[] result) {
- Bitmap img = BitmapFactory.decodeByteArray(result, 0, result.length);
- imgView.setImageBitmap(img);
- item.setActionView(null);
-
- }
-
- }
运行我们拥有的应用程序:
使用MultipartRequest将数据上传到服务器
这是处理http连接中最复杂的部分。 本地的HttpURLConnection不能处理这种类型的请求。 Android应用可能必须将一些二进制数据上传到服务器。 例如,某个应用可能必须上传图像。 在这种情况下,请求变得更加复杂,因为“普通”请求是不够的。 我们必须创建一个MultipartRequest。
MultipartRequest是由不同部分(例如参数和二进制数据)发出的请求。 我们如何处理此请求?
好了,第一步是打开一个连接,通知服务器我们要发送一些二进制信息:
- public void connectForMultipart() throws Exception {
- con = (HttpURLConnection) ( new URL(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行中,我们指定了请求内容类型和另一个称为boundary的字段。 此字段是用于分隔不同部分的char序列。
对于我们要添加的每个部分,我们需要指定它是文本部分(如post参数)还是文件(即二进制数据)。
- public void addFormPart(String paramName, String value) throws Exception {
- writeParamData(paramName, value);
- }
-
- private void writeParamData(String paramName, String value) throws Exception {
- 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());
-
- }
哪里
- private String delimiter = "--";
- private String boundary = "SwA"+Long.toString(System.currentTimeMillis())+"SwA";
要添加文件部分,我们可以使用:
- public void addFilePart(String paramName, String fileName, byte[] data) throws Exception {
- 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());
- }
因此,在我们的应用程序中,我们有:
- private class SendHttpRequestTask extends AsyncTask<String, Void, String> {
-
- @Override
- protected String 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 = new ByteArrayOutputStream();
- b.compress(CompressFormat.PNG, 0, baos);
-
- try {
- HttpClient client = new HttpClient(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();
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(String data) {
- item.setActionView(null);
-
- }
-
- }
运行它,我们有:
源代码@ github 。