知识点目录
知识点回顾
9.1 WebView的用法
WebView的主要作用就是在自己应用程序中显示网页,借助它我们可以在应用程序中嵌入一个浏览器。
一般使用步骤如下:
-
在布局中使用WebView控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent"> </WebView> </LinearLayout>
-
实现网页跳转
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = (WebView) findViewById(R.id.web_view); webView.getSettings().setJavaScriptEnabled(true); //支持JavaScript //当需要从一个网页跳转到另一个网页时,希望目标网页仍在当前WebView中显示,而不是打开系统浏览器 webView.setWebViewClient(new WebViewClient()); webView.loadUrl("http://www.baidu.com"); //转入要跳转的网址 } }
-
添加网络访问权限
<uses-permission android:name="android.permission.INTERNET"/>
效果图:
在运行程序之前,需要确保手机或者模拟器是连网的。
WebView中已经帮我们做了如下步骤的封装:
-
发送HTTP请求
-
接收服务响应
-
解析返回的数据
-
页面展示
9.2 使用HTTP协议访问网络
HTTP协议的工作原理:
客户端向服务器发送一条HTTP请求,服务器收到请求之后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理。
9.2.1 使用HttpURLConnection
Android原生发送HTTP请求的方式有两种:
-
HttpClient
-
HttpURLConnection
但HttpClient存在API数量过多、扩展困难等缺点,Android团队已经不建议我们使用HttpClient。在Android6.0系统中,HttpClient的功能已经被完全移除。
Android官方建议使用HttpURLConnection。
HttpURLConnection一般使用步骤:
1.布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/send_request"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send Request"
android:textAllCaps="false"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/response_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
ScrollView可以让用户以滚动的形式查看屏幕外的那部分内容。
2.使用示例代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mResponseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = (Button) findViewById(R.id.send_request);
mResponseText = (TextView) findViewById(R.id.response_text);
sendRequest.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.send_request) {
sendRequestWithHttpURLConnection();
}
}
private void sendRequestWithHttpURLConnection() {
//开启线程来发送网络请求
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL url = new URL("https://www.baidu.com");
connection = ((HttpURLConnection) url.openConnection());
// 设置请求方式,默认为GET
connection.setRequestMethod("GET");
// 设置连接超时
connection.setConnectTimeout(8000);
// 设置读取超时
connection.setReadTimeout(8000);
// 获取网络的输入流
InputStream inputStream = connection.getInputStream();
// 读取输入流中的数据
reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
showResponse(response.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private void showResponse(final String response) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//在这里进行UI操作,将结果显示到界面上
mResponseText.setText(response);
}
});
}
}
因为Android是不允许在子线程中进行UI操作的,这里我们通过使用runOnUiThread()方法将线程切换到主线程,然后再更新UI元素。
3.添加权限
<uses-permission android:name="android.permission.INTERNET"/>
4.Post请求
如果想要给服务器提交数据,只需要将HTTP请求方法改为POST,并在获取输入流之前把要提交的数据写出即可。每条数据之间以键值对的形式存在,数据与数据之间用&符号隔开。如下所示:
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
效果图:
9.2.2 使用OkHttp
OkHttp是Square公司开发的一个开源盛行的网络通信开源库。
OkHttp的项目主页地址是:
https://github.com/square/okhttp
可以在主页看到当前最新的版本。
在使用OkHttp之前,需要先在app/build.gradle文件的dependencies闭包中添加如下内容:
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.2.2")
}
OkHttp具体用法如下:
get请求:
new Thread(new Runnable() {
@Override
public void run() {
try {
//创建一个OkHttpClient实例
OkHttpClient client = new OkHttpClient();
//创建一个Request对象
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
//创建Call对象,并调用它的execute()方法发送请求并获取服务器返回的数据
Response response = client.newCall(request).execute();
String responseData = response.body().string();
showResponse(responseData);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
POST请求:
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
RequestBody body = new FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(body)
.build();
// 创建Call对象,并调用它的execute()方法发送请求并获取服务器返回的数据
Response response = client.newCall(request).execute();
String responseData = response.body().string();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
上面只对OkHttp的get和post进行简单的使用,更多的使用还需要到具体项目中去体验。
9.3 解析XML格式数据
解析XML格式的数据有很多种,我们这里主要学习下Pull解析和SAX解析。
9.3.1 Pull解析方式
具体用法如下:
private void parseXMLWithPull(String xmlData) {
try {
//获取一个XmlPullParserFactory实例对象
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
//通过XmlPullParserFactory实例对象获得一个XmlPullParser对象
XmlPullParser xmlPullParser = factory.newPullParser();
//将XML数据设置进去
xmlPullParser.setInput(new StringReader(xmlData));
//通过getEventType()得到当前的解析事件
int eventType = xmlPullParser.getEventType();
String id = "";
String name = "";
String version = "";
//开始解析,如果当前的解析事件不等于XmlPullParser.END_DOCUMENT,则表示解析还没完成
while (eventType != XmlPullParser.END_DOCUMENT) {
String nodeName = xmlPullParser.getName();
switch (eventType) {
//开始解析某个节点
case XmlPullParser.START_TAG:{
if ("id".equals(nodeName)) {
id = xmlPullParser.nextText();
} else if ("name".equals(nodeName)) {
name = xmlPullParser.nextText();
} else if ("version".equals(nodeName)) {
version = xmlPullParser.nextText();
}
break;
}
//完成解析某个节点
case XmlPullParser.END_TAG:{
if ("app".equals(nodeName)) {
Log.d("MainActivity", "id is = " + id);
Log.d("MainActivity", "name is = " + name);
Log.d("MainActivity", "version is = " + version);
}
break;
}
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
9.3.2 SAX解析方式
SAX解析比Pull解析复杂一些,但在语义方面会更加清楚。
具体用法如下:
第一步:新建一个类继承自DefaultHandler
public class ContentHandler extends DefaultHandler {
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
//开始XML解析时调用
@Override
public void startDocument() throws SAXException {
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
//开始解析某个节点时调用
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// 记录当前节点
nodeName = localName;
}
//获取去节点中内容的时候调用
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
// 根据当前的节点名判断将内容添加到哪一个StringBuilder对象中
if ("id".equals(nodeName)) {
id.append(ch, start, length);
} else if ("name".equals(nodeName)) {
name.append(ch, start, length);
} else if ("version".equals(nodeName)) {
version.append(ch, start, length);
}
}
//完成解析某个节点的时候调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("app".equals(localName)) {
Log.d("ContentHandler", "id is = " + id.toString().trim());
Log.d("ContentHandler", "name is = " + name.toString().trim());
Log.d("ContentHandler", "version is = " + version.toString().trim());
// 最后要将StringBuilder清空掉
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
//完成整个XML解析时调用
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
}
第二步:开始解析
private void parseXMLWithSAX(String xmlData) {
try {
//获取一个SAXParserFactory对象
SAXParserFactory factory = SAXParserFactory.newInstance();
//获取一个XMLReader对象
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
//new一个ContentHandler对象
ContentHandler handler = new ContentHandler();
//将ContentHandler的实例设置到XMLReader中
xmlReader.setContentHandler(handler);
//开始执行解析
xmlReader.parse(new InputSource(new StringReader(xmlData)));
} catch (Exception e) {
e.printStackTrace();
}
}
9.4 解析JSON格式数据
相比于XML,JSON数据的主要优势是体积更小,在网上传输的时候可以更省流量。但缺点在于,JSON语义性较差,看起来不如XML直观。
解析JSON数据的方法有很多种,我们主要学习官方提供的JSONObject和谷歌开源库GSON。
9.4.1 使用JSONObject
具体用法如下:
private void parseJSONWithJSONObject(String jsonData) {
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
String id = jsonObject.getString("id");
String name = jsonObject.getString("name");
String version = jsonObject.getString("version");
Log.d("MainActivity", "id is " + id);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "version is " + version);
}
} catch (Exception e) {
e.printStackTrace();
}
}
9.4.2 使用GSON
第一步:添加GSON库的依赖
在app/build.gradle文件的dependencies闭包中添加如下内容:
dependencies {
implementation 'com.google.code.gson:gson:2.8.6'
}
第二步:编写json数据的实例类
public class App {
private String id;
private String name;
private String version;
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setVersion(String version) {
this.version = version;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
}
当然这一步可以使用GsonFormat这个插件去完成!!!
第三步:具体解析
private void parseJSONWithGSON(String jsonData) {
Gson gson = new Gson();
//JSON数据是对象型
App app = gson.fromJson(jsonData, App.class);
//JSON数据是数组型
List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>(){}.getType());
}
这里根据JSON数据的不同类型,解析方式稍微有点不同。
9.5 网络编程的最佳实践
使用HttpURLConnection网络请求的工具类。
第一步:定义个接口
public interface HttpCallbackListener {
/**
* 当服务器成功相应我们的请求时调用
* @param response 服务器返回的数据
*/
void onFinish(String response);
/**
*当网络操作出现错误的时候调用
* @param e 记录着错误的详细信息
*/
void onError(Exception e);
}
第二步:编写工具类
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) {
e.printStackTrace();
if (listener != null) {
//回调onError()方法
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
第三步:使用工具类
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
// 在这里根据返回内容执行具体的逻辑
}
@Override
public void onError(Exception e) {
// 在这里对异常情况进行处理
}
});
使用OkHttp网络请求的工具类。
第一步:定义工具类
public class HttpUtil {
public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(address)
.build();
client.newCall(request).enqueue(callback);
}
}
这里newCall()后面调用的是enqueue()方法,enqueue()方法内部已经帮我们开启了子线程。
第二步:使用工具类
HttpUtil.sendOkHttpRequest("address", new okhttp3.Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
//得到服务器返回的具体内容
String responseData = response.body().string();
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
//在这里对异常情况进行处理
}
});
9.6 小结与点评
本章主要学习了在Android中使用HTTP协议来进行网络交互。使用HttpURLConnection和OkHttp来发送HTTP请求。然后学习了XML和JSON格式数据的解析方式。