Android学习随笔(15)------网络技术
学习流程来自《第一行代码》(第二版)
现在的Android手机基本上都是能够上网的,这样利用网络我们能开发出越来越多有意思的应用。
WebView
有时候我们需要在应用中显示一个网页,但是我们不可能自己去编写一个浏览器,Android提供了一个WebView控件。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<WebView android:id="@+id/web_view" android:layout_height="match_parent" android:layout_width="match_parent" />
</LinearLayout>
直接在布局中引入控件。
申请网络权限 :
<uses-permission android:name="android.permission.INTERNET" />
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.setWebViewClient(new WebViewClient()); // 当需要从一个网页跳转到另一个网页时仍在当前WebView中显示
webView.loadUrl("http://baidu.com"); // 要加载的Url
}
}
运行应用后,可以看到直接打开了百度的网页。
HTTP协议
客户端向服务器发送一条HTTP请求,服务器收到请求之后会返回数据给客户端,客户端再对数据进行解析处理就可以了。
HttpURLConnection
用于发送或接收数据。
- 利用URL的openConnection()方法来获取实例。
- 设置Http请求所使用的方法。GET表示希望从服务器上获取数据。POST表示提交数据给服务器。
- 还可以进行一些其他设置。
- 调用getInputStream()方法,获取到服务器返回的输入流。
- 对输入流进行读取。
disconnect()方法关闭HTTP连接。
<?xml version=”1.0” encoding=”utf-8”?>
利用在TextView外面嵌套一个ScrollView来滚动显示,屏幕显示不全的信息。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button sendRequest = (Button) findViewById(R.id.send_request);
responseText = (TextView) findViewById(R.id.response_text);
sendRequest.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send_request:
sendRequestWithHttpURLConnection(); // Use HttpURLConnection
break;
default:
break;
}
}
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"); // 设置发送GET(默认)请求
connection.setConnectTimeout(8000); // 设置连接超时
connection.setReadTimeout(8000); // 读取超时
// 获取服务器返回的状态码 connection.getResponseCode(); 200说明请求成功
InputStream in = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
showResponse(response.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
private void showResponse(final String response) { // 显示
runOnUiThread(new Runnable() {
@Override
public void run() {
responseText.setText(response); // 进行UI操作
}
});
}
一些耗时操作会导致 手机提醒ANR(Application Not Responding)所以访问网络要放在子线程中进行。
子线程中更新UI会导致android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
因为百度全站改用了https的协议,所以用http会提示302,在浏览器上输入http://baidu.com会进行一个跳转,百度自动给你跳转到https://www.baidu.com上,这个跳转由一个302页面来进行,动作很快,基本察觉不到。这就是为什么得到的是302而不是200的原因。
这里收到的就是未经解析的HTML语言。
提交数据,例 :
connection.setRequestMethod("POST");
DataOUtputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
OkHttp
OkHttp是一个比较好的开源库
在dependencies闭包下添加 :
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
- 创建一个OkHttpClient实例
- 创建一个request对象
- 调用OkHttpClient的newCall()方法创建一个Call对象,并调用它的execute()方法发送请求并获取服务器返回的数据。
发送POST,需要先构建RequestBody对象来存放待提交的参数
RequestBody requestBody = new FormBody.Bulider()
.add("username", "admin")
.add("passward", "123456")
.build();
在request中调用
Request request = new Request.Builder()
.url("https://www.baidu.com")
.post(requestBody)
.build();
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send_request:
// sendRequestWithHttpURLConnection(); // Use HttpURLConnection
sendRequestWithOKHttp(); // Use OKHttp
break;
default:
break;
}
}
private void sendRequestWithOKHttp() {
new Thread(new Runnable() {
@Override
public void run() {
try{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://baidu.com") // 获取百度的html信息
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
showResponse(responseData); // 在textview中展示
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
解析数据
从Apache的官网上下载一个Tomcat,在tomcat目录下的tomcat-8.5.23\webapps\ROOT文件夹中新建一个
get_data.xml文件 :
<apps>
<app>
<id>1</id>
<name>Google Map</name>
<version>1.0</version>
</app>
<app>
<id>2</id>
<name>Chrome</name>
<version>2.1</version>
</app>
<app>
<id>3</id>
<name>Google Play</name>
<version>2.3</version>
</app>
</apps>
电脑本机访的网址是http://127.0.0.1:8080/get_data.xml
XML
xml文件写入到手机
FileOutputStream fos = null;
try {
XmlSerializer serializer = Xml.newSerializer(); // 获取XmlSerializer的实例
File file = new File(Environment.getExternalStorageDirectory().getPath(), "serializer.xml");
fos = new FileOutputStream(file);
serializer.setOutput(fos, "utf-8"); // 设置Xml序列化器的参数
serializer.startDocument("utf-8", true); // 开始写xml文档开头 xml文件的编码 是否是一独立的xml文件
serializer.startTag(null, "apps"); // 写xml的根节点,namespace 命名空间
for (App app : AppList) { // 循环写app节点
serializer.startTag(null, "app");
serializer.startTag(null, "id");
serializer.text(app.getId());
serializer.endTag(null, "id");
serializer.startTag(null, "name");
serializer.text(app.getName());
serializer.endTag(null, "name");
serializer.startTag(null, "version");
serializer.text(app.getVersion());
serializer.endTag(null, "version");
serializer.endTag(null, "app");
}
serializer.endTag(null, "apps");
serializer.endDocument(); // 写文档结尾
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
对于XML解析有两个种方式Pull SAX
Pull
一行一行解析
基于事件解析
private void sendRequestWithOKHttp() {
new Thread(new Runnable() {
@Override
public void run() {
try{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://10.0.2.2:8080/get_data.xml") // 访问PC本地tomcat上的get_data.xml
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithPull(responseData); // 利用Pull方式解析url中的xml
parseJSONWithGson(responseData);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private void parseXMLWithPull(String xmlData) {
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); // 获取实例
XmlPullParser xmlPullParser = factory.newPullParser(); // Xml.newPullParser();
xmlPullParser.setInput(new StringReader(xmlData)); // 将服务器返回的XML数据设置进去 或(in, "utf-8")服务器返回的数据流
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(); // 获取节点内的具体内容 开始标签中有id属性xmlPullParser.getAttributeValue(0)
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("admin", "id is " + id);
Log.d("admin", "name is " + name);
Log.d("admin", "version is " + version);
}
break;
}
default:
break;
}
eventType = xmlPullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
}
SAX
新建ContentHandler类继承自DefaultHandler :
public class ContentHandler extends DefaultHandler {
private String nodeName;
private StringBuilder id;
private StringBuilder name;
private StringBuilder version;
@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 { // 解析出的数据会以参数的形式传入 此方法可能会被调用多次 一些换行符也被当作内容解析 需要控制
if ("id".equals(nodeName)) { // 根据当前节点名 判断将内容添加到哪个StringBuilder对象中
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());
id.setLength(0); // 将StringBuilder清空
name.setLength(0);
version.setLength(0);
}
}
@Override
public void endDocument() throws SAXException { // 完成整个文档的时候调用
super.endDocument();
}
}
调用 :
private void parseXMLWithSAX(String xmlData) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
XMLReader reader = factory.newSAXParser().getXMLReader();
ContentHandler handler = new ContentHandler();
reader.setContentHandler(handler); // 将ContentHandler实例设置到XMLReader中
reader.parse(new InputSource(new StringReader(xmlData))); // 开始执行解析
} catch (Exception e) {
e.printStackTrace();
}
}
JSON
在tomcat中新建
get_data.json :
[{"id":"5","version":"5.5","name":"Clash of Clans"},
{"id":"6","version":"7.0","name":"Boom Beach"},
{"id":"7","version":"3.5","name":"Clash Royale"}]
使用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("admin", "id is " + id);
Log.d("admin", "name is " + name);
Log.d("admin", "version is " + version);
}
} catch (Exception e) {
e.printStackTrace();
}
}
使用GSON
compile 'com.google.code.gson:gson:2.7'
根据要从服务器上接收的数据定义一个类
app :
public class App {
private String id;
private String name;
private String version;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
正好对应三个字段。
调用 :
private void parseJSONWithGson(String jsonData) {
Gson gson = new Gson();
List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>(){}.getType()); // 借助TypeToken将期望解析成的数据类型传入到fromJson()方法中
for (App app : appList) {
Log.d("admin", "id is " + app.getId());
Log.d("admin", "name is " + app.getName());
Log.d("admin", "version is " + app.getVersion());
}
}
此博文为个人学习笔记,仅供个人学习使用,希望对大家有帮助。
还没有评论,来说两句吧...