Android 网络通信—— HttpUrlConnection 和 OkHttp

标签: android网络通信httphttpurlconnectionokhttp
6631人阅读 评论(0) 收藏 举报
分类:

转载请注明出处:http://blog.csdn.net/smartbetter/article/details/51554118

本篇主要讲述如何在手机端使用HTTP协议和服务器端进行网络交互,并对服务端返回的数据进行解析。这也是Android中最常使用的网络技术,Android上发送HTTP请求目前主要有 原生的 HttpUrlConnection(HttpClient 在Android 6.0中已经被Google完全移除)和 Square公司开发的 OkHttp 两种方式。HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操作可以适用于大多数的应用程序,而 OkHttp 则不仅在接口封装上面做得简单易用,就连底层实现上也是自成一派,比起 原生的HttpURLConnection 则具有很多优势,现在已经成为了Android开发首选网络通信库。

另外需要注意的是,网络请求通常属于耗时操作,需要在子线程中进行,并且需要 在AndroidManifest.xml中声明 网络访问权限:

<uses-permission android:name="android.permission.INTERNET"/>

1.使用HttpURLConnection

首先需要获取 HttpURLConnection 的实例,一般只需 new 出一个 URL对象(URL对象代表统一资源定位符,它是指向互联网“资源”的指针,格式 protocol://host:port/resourceName),并传入目标网络地址,然后调用一下 openConnection() 方法即可。

另外 HttpURLConnection 还包含一些用于操作HTTP资源的便捷方法:

int getResponseCode():获取服务器的响应代码
String getResponseMessage():获取服务器的响应消息
String getRequestMethod():获取发送请求的方法
void setRequestMethod(String method):设置发送请求的方法

首先我们看一下GET请求和POST请求的不同:

请求 特点
GET GET用于信息获取,而且应该是安全的和幂等的,所谓安全的意味着该操作用于获取信息而非修改信息,不会影响资源的状态。幂等意味着对同一URL的多个请求应该返回同样的结果。
POST post用于修改服务器上的资源,需要注意的是post必须要到Form(表单)。

1.HttpURLConnection发送GET请求

private void doGet() {
    HttpURLConnection connection = null;
    try {
        URL url = new URL("http://localhost:8080/web/HelloServlet");
        connection = (HttpURLConnection)url.openConnection();
        connection.setRequestMethod("GET"); // 设置请求方式
        connection.setConnectTimeout(8000); // 设置连接服务端超时时间(毫秒)
        connection.setReadTimeout(8000); // 设置从服务端读取超时时间(毫秒)
        connection.setUseCaches(true); // 设置是否使用缓存, 默认true
        // 设置请求头信息
        connection.setRequestProperty("Content-Type", "application/json"); // 设置请求中的媒体类型信息
        connection.addRequestProperty("Connection", "Keep-Alive"); // 设置客户端与服务端连接类型
        connection.connect(); // 开始连接

        if (connection.getResponseCode() == 200) {
            Log.i("JAVA","success:"+ inputStramToString(connection.getInputStream()) );
        } else {
            Log.i("JAVA","fail");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }
}
/**
 * 将字节输入流转换成字符串
 *
 * @param in 字节输入流
 * @return 字符串
 */
private String inputStramToString(InputStream in) {
    BufferedReader reader = null; // 带有默认缓冲的字符输入流
    reader = new BufferedReader(new InputStreamReader(in));
    StringBuilder response = new StringBuilder();
    String line;
    try {
        while ((line = reader.readLine()) != null) {
            response.append(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return response.toString();
}

2.HttpURLConnection发送POST请求

private void doPost() {
    HttpURLConnection connection = null;
    try {
        URL url = new URL("http://localhost:8080/web/HelloServlet");
        connection = (HttpURLConnection)url.openConnection();
        connection.setRequestMethod("POST"); // 设置请求方式
        connection.setConnectTimeout(8000); // 设置连接服务端超时时间(毫秒)
        connection.setReadTimeout(8000); // 设置从服务端读取超时时间(毫秒)
        connection.setDoOutput(true); // POST请求必须设置允许输出, 默认false
        connection.setDoInput(true); // 设置请求允许输入, 默认true
        connection.setUseCaches(false); // POST请求不能使用缓存
        connection.setInstanceFollowRedirects(true); // 设置本次连接是否自动处理重定向
        // 设置请求头信息
        connection.setRequestProperty("Content-Type", "application/json"); // 设置请求中的媒体类型信息
        connection.addRequestProperty("Connection", "Keep-Alive"); // 设置客户端与服务端连接类型
        connection.connect(); // 开始连接

        // 发送请求参数
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        out.writeBytes("username=admin&password=123");
        out.flush();
        out.close();

        if (connection.getResponseCode() == 200) {
            Log.i("JAVA","success:"+ inputStramToString(connection.getInputStream()) );
        } else {
            Log.i("JAVA","fail");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (connection != null) {
            connection.disconnect();
        }
    }
}

2.使用OkHttp

关于 OkHttp 的使用方法官方文档中已经给出了说明:http://square.github.io/okhttp/,下面我们将对 OkHttp(OkHttp3) 做出补充说明。

1.OkHttp的同步请求和异步请求

OkHttp的同步请求(我们使用junit编写网络的测试方法,并在控制台输出结果):

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        System.out.println(response.body().string());
    }
} catch (IOException e) {
    e.printStackTrace();
}

OkHttp的异步请求:

System.out.println(Thread.currentThread().getId());
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
    }
    @Override
    public void onResponse(Call call, Response response) throws IOException {
        if (response.isSuccessful()) {
            System.out.println(Thread.currentThread().getId());
        }
    }
});

2.OkHttp请求头和响应头的实际应用

请求头可参考:http://tools.jb51.net/table/http_header

OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) " +
                "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36")
        .addHeader("Range", "bytes=2-")
        .addHeader("Accept-Encoding", "identity")
        .build();
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        Headers headers = response.headers();
        for (int i = 0; i < headers.size(); i++) {
            System.out.println(headers.name(i) + " : " + headers.value(i));
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

3.OkHttp发送GET请求

OkHttpClient client = new OkHttpClient();
HttpUrl httpUrl = HttpUrl.parse("http://localhost:8080/web/HelloServlet")
        .newBuilder()
        .addQueryParameter("username", "user")
        .addQueryParameter("password", "user123")
        .build();
Request request = new Request.Builder().url(httpUrl.toString()).build();
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        System.out.println(response.body().string());
    }
} catch (IOException e) {
    e.printStackTrace();
}

4.OkHttp发送POST请求

默认地,表单数据会编码为”application/x-www-form-urlencoded”。就是说,在发送到服务器之前,所有字符都会进行编码,空格转换为”+”,特殊符号转换为ASCII HEX值。除此之外,还有一个multipart/form-data,它不对字符编码,在使用包含文件上传的控件的表单时,必须使用该值,例如拍照上传等。okhttp已经对multipart/form-data进行了相应的封装,简化了很多操作。

OkHttpClient client = new OkHttpClient();
FormBody body = new FormBody.Builder()
        .add("username", "user") //如果包含中文需要调用addEncoded方法进行转码,而不是add方法
        .add("age", "18")
        .build();
Request request = new Request.Builder().url("http://localhost:8080/web/HelloServlet").post(body).build();
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        System.out.println(response.body().string());
    }
} catch (IOException e) {
    e.printStackTrace();
}

5.OkHttp上传文件

服务端 我们新建这么一个HttpServlet用于接受Android端上传的文件:

@MultipartConfig
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public UploadServlet() {
        super();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        doGet(request, response);
        String name=request.getParameter("name");
        Part part=request.getPart("filename");
        PrintWriter out=response.getWriter();
        out.print("name="+name+"\n");
        out.print("ContentType="+part.getContentType()+"\n");
        out.print("size="+part.getSize()+"\n");
        part.write("/Users/macbook/Desktop/temp_server.jpg");
    }
}

Android端使用OkHttp (mutipart协议) 上传文件

RequestBody imageBody = RequestBody.create(MediaType.parse("image/jpeg"), 
        new File("/Users/macbook/Desktop/temp.jpg"));
MultipartBody body = new MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        .addFormDataPart("name", "zhangsan")
        .addFormDataPart("filename", "temp.jpg", imageBody)
        .build();
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().
        url("http://192.168.1.20:8080/web/UploadServlet").post(body).build();
try {
    Response response = client.newCall(request).execute();
    if (response.isSuccessful()) {
        System.out.println(response.body().string());
    }
} catch (IOException e) {
    e.printStackTrace();
}

6.OkHttp实现数据缓存

OkHttp缓存相关类:CacheInterceptor、CacheStrategy、Cache、DiskLruCache。

int maxCacheSize = 10 * 1024 * 1024;
Cache cache = new Cache(new File("/Users/macbook/Desktop/source"), maxCacheSize);
OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();

Request request = new Request.Builder().url("http://www.163.com/")
        .cacheControl(new CacheControl.Builder().maxStale(365, TimeUnit.DAYS).build())
        .build();

Response response = client.newCall(request).execute();

String body1 = response.body().string();
System.out.println("network response " + response.networkResponse());
System.out.println("cache response " + response.cacheResponse());

System.out.println("**************************");

Response response1 = client.newCall(request).execute();

String body2 = response1.body().string();
System.out.println("network response " + response1.networkResponse());
System.out.println("cache response " + response1.cacheResponse());

我们运行这个测试类,显示如下,可以看到,先请求了network,后请求了cache:

数据缓存

15
4

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:397203次
    • 积分:3460
    • 等级:
    • 排名:第10178名
    • 原创:53篇
    • 转载:0篇
    • 译文:0篇
    • 评论:88条
    博客专栏