Android学习随笔(15)------网络技术

喜欢ヅ旅行 2022-06-03 03:28 306阅读 0赞

学习流程来自《第一行代码》(第二版)
现在的Android手机基本上都是能够上网的,这样利用网络我们能开发出越来越多有意思的应用。

WebView

有时候我们需要在应用中显示一个网页,但是我们不可能自己去编写一个浏览器,Android提供了一个WebView控件。

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
  3. <WebView android:id="@+id/web_view" android:layout_height="match_parent" android:layout_width="match_parent" />
  4. </LinearLayout>

直接在布局中引入控件。

申请网络权限 :

  1. <uses-permission android:name="android.permission.INTERNET" />
  2. public class MainActivity extends AppCompatActivity {
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. WebView webView = (WebView) findViewById(R.id.web_view);
  8. webView.getSettings().setJavaScriptEnabled(true); // 设置浏览器属性 支持JavaScript
  9. webView.setWebViewClient(new WebViewClient()); // 当需要从一个网页跳转到另一个网页时仍在当前WebView中显示
  10. webView.loadUrl("http://baidu.com"); // 要加载的Url
  11. }
  12. }

运行应用后,可以看到直接打开了百度的网页。

Exler

HTTP协议

客户端向服务器发送一条HTTP请求,服务器收到请求之后会返回数据给客户端,客户端再对数据进行解析处理就可以了。

HttpURLConnection

用于发送或接收数据。

  1. 利用URL的openConnection()方法来获取实例。
  2. 设置Http请求所使用的方法。GET表示希望从服务器上获取数据。POST表示提交数据给服务器。
  3. 还可以进行一些其他设置。
  4. 调用getInputStream()方法,获取到服务器返回的输入流。
  5. 对输入流进行读取。
  6. disconnect()方法关闭HTTP连接。

    <?xml version=”1.0” encoding=”utf-8”?>



利用在TextView外面嵌套一个ScrollView来滚动显示,屏幕显示不全的信息。

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. setContentView(R.layout.activity_main);
  5. final Button sendRequest = (Button) findViewById(R.id.send_request);
  6. responseText = (TextView) findViewById(R.id.response_text);
  7. sendRequest.setOnClickListener(this);
  8. }
  9. @Override
  10. public void onClick(View v) {
  11. switch (v.getId()) {
  12. case R.id.send_request:
  13. sendRequestWithHttpURLConnection(); // Use HttpURLConnection
  14. break;
  15. default:
  16. break;
  17. }
  18. }
  19. private void sendRequestWithHttpURLConnection() { // 开启线程来发起网络请求
  20. new Thread(new Runnable() {
  21. @Override
  22. public void run() {
  23. HttpURLConnection connection = null;
  24. BufferedReader reader = null;
  25. try {
  26. URL url = new URL("https://www.baidu.com");
  27. connection = (HttpURLConnection) url.openConnection();
  28. connection.setRequestMethod("GET"); // 设置发送GET(默认)请求
  29. connection.setConnectTimeout(8000); // 设置连接超时
  30. connection.setReadTimeout(8000); // 读取超时
  31. // 获取服务器返回的状态码 connection.getResponseCode(); 200说明请求成功
  32. InputStream in = connection.getInputStream();
  33. reader = new BufferedReader(new InputStreamReader(in));
  34. StringBuilder response = new StringBuilder();
  35. String line;
  36. while ((line = reader.readLine()) != null) {
  37. response.append(line);
  38. }
  39. showResponse(response.toString());
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. } finally {
  43. if (reader != null) {
  44. try {
  45. reader.close();
  46. } catch (IOException e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. if (connection != null) {
  51. connection.disconnect();
  52. }
  53. }
  54. }
  55. }).start();
  56. }
  57. private void showResponse(final String response) { // 显示
  58. runOnUiThread(new Runnable() {
  59. @Override
  60. public void run() {
  61. responseText.setText(response); // 进行UI操作
  62. }
  63. });
  64. }

一些耗时操作会导致 手机提醒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的原因。

Exler
这里收到的就是未经解析的HTML语言。

提交数据,例 :

  1. connection.setRequestMethod("POST");
  2. DataOUtputStream out = new DataOutputStream(connection.getOutputStream());
  3. out.writeBytes("username=admin&password=123456");

OkHttp

OkHttp是一个比较好的开源库
在dependencies闭包下添加 :

  1. implementation 'com.squareup.okhttp3:okhttp:3.9.1'
  1. 创建一个OkHttpClient实例
  2. 创建一个request对象
  3. 调用OkHttpClient的newCall()方法创建一个Call对象,并调用它的execute()方法发送请求并获取服务器返回的数据。

发送POST,需要先构建RequestBody对象来存放待提交的参数

  1. RequestBody requestBody = new FormBody.Bulider()
  2. .add("username", "admin")
  3. .add("passward", "123456")
  4. .build();

在request中调用

  1. Request request = new Request.Builder()
  2. .url("https://www.baidu.com")
  3. .post(requestBody)
  4. .build();
  5. @Override
  6. public void onClick(View v) {
  7. switch (v.getId()) {
  8. case R.id.send_request:
  9. // sendRequestWithHttpURLConnection(); // Use HttpURLConnection
  10. sendRequestWithOKHttp(); // Use OKHttp
  11. break;
  12. default:
  13. break;
  14. }
  15. }
  16. private void sendRequestWithOKHttp() {
  17. new Thread(new Runnable() {
  18. @Override
  19. public void run() {
  20. try{
  21. OkHttpClient client = new OkHttpClient();
  22. Request request = new Request.Builder()
  23. .url("https://baidu.com") // 获取百度的html信息
  24. .build();
  25. Response response = client.newCall(request).execute();
  26. String responseData = response.body().string();
  27. showResponse(responseData); // 在textview中展示
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. }
  32. }).start();
  33. }

解析数据

从Apache的官网上下载一个Tomcat,在tomcat目录下的tomcat-8.5.23\webapps\ROOT文件夹中新建一个
get_data.xml文件 :

  1. <apps>
  2. <app>
  3. <id>1</id>
  4. <name>Google Map</name>
  5. <version>1.0</version>
  6. </app>
  7. <app>
  8. <id>2</id>
  9. <name>Chrome</name>
  10. <version>2.1</version>
  11. </app>
  12. <app>
  13. <id>3</id>
  14. <name>Google Play</name>
  15. <version>2.3</version>
  16. </app>
  17. </apps>

电脑本机访的网址是http://127.0.0.1:8080/get_data.xml

Exler

XML

xml文件写入到手机
  1. FileOutputStream fos = null;
  2. try {
  3. XmlSerializer serializer = Xml.newSerializer(); // 获取XmlSerializer的实例
  4. File file = new File(Environment.getExternalStorageDirectory().getPath(), "serializer.xml");
  5. fos = new FileOutputStream(file);
  6. serializer.setOutput(fos, "utf-8"); // 设置Xml序列化器的参数
  7. serializer.startDocument("utf-8", true); // 开始写xml文档开头 xml文件的编码 是否是一独立的xml文件
  8. serializer.startTag(null, "apps"); // 写xml的根节点,namespace 命名空间
  9. for (App app : AppList) { // 循环写app节点
  10. serializer.startTag(null, "app");
  11. serializer.startTag(null, "id");
  12. serializer.text(app.getId());
  13. serializer.endTag(null, "id");
  14. serializer.startTag(null, "name");
  15. serializer.text(app.getName());
  16. serializer.endTag(null, "name");
  17. serializer.startTag(null, "version");
  18. serializer.text(app.getVersion());
  19. serializer.endTag(null, "version");
  20. serializer.endTag(null, "app");
  21. }
  22. serializer.endTag(null, "apps");
  23. serializer.endDocument(); // 写文档结尾
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. } finally {
  27. try {
  28. if (fos != null) {
  29. fos.close();
  30. }
  31. } catch (Exception e) {
  32. e.printStackTrace();
  33. }
  34. }

对于XML解析有两个种方式Pull SAX

Pull

一行一行解析
基于事件解析

  1. private void sendRequestWithOKHttp() {
  2. new Thread(new Runnable() {
  3. @Override
  4. public void run() {
  5. try{
  6. OkHttpClient client = new OkHttpClient();
  7. Request request = new Request.Builder()
  8. .url("http://10.0.2.2:8080/get_data.xml") // 访问PC本地tomcat上的get_data.xml
  9. .build();
  10. Response response = client.newCall(request).execute();
  11. String responseData = response.body().string();
  12. parseXMLWithPull(responseData); // 利用Pull方式解析url中的xml
  13. parseJSONWithGson(responseData);
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }).start();
  19. }
  20. private void parseXMLWithPull(String xmlData) {
  21. try {
  22. XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); // 获取实例
  23. XmlPullParser xmlPullParser = factory.newPullParser(); // Xml.newPullParser();
  24. xmlPullParser.setInput(new StringReader(xmlData)); // 将服务器返回的XML数据设置进去 或(in, "utf-8")服务器返回的数据流
  25. int eventType = xmlPullParser.getEventType(); // 得到当前的解析事件
  26. String id = "";
  27. String name = "";
  28. String version = "";
  29. while (eventType != XmlPullParser.END_DOCUMENT) { // 循环解析
  30. String nodeName = xmlPullParser.getName(); // 得到当前节点的名字
  31. switch (eventType) { // 开始解析某个节点
  32. case XmlPullParser.START_TAG: {
  33. if ("id".equals(nodeName))
  34. id = xmlPullParser.nextText(); // 获取节点内的具体内容 开始标签中有id属性xmlPullParser.getAttributeValue(0)
  35. else if ("name".equals(nodeName))
  36. name = xmlPullParser.nextText();
  37. else if ("version".equals(nodeName))
  38. version = xmlPullParser.nextText();
  39. break;
  40. }
  41. case XmlPullParser.END_TAG: {
  42. if ("app".equals(nodeName)) {
  43. Log.d("admin", "id is " + id);
  44. Log.d("admin", "name is " + name);
  45. Log.d("admin", "version is " + version);
  46. }
  47. break;
  48. }
  49. default:
  50. break;
  51. }
  52. eventType = xmlPullParser.next();
  53. }
  54. } catch (Exception e) {
  55. e.printStackTrace();
  56. }
  57. }

Exler

SAX

新建ContentHandler类继承自DefaultHandler :

  1. public class ContentHandler extends DefaultHandler {
  2. private String nodeName;
  3. private StringBuilder id;
  4. private StringBuilder name;
  5. private StringBuilder version;
  6. @Override
  7. public void startDocument() throws SAXException { // 开始解析时调用
  8. id = new StringBuilder();
  9. name = new StringBuilder();
  10. version = new StringBuilder();
  11. }
  12. @Override
  13. public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // 开始解析某个节点的时候调用
  14. nodeName = localName; // 记录当前节点名
  15. }
  16. @Override
  17. public void characters(char[] ch, int start, int length) throws SAXException { // 解析出的数据会以参数的形式传入 此方法可能会被调用多次 一些换行符也被当作内容解析 需要控制
  18. if ("id".equals(nodeName)) { // 根据当前节点名 判断将内容添加到哪个StringBuilder对象中
  19. id.append(ch, start, length);
  20. } else if ("name".equals(nodeName)) {
  21. name.append(ch, start, length);
  22. } else if ("version".equals(nodeName)) {
  23. version.append(ch, start, length);
  24. }
  25. }
  26. @Override
  27. public void endElement(String uri, String localName, String qName) throws SAXException { // 完成解析某个节点的时候调用
  28. if ("app".equals(localName)) {
  29. Log.d("ContentHandler", "id is "+ id.toString().trim());
  30. Log.d("ContentHandler", "name is " + name.toString().trim());
  31. Log.d("ContentHandler", "version is " + version.toString().trim());
  32. id.setLength(0); // 将StringBuilder清空
  33. name.setLength(0);
  34. version.setLength(0);
  35. }
  36. }
  37. @Override
  38. public void endDocument() throws SAXException { // 完成整个文档的时候调用
  39. super.endDocument();
  40. }
  41. }

调用 :

  1. private void parseXMLWithSAX(String xmlData) {
  2. try {
  3. SAXParserFactory factory = SAXParserFactory.newInstance();
  4. XMLReader reader = factory.newSAXParser().getXMLReader();
  5. ContentHandler handler = new ContentHandler();
  6. reader.setContentHandler(handler); // 将ContentHandler实例设置到XMLReader中
  7. reader.parse(new InputSource(new StringReader(xmlData))); // 开始执行解析
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. }

JSON

在tomcat中新建
get_data.json :

  1. [{"id":"5","version":"5.5","name":"Clash of Clans"},
  2. {"id":"6","version":"7.0","name":"Boom Beach"},
  3. {"id":"7","version":"3.5","name":"Clash Royale"}]

使用JSONObject

  1. private void parseJSONWithJSONObject(String jsonData) {
  2. try {
  3. JSONArray jsonArray = new JSONArray(jsonData); // 数据传入
  4. for (int i = 0; i < jsonArray.length(); i++) {
  5. JSONObject jsonObject = jsonArray.getJSONObject(i); // 每一个元素
  6. String id = jsonObject.getString("id");
  7. String name = jsonObject.getString("name");
  8. String version = jsonObject.getString("version");
  9. Log.d("admin", "id is " + id);
  10. Log.d("admin", "name is " + name);
  11. Log.d("admin", "version is " + version);
  12. }
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. }

使用GSON

  1. compile 'com.google.code.gson:gson:2.7'

根据要从服务器上接收的数据定义一个类
app :

  1. public class App {
  2. private String id;
  3. private String name;
  4. private String version;
  5. public String getId() {
  6. return id;
  7. }
  8. public void setId(String id) {
  9. this.id = id;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public String getVersion() {
  18. return version;
  19. }
  20. public void setVersion(String version) {
  21. this.version = version;
  22. }
  23. }

正好对应三个字段。

调用 :

  1. private void parseJSONWithGson(String jsonData) {
  2. Gson gson = new Gson();
  3. List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>(){}.getType()); // 借助TypeToken将期望解析成的数据类型传入到fromJson()方法中
  4. for (App app : appList) {
  5. Log.d("admin", "id is " + app.getId());
  6. Log.d("admin", "name is " + app.getName());
  7. Log.d("admin", "version is " + app.getVersion());
  8. }
  9. }

此博文为个人学习笔记,仅供个人学习使用,希望对大家有帮助。

发表评论

表情:
评论列表 (有 0 条评论,306人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android学习随笔(1)

    学习流程来自《第一行代码》(第二版) 最近开始了Android的学习,看到很多人都推荐这一本书,就决定按照这一本书的讲解流程熟悉一下Android。 环境配置 ![