Do you want to spend the rest of your life selling sugared water or do you want a chance to change the world?
你想用卖糖水来度过余生,还是想要一个机会来改变世界?——乔布斯
小弟初学安卓,该文算是小弟的学习过程,课后笔记与一些自己的思考,希望在自己的自学路上留下印记,也许有一些自己想的不对的地方,希望各位前辈斧正。(心有愧疚,这篇笔记没有太多自己思考的地方,暂时把已经了解的先记下,未来深入研究)
一、使用HTTP协议访问网络获取数据
简单的来说就是我们向服务器发送一条HTTP请求,服务器接收请求后返回一些数据给我们,然后我们可以对这些数据处理或者解析。
HTTP请求方式有八种:
- OPTIONS
- GET
- HEAD
- POST
- PUT
- DELETE
- TRACE
- CONNECT
这些请求的具体作用可以通过搜索了解,小弟就不在这复制别人的了,对我们来说最常用的应该是"GET"和"POST"了,"GET"是向服务器请求数据,"POST"是向服务器提交数据。
Android要发送HTTP请求原本有两种,HttpURLConnection 和HttpClient,但是HttpClient已经在最新的几个API中弃用了,且HttpURLConnection 受到了Google的青睐。感兴趣的朋友可以搜索看看。
1.获取HttpURLConnection 的实例
URL url = new URL(http://www.baidu.com);//以百度为例
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
其中URL实例化对象会抛出MalformedURLException异常,而URL的openConnection()方法会抛出IOException异常,需要我们捕获。
2.连接前的设置
首先是我们之前提到的HTTP请求方式,我们这里用"GET":
connection.setRequestMethod("GET");//该方法同样会抛出ProtocolException异常,该异常类是IOException的子类,指在底层协议中存在错误,如 TCP 错误。
注意,安卓中该方法参数不能使用"CONNECT"。且我们可以在HttpURLConnection类中看到一个字符串数组PERMITTED_USER_METHODS,里面是不含"CONNECT"的。
然后就是我们平时在上网时或是玩网络游戏时会出现的很可恶的东西,"网络连接超时!",这时候我们要不就是重新登录要不就是放弃抵抗,而实际上,超时超时,到底超过多久时间算超时呢?实际上这是可以由我们自己设置的,代码如下:
connection.setConnectTimeout(20000);//设置超时时间20秒
connection.setReadTimeout(20000);//还可以设置读取超时的时间20秒
最后设置完成就可以连接了:
connection.connect();//请求连接
另外我们可以在尝试连接后去获取返回码与返回信息,分别是HttpURLConnection的getResponseCode()和getResponseMessage()两个方法,返回值类型分别为int和String类型。
可以从上图看出,返回信息显示OK就是连接成功了,而返回码为200,在HttpURLConnection中的静态常量HTTP_OK值就是200,就是表示HTTP请求成功的意思,实际上还有很多返回码,像我们常用的404.
3.获取服务器返回的输入流
我们之前的请求其实令我们获得了百度首页的HTML文本,它以输入流的形式传给了我们,而我们需要获得这个输入流只需要使用HttpURLConnection的getInputStream()方法就行了:
InputStream inputStream = connection.getInputStream();
当我们完成我们需要的操作时,应该断开HTTP连接:
connection.disconnect();
二、获取网站HTML文本小DEMO
主要功能,输入网站地址后点击按钮,在底下的文本框中显示HTML文本内容。
注意,一定要在AndroidManifest.xml中申请网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
主活动HttpActivity的XML布局 activity_http.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.qdf.test.HttpActivity">
<EditText
android:id="@+id/editText_uri"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:inputType="textUri"/>
<Button
android:id="@+id/btn_getData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/editText_uri"
android:layout_centerHorizontal="true"
android:text="获取数据"/>
<ScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/btn_getData"
android:layout_centerHorizontal="true">
<TextView
android:id="@+id/textView_present"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="New Text"
android:textSize="15sp"/>
</ScrollView>
</RelativeLayout>
其中用来显示HTML文本的TextView放在ScrollView中,因为一个屏幕可能显示不完整个HTML文本。
主活动代码HttpActivit.class:
package com.qdf.test;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class HttpActivity extends AppCompatActivity {
private EditText mUriEditText;
private TextView mPresentTextView;
private Button mGetDataButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_http);
mUriEditText = (EditText) findViewById(R.id.editText_uri);
mPresentTextView = (TextView) findViewById(R.id.textView_present);
mGetDataButton = (Button) findViewById(R.id.btn_getData);
mGetDataButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String url = getEditTextUrl(mUriEditText).trim();
if (!url.startsWith("http://")) {
Toast.makeText(HttpActivity.this, "URL输入错误",Toast.LENGTH_SHORT).show();
return;
}
new RequestNetWorkDataTask().execute(url);
}
});
}
private String getEditTextUrl(EditText editText) {
return editText != null ? editText.getText().toString() : "";
}
private String requestData(String strUrl) {
HttpURLConnection connection = null;
try {
URL url = new URL(strUrl);
//打开链接
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(20000);//设置超时时间
connection.setRequestMethod("GET");//HTTP的请求方式,GET是发送一个请求来取得服务器上的某一资源。
connection.connect();//请求连接
int responseCode = connection.getResponseCode();
String responseMessage = connection.getResponseMessage();
Log.i("tag", "responseCode: " + responseCode + " responseMessage: " + responseMessage);
if (responseCode == HttpURLConnection.HTTP_OK) {
//当连接成功时
InputStream inputStream = connection.getInputStream();
Reader reader = new InputStreamReader(inputStream, "UTF-8");
char[] buffer = new char[1024];
int len = 0;
String result = null;
while ((len = reader.read(buffer)) != -1) {
result = result + new String(buffer, 0, len);
}
reader.close();
return result;
}
} catch (MalformedURLException e) {
e.printStackTrace();
Toast.makeText(HttpActivity.this, "非法的URL", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
return null;
}
class RequestNetWorkDataTask extends AsyncTask<String, Integer, String> {
//在后台work之前
@Override
protected void onPreExecute() {
//在主线程中
//可加载数据,如:UI Loading
super.onPreExecute();
}
@Override
protected String doInBackground(String[] params) {
return requestData(params[0]);
}
@Override
protected void onPostExecute(String result) {
//执行完之后在主线程中
super.onPostExecute(result);
mPresentTextView.setText(result);
}
@Override
protected void onProgressUpdate(Integer... values) {
//进度更新操作
super.onProgressUpdate(values);
}
}
}
因为IO操作应该放在线程中,但因为我们要将获得的HTML文本输入到TextView上,这是UI操作不可以在线程中进行,我们这里利用了AsyncTask来解决,使用起来很方便,也可以使用Handler来处理。关于AsyncTask,推荐一篇博客:[详解Android中AsyncTask的使用]
稍微说一下requestData()这个方法,当我点击“获取数据”按钮后就会先将文本输入框中的字符串(url)获取,然后传入RequestNetWorkDataTask类的匿名对象,而requestData()方法将在RequestNetWorkDataTask的doInBackground()方法内执行,即开启线程并运行了requestData()方法,而为什么requestData()方法的参数是params[0]呢,首先这个params[0]就是我们之前传进来的url,而这个params在AsyncTask是个可变参数,它的数量是可变的,最后JAVA将其处理为数组,现在我们只传了一个字符串(url),那它自然是这个数组的第一位元素(params[0])。
而当我们connection.connect()连接后,获取了回应码,我们通过它来判断是否连接成功,如果成功了才去获取输入流。最后我将输入流内的信息都存放在result这个字符串中,然后返回这个值。而这个值正是doInBackground的返回值,它被onPostExecute()方法接收,而此时已经回到了UI线程,我们再将其输出到TextView上。
最后的效果,说起来我也应该学习一下HTML呢。
总结
其实我挺害怕学习网络编程的,因为我觉得网络这块领域真的好庞大,事实上确实自己懂得不是很多,所以先灌了自己一口鸡汤,未来继续拼搏吧。