This lesson shows you how to implement a simple application that connects to the network. It explains some of the best practices you should follow in creating even the simplest network-connected app.
本篇文档向您展示了如何实现一个可以连接到网络的应用。文章也给出了一些你在开发应用时应该注意的事项。
Note that to perform the network operations described in this lesson, your application manifest must include the following permissions:
记住,要想让你的应用执行一些与网络相关的操作,在你的manifest文件中添加以下的权限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Choose an HTTP Client
Most network-connected Android apps use HTTP to send and receive data. Android includes two HTTP clients: HttpURLConnection
and Apache HttpClient
. Both support HTTPS, streaming uploads and downloads, configurable timeouts, IPv6, and connection pooling. We recommend using HttpURLConnection
for applications targeted at Gingerbread and higher. For more discussion of this topic, see the blog post Android's HTTP Clients.
大多数的安卓网络应用都是使用http协议来收发数据的。安卓包括两个http客户端类,一个是HttpUrlConnection,一个是阿帕奇的HttpClient。这两者都支持https协议,流数据的上传和下载,配置连接时长,IPV6,以及连接池技术。我们建议,在Gingerbread或者更高的版本中,使用HttpUrlConnection类。
Check the Network Connection
Before your app attempts to connect to the network, it should check to see whether a network connection is available using getActiveNetworkInfo()
and isConnected()
. Remember, the device may be out of range of a network, or the user may have disabled both Wi-Fi and mobile data access. For more discussion of this topic, see the lesson Managing Network Usage.
在你的应用连接网络之前,先使用getActiveNetworkInfo
方法和isConnected
()检测下网络连接是否可用。记住,用户随时会走出网络的范围,或者是用户会关掉wi-fi什么的。
其中,isConnected(),
Indicates whether network connectivity exists and it is possible to establish connections and pass data.
Always call this before attempting to perform data transactions.
判断是否有是否与网络建立连接。
Returns details about the currently active data network. When connected, this network is the default route for outgoing connections. You should always check isConnected()
before initiating network traffic. This may return null
when no networks are available.
public void myClickHandler(View view) { ... ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { // fetch data } else { // display error } ... }
Perform Network Operations on a Separate Thread
Network operations can involve unpredictable delays. To prevent this from causing a poor user experience, always perform network operations on a separate thread from the UI. The AsyncTask
class provides one of the simplest ways to fire off a new task from the UI thread. For more discussion of this topic, see the blog postMultithreading For Performance.
与网络相关的操作难免会有时延的问题,时延也是不可预见的。为了避免时延引起的糟糕的用户体验,将任何与网络相关的操作放在单独的线程中。AsyncTask类就提供了一个不错的方法,可以让一个与网络相关的操作不在主线程中运行。
In the following snippet, the myClickHandler()
method invokes new DownloadWebpageTask().execute(stringUrl)
. The DownloadWebpageTask
class is a subclass ofAsyncTask
. DownloadWebpageTask
implements the following AsyncTask
methods:
在下面的实例中,myClickHandler
方法会调用new DownloadWebpageTask().execute(stringUrl)
.其中,DownloadWebpageTask
类是AsyncTask类的一个子类,当然了,它就要实现AsyncTask类的方法:没有直接用AsyncTask类,而是使用它的子类。
doInBackground()
executes the methoddownloadUrl()
. It passes the web page URL as a parameter. The methoddownloadUrl()
fetches and processes the web page content. When it finishes, it passes back a result string.
实现的第一个方法是doInBackground。这个方法会执行子类DownloadWebpageTask
的downloadUrl
方法。也就是将下载操作放在doInBackground方法中实现。downloadUrl
方法的参数是一个要下载的网页地址。downloadUrl
下载该网页,并且处理网页内容。当处理网页操作结束后,downloadUrl
方法会返回一个字符串。
onPostExecute()
takes the returned string and displays it in the UI.
第二个要实现的方法是onPostExecute。这个方法会自动接收downloadUrl
方法返回的处理结果字符串,也就是接收doInBackGround方法返回的字符串。并且将这个字符串显示在界面上。
public class HttpExampleActivity extends Activity { private static final String DEBUG_TAG = "HttpExample"; private EditText urlText; private TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); urlText = (EditText) findViewById(R.id.myUrl); textView = (TextView) findViewById(R.id.myText); } // When user clicks button, calls AsyncTask. // Before attempting to fetch the URL, makes sure that there is a network connection. public void myClickHandler(View view) { // Gets the URL from the UI's text field. String stringUrl = urlText.getText().toString(); ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { new DownloadWebpageText().execute(stringUrl); } else { textView.setText("No network connection available."); } } // Uses AsyncTask to create a task away from the main UI thread. This task takes a // URL string and uses it to create an HttpUrlConnection. Once the connection // has been established, the AsyncTask downloads the contents of the webpage as // an InputStream. Finally, the InputStream is converted into a string, which is // displayed in the UI by the AsyncTask's onPostExecute method. private class DownloadWebpageText extends AsyncTask { @Override protected String doInBackground(String... urls) { // params comes from the execute() call: params[0] is the url. try { return downloadUrl(urls[0]); } catch (IOException e) { return "Unable to retrieve web page. URL may be invalid."; } } // onPostExecute displays the results of the AsyncTask. @Override protected void onPostExecute(String result) { textView.setText(result); } } ... }
The sequence of events in this snippet is as follows:
这个实例代码执行的逻辑顺序是:
- When users click the button that invokes
myClickHandler()
, the app passes the specified URL to theAsyncTask
subclassDownloadWebpageTask
.
当用户点击一个按钮后,会调用myClickHandler
方法,在这个方法中,会将指定的要下载的链接传递给AsyncTask类的子类DownloadWebpageTask
。
2. The AsyncTask
method doInBackground()
calls the downloadUrl()
method.
AsyncTask类的方法doInBackground
会调用downloadUrl
方法。
3. The downloadUrl()
method takes a URL string as a parameter and uses it to create a URL
object.
downloadUrl
方法将下载页面的地址作为参数,并以此参数创建一个URL对象。
4. The URL
object is used to establish an HttpURLConnection
.
再使用这个URL对象创建一个HttpURLConnection
连接。
5. Once the connection has been established, the HttpURLConnection
object fetches the web page content as an InputStream
.
一旦这个连接建立,HttpURLConnection
对象下载网页内容,并且将网页内容作为流;
6. The InputStream
is passed to the readIt()
method, which converts the stream to a string.
网页流再次传递给readIt
方法,在readIt
方法中,会将网页流转换为字符串。
7. Finally, the AsyncTask
's onPostExecute()
method displays the string in the main activity's UI.
最后,AsyncTask的onPostExecute方法会将这个转换后额字符串显示在界面上。
Connect and Download Data
In your thread that performs your network transactions, you can use HttpURLConnection
to perform a GET
and download your data. After you call connect()
, you can get an InputStream
of the data by callinggetInputStream()
.
在线程中执行网络操作时,你可以使用HttpURLConnection
这个类来执行get操作,以下载网页内容。当你调用了connect方法,再次调用getInputStream方法时,你就可以获取网页内容的流了。
In the following snippet, the doInBackground()
method calls the method downloadUrl()
. ThedownloadUrl()
method takes the given URL and uses it to connect to the network via HttpURLConnection
. Once a connection has been established, the app uses the method getInputStream()
to retrieve the data as anInputStream
.
在下面的实例中,doInBackground方法会调用downloadUrl
方法。downloadUrl
以指定的要下载的网页地址为参数,通过HttpURLConnection
来连接到这个网页。一旦连接建立了,就可以使用getInputStream
方法来获取网页流数据了。
// Given a URL, establishes an HttpUrlConnection and retrieves // the web page content as a InputStream, which it returns as // a string. private String downloadUrl(String myurl) throws IOException { InputStream is = null; // Only display the first 500 characters of the retrieved // web page content. int len = 500; try { URL url = new URL(myurl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(10000 /* milliseconds */); conn.setConnectTimeout(15000 /* milliseconds */); conn.setRequestMethod("GET"); conn.setDoInput(true); // Starts the query conn.connect(); int response = conn.getResponseCode(); Log.d(DEBUG_TAG, "The response is: " + response); is = conn.getInputStream(); // Convert the InputStream into a string String contentAsString = readIt(is, len); return contentAsString; // Makes sure that the InputStream is closed after the app is // finished using it. } finally { if (is != null) { is.close(); } } }
Note that the method getResponseCode()
returns the connection's status code. This is a useful way of getting additional information about the connection. A status code of 200 indicates success.
记住,getResponseCode
返回创建连接时的服务器的响应。这是一个获取额外信息的方法。200一般表示连接成功。
Convert the InputStream to a String
An InputStream
is a readable source of bytes. Once you get an InputStream
, it's common to decode or convert it into a target data type. For example, if you were downloading image data, you might decode and display it like this:
InputStream
就是可读的字节序列。一旦你获取了InputStream
,一般而言,你需要将其解码或者转化为一个具体的类型。即,字节序列谁都看不懂,要把它转化成有意义的东西,比如字符串,位图等。比如,如果你下载了一个图片,你需要解码这个字节序列成图片,并且显示这个图片:
InputStream is = null; ... Bitmap bitmap = BitmapFactory.decodeStream(is); ImageView imageView = (ImageView) findViewById(R.id.image_view); imageView.setImageBitmap(bitmap);
In the example shown above, the InputStream
represents the text of a web page. This is how the example converts the InputStream
to a string so that the activity can display it in the UI:
在上述的例子中,InputStream
代表了网页的文本内容。下面的代码显示了如何将InputStream
转化为字符串的,这样activity就可显示这个字符串了:
// Reads an InputStream and converts it to a String. public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException { Reader reader = null; reader = new InputStreamReader(stream, "UTF-8"); char[] buffer = new char[len]; reader.read(buffer); return new String(buffer); }
总结:
1. 与网络相关的操作放在单独的线程中,如可以使用AsyncTask类的子类,实现doInBackground方法和onPostExecuter方法;
2. 判断下当前用户设备是否已经连接上网络了,以及网络的连接状态,比如,移动网络是否已经打开,wifi是否已经打开;
3. 根据下载的网页地址URL创建HttpUrlConnection类,HttpUrlConnection.connect建立连接。
4. 记住,关闭inputStream流;
5. 将返回的网页流数据解码为有意义的东西。