概览
9. 使用网络技术
9.1 WebView的用法
流程:
- 首先在布局文件中加入WebView标签
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</WebView>
-
在主活动中获取并进行设置
- webView.getSettings():设置浏览器的属性
- webView.setWebViewClient(new WebViewClient()):当需要从一个网页跳转到另一个网页时,目标网页仍在当前WebView显示
- webView.loadUrl():传入网址
- 最后在Manifest文件中声明访问网络的权限: < uses-permission android:name=“android.permission.INTERNET”/>
代码示例:
WebView webView = (WebView)findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
//当需要从一个网页跳转到另一个网页时,目标网页仍在当前WebView显示
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("https://www.baidu.com");
9.2 使用HTTP协议访问网络
9.2.1 使用HttpURLConnection
HttpURLConnection的用法:
- 首先new出一个URL实例并传入目标网址
- 获取HttpURLConnection实例 使用(HttpURLConnection)url.openConnection()方法获取
- 通过connection实例进行一些设置
connection.setRequestMethod(“GET”):设置请求方法,GET或POST
connection.setConnectTimeout(8000):设置连接超时时间
connection.setReadTimeout(8000):设置读取超时的时间 - 通过 connection.getInputStream()方法获取字节输入流,并对输入流进行读取
- 通过connection.disconnect()方法将连接关闭
代码示例:
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();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
InputStream in = connection.getInputStream();
//对获取到的流进行读取
reader = new BufferedReader(new InputStreamReader(in)); //将字节流转换为字符流
StringBuilder response = new StringBuilder();
String line;
while((line = reader.readLine())!=null){
response.append(line);
}
Log.d("MainActivity",response.toString());
//TODO: 将读取到的内容显示到界面上
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(reader!=null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if(connection!=null){
connection.disconnect();
}
}
}
}).start();
}
9.2.2 使用OkHttp
OkHttp的用法:
- 在Build.gradle文件中添加依赖: implementation ‘com.squareup.okhttp3:okhttp:3.4.1’
- 创建一个OkHttpClient的实例
- 创建一个Request对象: Request request = new Request.Builder()
.url(“网址”)
.build(); - 调用newCall()方法并传入刚才创建的Request对象,创建一个Call对象并调用execute()方法发送请求并获取服务器返回的数据,返回一个Response对象
- 调用response.body().string()方法获取返回的数据
- 如果是发POST请求,需要先创建一个RequestBody对象
RequestBody body = new FormBody.Builder()
.add() 以键值对的形式存放数据
.build()
在Request.Builder中再调用post()方法,并传入创建好的requestbody
代码示例:
- 发送GET请求:
private void sendRequestWithOkHttp(){
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();
Response response = client.newCall(request).execute();
String resData = response.body().string();
//TODO: 将读取到的内容显示到界面上
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
- 发送POST请求
private void sendRequestWithOkHttp(){
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
//添加请求参数
.add("username","admin")
.add("password","123456")
.build();
Request request = new Request.Builder()
.url("https://www.baidu.com")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
String resData = response.body().string();
//TODO: 将读取到的内容显示到界面上
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
9.3 解析XML格式数据
9.3.1 使用Pull解析方式
- 通过XmlPullParserFactory.newInstance()实例通过newPullParser()方法来获取 xmlPullParser对象
- 通过setInput()方法将服务器返回的XML数据设置进去,传入一个Reader对象
- 通过getEventType()方法得到当前的解析事件
- 利用while循环进行解析
通过getName()得到当前节点的名字,
根据switch(eventType):通过节点名对比,使用nextText()方法获取当前节点的具体内容
case XmlPullParser.START_TAG: 开始解析某个节点
case XmlPullParser.END_TAG:完成解析某个节点
调用next()方法获取下一个解析事件即更新eventType
代码示例:
//Pull解析xml
private void parseXMLWithPull(String resData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(resData)); //将xml数据传给xmlPullParser
//得到当前的解析事件
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("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解析方式
-
首先新建一个类继承自DefaultHandler,并重写父类的5个方法
定义StringBuilder对象和nodeName对象- startDocument():在开始XML解析的时候调用
初始化StringBuilder对象 - startElement():在开始解析某个节点时调用
记录当前节点名 nodeName = localName - characters():获取节点内容时,多次调用
根据当前的节点名判断获取对应节点具体内容 - endElement():在完成解析某个节点的时候调用
在解析完一个节点后,通过setLength(0)方法将StringBuilder对象清空,以防影响下一个节点的解析 - endDocument():在完成整个XML解析的时候调用
- startDocument():在开始XML解析的时候调用
-
通过SAXParserFactory.newInstance()获取factory的实例再通过factory.newSAXParser().getXMLReader()方法获取 XML
将contentHandler的实例通过setContentHandler()方法设置到XMLReader中
开始执行解析 parse(new InputSource(new StringReader(data)));
代码示例:
- 新建类继承自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 {
super.startDocument();
id = new StringBuilder();
name = new StringBuilder();
version = new StringBuilder();
}
//在开始解析某个节点时调用
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
//记录当前节点名
nodeName = localName;
}
//在完成解析某个节点的时候调用
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
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();
}
//获取节点内容时,多次调用
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
//根据当前的节点名判断将内容添加到哪一个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);
}
}
}
- 在主活动中使用
//SAX方式解析XML
private void parseXMLWithSAX(String resData) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
//将contentHandler的实例设置到XMLReader中
xmlReader.setContentHandler(handler);
//开始执行解析
xmlReader.parse(new InputSource(new StringReader(resData)));
} catch (Exception e) {
e.printStackTrace();
}
}
9.4 解析JSON格式数据
9.4.1 使用JSONObject
- 如果服务器中JSON数据为一个JSON对象,那么将json字符串传入JSONObject的构造方法中
- 如果服务器中JSON数据为一个JSON数组,那么将数据传入到 JSONArray对象中
- 循环遍历 JSONArray,通过jsonArray.getJSONObject(i)取出的对象都是一个JSONObject对象
- 再通过getString()方法将其中的数据取出
代码示例:
private void parseJSONWithJSONObject(String resData) {
try {
//JSON对象
//JSONObject object = new JSONObject(resData);
//JSON数组
JSONArray jsonArray = new JSONArray(resData);
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("ParseJSONActivity","id is "+id);
Log.d("ParseJSONActivity","name is "+name);
Log.d("ParseJSONActivity","version is "+version);
}
} catch (Exception e) {
e.printStackTrace();
}
}
9.4.2 使用GSON
- 首先在build.gradle文件中添加依赖 implementation ‘com.google.code.gson:gson:2.7’
- 定义一个要解析的数据的实体类
- 如果要解析是一个JSON对象则使用
Gson gson = new Gson();
通过 gson.fromJson(data,实体类.class)获取解析后的对象 - 如果是要解析一个JSON数组则使用
Gson gson = new Gson();
List<实体类> list = gson.fromJson(data,new TypeToken<List<实体类>>(){}.getType());
代码示例:
- 实体类
public class App {
private String id;
private String name;
private String version;
}
- 对JSON数组进行解析
private void parseJSONWithGSON(String resData) {
Gson gson = new Gson();
List<App> appList = gson.fromJson(resData,new TypeToken<List<App>>(){}.getType());
for(App app:appList){
Log.d("ParseJSONActivity","id is "+app.getId());
Log.d("ParseJSONActivity","name is "+app.getName());
Log.d("ParseJSONActivity","version is "+app.getVersion());
}
}
9.5 网络编程的最佳实践
- 创建一个用于回调的接口
public interface HttpCallbackListener {
void onFinish(String response);// 表示当服务器响应成功时调用
void onError(Exception e);// 表示当服务器出现错误时调用
}
- 创建一个用于发起网络请求的Util类
public class HttpUtil {
//使用HttpURLConnection发送
public static void sendHttpRequest(final String address,final HttpCallbackListener listener){
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
BufferedReader reader = 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();
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) {
if(listener!=null){
listener.onError(e);
}
} finally {
try {
if(reader!=null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if(connection!=null){
connection.disconnect();
}
}
}
}).start();
}
//使用OkHttp发送
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);
}
}
- 在主活动中进行使用
/**
* 回调接口还是在子线程运行,所以不能进行UI操作
*/
public class HttpUtilTestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_http_util_test);
//调用 sendHttpRequest()方法
String address = "https://www.baidu.com";
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
//根据返回内容执行具体的逻辑
}
@Override
public void onError(Exception e) {
//对异常情况进行处理
}
});
//调用 sendOkHttpRequest()方法
HttpUtil.sendOkHttpRequest(address,new okhttp3.Callback(){
@Override
public void onFailure(Call call, IOException e) {
//对异常情况进行处理
}
//得到服务器返回的具体内容
@Override
public void onResponse(Call call, Response response) throws IOException {
String responseData = response.body().string();
Log.d("HttpUtilTestActivity",responseData);
}
});
}
}