9.1 WebView的用法
9.2 使用HTTP协议访问网络
9.2.1 使用HttpURLConnection
9.2.2 使用OkHttp
9.3 解析XML格式数据
9.3.1 Pull解析方式
9.3.2 SAX解析方式
9.4 解析JSON格式数据
9.4.1 使用JSONObject
9.4.2 使用GSON
9.5 网络编程的最佳实践
9.6 小结与点评
要求在应用程序里展示一些网页,不允许打开系统浏览器,这时候就要借助WebView控件了。
创建WebActivity,最简单
package com.dak.administrator.firstcode.net;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.dak.administrator.firstcode.R;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class WebActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
WebView webView = findViewById(R.id.web_view);
//支持JavaScript脚本
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("http://www.baidu.com");
}
}
Xml页面:
当然不要忘记加上网络权限。
HTTP 协议,其工作原理很简单:客户端向服务器发出一条 HTTP 请求,服务器收到请求后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理。
这里我们使用HttpURLConnection ,对于任何网络请求,我们应该在子线程中进行。
这里可以new Thread 一下,我就不写了。
private void HttpUrl(){
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL("www.baidu.com").openConnection();
InputStream s=connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(s));
String str = "";
while ((str = reader.readLine()) != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
若是想要提交数据给服务器只需把请求方法改为 POST,并在获取输入流之前把要提交的数据写出即可。注意每条数据要以键值对的形式存在,数据与数据之间用 “&” 隔开,比如向服务器提交用户名和密码可写成:
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
Okhttp 是由大名鼎鼎的Square公司开发的,这个公司在开源事业上面贡献良多,除了Okhttp外,还开发了像Picasso、Retrofit等著名的开源项目。
Okhttp 项目主页地址是:https://github.com/square/okhttp
在使用 OKHttp 前,需要在项目中添加 OKHttp 库的依赖,如下:
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
接下来创建Activity
package com.dak.administrator.firstcode.net;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import com.dak.administrator.firstcode.R;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class OkHttpActivity extends AppCompatActivity {
private static final String TAG = "OkHttpActivity===";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ok_http);
getRequest();
}
private void getRequest() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://www.baidu.com")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
}
private void postRequest() {
try {
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build();
Request request = new Request.Builder()
.url("www.baidu.com")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
}
}
相比 HttpURLConnection,OKHttp 简单易用,若是发起一条 POST 请求,会比 GET 请求稍微复杂点,需要构建一个 RequestBody 对象来存放待提交的参数:
RequestBody requestBody = new FormBody.Builder()
.add("username","admin")
.add("password","123456")
.build();
然后在 Request.Builder 中调用一下 post() 方法,并将 RequestBody 对象传入:
Request request = new Request.Builder()
.url("http://www.baidu.com")
.post(RequestBody)
.build();
在网络传输数据时,最常用的格式有两种:XMl和JSON,下面我们就来一个个地学习吧。
首先创建一个xml文件
1
Google maps
1.0
2
Chrome
2.1
3
Google Play
2.3
private void xmlWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData));
int eventType = xmlPullParser.getEventType();
String id = "";
String name = "";
String version = "";
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(TAG, "xmlWithPull: id"+id);
Log.d(TAG, "xmlWithPull: name"+name);
Log.d(TAG, "xmlWithPull: version"+version);
}
break;
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void xmlWithSax(String xmlData){
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
MyHandler handler=new MyHandler();
xmlReader.setContentHandler(handler);
xmlReader.parse(new InputSource(xmlData));
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
创建所需要的DefaultHandler
package com.dak.administrator.firstcode.net;
import android.util.Log;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Created by Administrator on 2018/10/29.
*/
public class MyHandler extends DefaultHandler {
private static final String TAG = "MyHandler===";
String nodeName;
StringBuilder id,name, version;
/**
* 开始解析时调用
*
* @throws SAXException
*/
@Override
public void startDocument() throws SAXException {
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
/**
* 完成整个xml解析后使用
*
* @throws SAXException
*/
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
/**
* 开始解析 某个节点时时调用
*
* @param uri
* @param localName
* @param qName
* @param attributes
* @throws SAXException
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
nodeName = localName;//记录当前节点名
}
/**
* 完成解析某个节点调用
*
* @param uri
* @param localName
* @param qName
* @throws SAXException
*/
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("app".equals(localName)) {
Log.d(TAG, "xmlWithPull: id"+id.toString().trim());
Log.d(TAG, "xmlWithPull: name"+name.toString().trim());
Log.d(TAG, "xmlWithPull: version"+version.toString().trim());
id.setLength(0);
name.setLength(0);
version.setLength(0);
}
}
/**
* 获取某个节点内容时调用
* @param ch
* @param start
* @param length
* @throws SAXException
*/
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
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);
}
}
}
/**
* 用 JSONObject 解析
* @param jsonData 需要解析的数据
*/
private void parseJSONWithJSONObject(String jsonData) {
try {
// 把需要解析的数据传入到 JSONArray 对象中
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 sex = jsonObject.getString("sex");
Log.d("JSONObject解析", "id is "+id);
Log.d("JSONObject解析", "name is "+name);
Log.d("JSONObject解析", "sex is "+sex);
}
}catch (Exception e){
e.printStackTrace();
}
}
添加相关依赖
implementation 'com.google.code.gson:gson:2.8.2'
GSON 可以将一段 JSON 格式的字符串自动映射成一个对象,从而不需要手动去编写代码进行解析了。
比如解析一段 JSON 格式数据:
{"name":"Tom","age":20}
就可以定义一个 Person 类,并加入 name 和 age 两字段,然后只需调用如下代码就可以将 JSON 数据自动解析成一个 Person 对象:
Gson gson = new Gson();
Person person = gson.fromJson(jsonData,Person.class);
若解析一段 JSON 数组会麻烦些,需要借助 TypeToken 把期望解析成的数据类型传入到 fromJson() 方法中:
List people = gson.fromJson(jsonData,new TypeToken>(){}.getType());
GSON 的基本用法就是这样。下面来解析上面的 JSON 文本,首先新增一个 Student 类:
public class Student {
private String id;
private String name;
private String sex;
// Getter and Setter
. . .
}
接下来就非常简单了,代码如下:
/**
* 用 GSON 解析
* @param jsonData
*/
private void parseJSONWithGSON(String jsonData){
Gson gson = new Gson();
ListstudentList = gson.fromJson(jsonData,new TypeToken>(){}.getType());
for (Student student:studentList){
Log.d("GSON解析", "id is "+student.getId());
Log.d("GSON解析", "name is "+student.getName());
Log.d("GSON解析", "sex is "+student.getSex());
}
}
这里涉及到两个问题:首先是网络连接的代码比较长,所以可以把它封装在一个类里面,然后设置一个静态的方法,每次要进行网络连接的时候调用它就可以了;还有一个问题是由于网络连接需要开启子线程,然而子线程又不能返回数据,所以需要设置回调函数。
这里首先创建一个接口,意思就是回调函数:
public interface HttpCallbackListener {
void finish(String response);
void onError(Exception e);
}
然后定义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.setDoInput(true);
connection.setDoOutput(true);
InputStream in = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
listener.finish(sb.toString());
}
catch (Exception e) {
e.printStackTrace();
listener.onError(e);
}
finally {
if (connection != null) connection.disconnect();
}
}
}).start();
}
}
这里就是发送请求然后接收返回的数据的代码,增加的地方就是调用接口的函数。然后我们在MainActivity里调用它,注意接口都是要先实例化之后才可以调用。
private void sendHttpRequestWithHttpUtil() {
String address = "http://www.baidu.com";
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onError(Exception e) {
Log.d("MainActivity", "连接失败");
}
@Override
public void finish(String response) {
Message msg = new Message();
msg.obj = response;
handler.sendMessage(msg);
}
});
}
需要注意的地方是接口中的代码依然是在子线程中执行的,所以也不能直接修改UI。
来源:https://blog.csdn.net/xiaoliizi/article/details/50773218?locationNum=4
郭霖总结:
本章中我们主要学习了在Android 中使用HTTP协议来进行网络交互的知识,虽然Android中支持的网络通信协议有很多种,但HTTP协议无疑是最常用的一种。 通常我们有两种方式来发送HTTP请求,分别是HtpURLConnection和OktHtp,相信这两种方式你都已经很好地掌握了。
接着我们又学习了XML和JSON格式数据的解析方式,因为服务器响应给我们的数据般都是属于这两种格式的。无论是XML还是JSON,它们各自又拥有多种解析方式,这里我们只是学习了最常用的几种,如果以后你的工作中还需要用到其他的解析方式,可以自行去学习。
本章的最后同样是最佳实践环节,在这次的最佳实践中,我们主要学习了如何利用Java的回调机制来将服务器响应的数据进行返回。其实除此之外,还有很多地方都可以使用到Java的回调机制,希望你能举一反三, 以后在其他地方需要用到回调机制时都能够灵活地使用。
在进行了一章多媒体和一章网络的相关知识学习后,你是否想起来Android四大组件中还剩一个没有学过呢! 那么下面就让我们进人到Android 服务的学习旅程之中。
我的总结:
网络技术是必不可缺的一块内容,在工作中,我们最常用的就是网络了,对于刚刚入职的小伙伴们,或许公司中有封装好的框架,但是对于一些技术要点,一定要弄得明明白白的。