连接到网络

    这一课会演示如何实现一个简单的连接到网络的程序。它提供了一些我们在创建即使最简单的网络连接程序时也应该遵循的最佳示例。

    请注意,想要执行本课的网络操作首先需要在程序的 manifest 文件中添加以下权限:

    大多数连接网络的 Android app 会使用 HTTP 来发送与接收数据。Android 提供了两种 HTTP clients:HttpURLConnection 与 Apache 。二者均支持 HTTPS、流媒体上传和下载、可配置的超时、IPv6 与连接池(connection pooling)。对于 Android 2.3 Gingerbread 或更高的版本,推荐使用 HttpURLConnection。关于这部分的更多详情,请参考 Android’s HTTP Clients

    1. ...
    2. ConnectivityManager connMgr = (ConnectivityManager)
    3. getSystemService(Context.CONNECTIVITY_SERVICE);
    4. NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    5. if (networkInfo != null && networkInfo.isConnected()) {
    6. // fetch data
    7. } else {
    8. // display error
    9. }
    10. ...
    11. }

    网络操作会遇到不可预期的延迟。为了避免造成不好的用户体验,总是在 UI 线程之外单独的线程中执行网络操作。 类提供了一种简单的方式来处理这个问题。这部分的详情,请参考 Multithreading For Performance

    在下面的代码示例中,myClickHandler() 方法会执行 new DownloadWebpageTask().execute(stringUrl)DownloadWebpageTaskAsyncTask 的子类,它实现了下面两个方法:

    • 执行 downloadUrl() 方法。它以网页的 URL 作为参数,方法 downloadUrl() 获取并处理网页返回的数据。执行完毕后,返回一个结果字符串。
    • onPostExecute() 接收结果字符串并把它显示到 UI 上。

    上面这段代码的事件顺序如下:

    1. 当用户点击按钮时调用 myClickHandler(),app 将指定的 URL 传给 的子类 DownloadWebpageTask
    2. AsyncTask 的 方法调用 downloadUrl() 方法。
    3. downloadUrl() 方法以一个 URL 字符串作为参数,并用它创建一个 URL 对象。
    4. 一旦建立连接, 对象将获取网页的内容并得到一个 InputStream
    5. 被传给 readIt() 方法,该方法将流转换成字符串。
    6. 最后,AsyncTask 的 方法将字符串展示在 main activity 的 UI 上。

    在下面的代码示例中,doInBackground() 方法会调用 downloadUrl()。这个 downloadUrl() 方法使用给予的 URL,通过 连接到网络。一旦建立连接后,app 就会使用 getInputStream() 来获取包含数据的 InputStream

    1. // Given a URL, establishes an HttpUrlConnection and retrieves
    2. // the web page content as a InputStream, which it returns as
    3. // a string.
    4. private String downloadUrl(String myurl) throws IOException {
    5. InputStream is = null;
    6. // Only display the first 500 characters of the retrieved
    7. // web page content.
    8. int len = 500;
    9. try {
    10. URL url = new URL(myurl);
    11. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    12. conn.setReadTimeout(10000 /* milliseconds */);
    13. conn.setConnectTimeout(15000 /* milliseconds */);
    14. conn.setRequestMethod("GET");
    15. conn.setDoInput(true);
    16. // Starts the query
    17. conn.connect();
    18. is = conn.getInputStream();
    19. // Convert the InputStream into a string
    20. String contentAsString = readIt(is, len);
    21. return contentAsString;
    22. // Makes sure that the InputStream is closed after the app is
    23. // finished using it.
    24. } finally {
    25. if (is != null) {
    26. is.close();
    27. }
    28. }
    29. }

    请注意,getResponseCode() 会返回连接的状态码(status code)。这是一种获知额外网络连接信息的有效方式。其中,状态码是 200 则意味着连接成功。

    是一种可读的 byte 数据源。如果我们获得了一个 InputStream,通常会进行解码(decode)或者转换为目标数据类型。例如,如果我们是在下载图片数据,那么可能需要像下面这样解码并展示它:

    1. // Reads an InputStream and converts it to a String.
    2. public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
    3. Reader reader = null;
    4. reader = new InputStreamReader(stream, "UTF-8");
    5. char[] buffer = new char[len];
    6. reader.read(buffer);
    7. }