实践:网络编程

  • 一个应用程序很可能会在许多地方都使用到网络功能,而发送 HTTP 请求的代码基本都是相同的,如果我们每次都去编写一遍发送 HTTP 请求的代码,这显然是非常差劲的做法
  • 应该将这些通用的网络操作提取到一个公共的类里,并提供一个静态方法,当想要发起网络请求的时候只需简单地调用一下这个方法即可。
public class HttpUtil {
    public static String sendHttpRequest(String address) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(address);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(8000);
            connection.setReadTimeout(8000);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            InputStream in = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new
            InputStreamReader(in));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}

以后每当需要发起一条 HTTP 请求的时候就可以这样写:

String address = "http://www.baidu.com";
String response = HttpUtil.sendHttpRequest(address);
  • 但是 sendHttpRequest() 方法的内部并没有开启线程,这样就有可能导致在调用 sendHttpRequest() 方法的时候使得主线程被阻塞住。
  • 不能直接在 sendHttpRequest() 方法中开启一个线程来发起 HTTP 请求,因为这样,服务器响应的数据是无法进行返回的。所有的耗时逻辑都是在子线程里进行的,sendHttpRequest() 方法会在服务器还来不及响应的时候就执行结束了,也就无法返回响应的数据了
  • 需要使用 Java 的回调机制
1. 先定义一个接口,比如将它命名成 HttpCallbackListener。
public interface HttpCallbackListener {
    void onFinish(String response);
    void onError(Exception e);
}
  • onFinish() 方法表示当服务器成功响应请求的时候调用。
  • onError() 表示当进行网络操作出现错误的时候调用。
2. 修改 HttpUtil 中的代码。
public class HttpUtil {
    public static void sendHttpRequest(final String address, final HttpCallbackListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                try {
                    URL url = new URL(address);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    InputStream in = connection.getInputStream();
                    BufferedReader reader = new BufferedReader(new
                    InputStreamReader(in));
                    StringBuilder response = new StringBuilder();\
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    if (listener != null) {
                        // 回调onFinish()方法
                        listener.onFinish(response.toString());
                    }
                } catch (Exception e) {
                    if (listener != null) {
                        // 回调onError()方法
                        listener.onError(e);
                    }
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}
  • 注意子线程中是无法通过 return 语句来返回数据的
  • 因此将服务器响应的数据传入 HttpCallbackListener 的 onFinish() 方法中。
  • 如果出现了异常就将异常原因传入到 onError() 方法中。
3. 现在 sendHttpRequest() 方法接收两个参数了,因此在调用它的时候还需要将 HttpCallbackListener 的实例传入。
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
    @Override
    public void onFinish(String response) {
        // 在这里根据返回内容执行具体的逻辑
    }
    @Override
    public void onError(Exception e) {
        // 在这里对异常情况进行处理
    }
});
  • 需要注意的是,onFinish() 方法和** onError() 方法最终还是在子线程中运行的**,因此我们不可以在这里执行任何的 UI 操作.
  • 如果需要根据返回的结果来更新 UI,则仍然要使用异步消息处理机制。

推荐阅读更多精彩内容