CH9-网络编程

目标

  • 了解HTTP协议通信简介,能够说出什么是HTTP协议
  • 掌握HttpURLConnection的使用方法,能够使用HttpURLConnection访问网络
  • 掌握WebView控件的使用方式,能够使用WebView控件加载不同的网页
  • 掌握JSON数据的解析,能够通过不同的方式解析JSON数据
  • 熟悉Handler消息机制的概述,能够归纳Handler消息机制的原理

​ 在移动互联网时代,手机联网实现信息互通是最基本的功能体验。例如,在上下班的途中或旅行时,只要有时间人们就会拿出手机上网,通过手机接收新资讯、搜索网络资源。Android作为智能手机市场中主流的操作系统,它的强大离不开其对网络功能的支持。Android系统提供了多种实现网络通信的方式。接下来,我们从最基础的HTTP协议开始,到Android中原生的HttpURLConnection、WebView控件的使用以及网络数据的解析进行详细讲解。

一、通过HTTP访问网络

  • 了解HTTP协议通信简介,能够说出什么是HTTP协议
  • 掌握HttpURLConnection的使用方法,能够使用HttpURLConnection访问网络

1.1 HTTP协议通信简介

HTTP(Hyper Text Transfer Protocol)即超文本传输协议,它规定了浏览器服务器之间相互通信的规则。

HTTP协议是一种请求/响应式的协议

  • 当客户端与服务器端建立连接后,向服务器端发送的请求,称作HTTP请求

  • 服务器端接收到请求后会做出响应,称为HTTP响应

​ 使用手机客户端访问百度时,会发送一个HTTP请求,当服务器端接收到请求后,会做出响应并将百度页面(数据)返回给客户端浏览器,这个请求响应的过程就是HTTP通信的过程。

image-20220306143836237

1.2 使用HttpURLConnection访问网络

GET与POST请求

(1)GET方式

GET方式是以实体的方式得到由请求URL所指向的资源信息,它向服务器提交的参数跟在请求URL后面。使用GET方式访问网络URL的长度一般要小于1KB

(2)POST方式

POST方式向服务器发出请求时需要在请求后附加实体。它向服务器提交的参数在请求后的实体中,POST方式对URL的长度是没有限制的。

​ 采用POST方式提交数据时,用户在浏览器中看不到向服务器提交的请求参数,因此POST方式要比GET方式相对安全。

GET方式提交数据

//将用户名和密码拼在指定资源路径后面,并对用户名和密码进行编码
String path = "http://192.168.1.100:8080/web/LoginServlet?username="
                  + URLEncoder.encode("zhangsan")
                  +"&password="+ URLEncoder.encode("123");  
URL url =  new  URL(path);                      
HttpURLConnection  conn  =  (HttpURLConnection)url.openConnection();  
conn.setRequestMethod("GET");                  
conn.setConnectTimeout(5000);                  
int responseCode = conn.getResponseCode();  //获取到状态码
if(responseCode == 200){          //状态码为200,表示访问成功获取返回内容的输入流         
        InputStream is = conn.getInputStream(); 
}

POST方式提交数据

URL url = new URL("http://192.168.1.100:8080/web/LoginServlet");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);                
conn.setRequestMethod("POST");               
//准备数据并给参数进行编码
String data = "username=" + URLEncoder.encode("zhangsan")
                  + "&password=" + URLEncoder.encode("123");
//设置请求头数据提交方式以及提交数据的长度,这里是以form表单的方式提交
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); 
conn.setRequestProperty("Content-Length", data.length() + ""); 
//以流的形式将数据写到服务器上
conn.setDoOutput(true); 
OutputStream os = conn.getOutputStream(); 
os.write(data.getBytes()); 
int code = conn.getResponseCode(); 
if (code == 200) {
    InputStream is = conn.getInputStream(); 
}

注意

​ 在实际开发中,手机端与服务器端进行交互的过程中避免不了要提交中文到服务器,这时就会出现中文乱码的情况。无论是GET方式还是POST方式提交参数时都要给参数进行编码,编码方式必须与服务器解码方式一致。同样在获取服务器返回的中文字符时,也需要用指定格式进行解码。

二、使用WebView进行网络开发

目标

  • 掌握WebView的使用方式,能够使用WebView浏览不同网页、执行HTML代码和支持JavaScript

2.1 使用WebView浏览网页

​ 在Android程序中,WebView控件可以在XML布局文件中使用标签来添加,也可以在Java文件中通过new关键字来创建。

​ 通常会采用在XML布局文件中添加标签的形式,具体代码如下

<!--WebView控件的id-->
<WebView
    android:id="@+id/webView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

​ 控件的常用方法如下表所示。

属性名称功能描述
loadUrl(String url)用于加载指定URL对应的网页
loadData(String data, String mimeType, String encoding)用于将指定的字符串数据加载到浏览器中
loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,String historyUrl)基于URL加载指定的数据
capturePicture()用于创建当前屏幕的快照
goBack()用于执行后退操作,相当于浏览器上后退按钮的功能
goForward()用于执行前进操作,相当于浏览器上前进按钮的功能
stopLoading()用于停止加载当前页面
reload()用于刷新当前页面
emulator.exe -list-avds
emulator.exe -avd Nexus_4_API_28 -dns-server 192.168.1.1
setprop net.dns1 192.168.1.1
setprop net.eth0.dns1 192.168.1.1
setprop net.eth0.gw 192.168.1.1

接下来通过一个案例来演示如何使用WebView控件加载网页,本案例的界面效果如下图所示。

image-20220306145625620

放置界面控件 res\layout\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

编写界面交互代码 webview\MainActivity.java

package cn.itcast.webview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
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.webView);
        webview.loadUrl("http://m.itheima.com/"); // 指定要加载的网页
    }
}

​ 在MainActivity中实现WebView控件浏览网页的功能,通过WebView控件的loadUrl()方法来加载指定的网页,主要代码如下。

WebView webview=(WebView)findViewById(R.id.webView); 
webview.loadUrl("http://www.itheima.com/"); 	//通过加载网页地址加载网页
	 需要在清单文件(AndroidManifest.xml)的< manifest>标签中<font color='cornflowerblue'>添加允许访问网络资源的权限</font>。

注 意

​ 如果想让上述WebView控件具备放大和缩小网页的功能,则需要对该控件进行如下设置:

//设置WebView控件支持使用屏幕控件或手势进行缩放
webview.getSettings().setSupportZoom(true);
//设置WebView控件使用其内置的变焦机制,该机制集合屏幕缩放控件使用
webview.getSettings().setBuiltInZoomControls(true);

2.2 使用WebView执行HTML代码

​ WebView类提供了loadData()和 loadDataWithBaseURL()方法加载HTML代码。当使用loadData()方法来加载带中文的HTML内容时会产生乱码,但是使用loadDataWithBaseURL()方法就不会出现这种情况。loadDataWithBaseURL()方法的定义方式如下:

image-20220306150031612

​ 接下来通过一个案例来演示如何使用WebView控件加载HTML代码,本案例的界面效果如下图所示。

image-20220306150639340

放置界面控件 res\layout\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

实现加载HTML的功能 webviewhtml\MainActivity.java

package cn.itcast.webviewhtml;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
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.webView);
        // 创建一个字符串构建器,将要显示的HTML内容放置在该构建器中
        StringBuilder sb = new StringBuilder();
        sb.append("<div>请选择您要学习的课程:</div>");
        sb.append("<ul>");
        sb.append("<li>新媒体课程</li>");
        sb.append("<li>大数据课程</li>");
        sb.append("<li>人工智能课程</li>");
        sb.append("</ul>");
        // 加载数据
//        null      表示默认的空白页面
//        sb.toString()     sb中的数据
//        "text/html"       指定要显示内容的MIME类型
        webview.loadDataWithBaseURL(null, sb.toString(), "text/html", "utf-8",
                null);
    }
}

2.3 设置WebView支持JavaScript

​ 为了解决WebView控件在默认情况下不支持JavaScript代码的问题,我们需要通过setJavaScriptEnabled()方法来设置WebView控件,使其可以支持JavaScript代码

WebSettings settings= webview.getSettings(); // 获取WebSettings对象
settings.setJavaScriptEnabled(true); 		//设置JavaScript可用
//使WebView控件显示带有JavaScript代码的提示框
webview.setWebChromeClient(new WebChromeClient());	

​ 接下来通过一个案例来演示如何使用WebView控件支持一个带有JavaScript代码的网页,本案例的界面效果如下图所示。

image-20220306151029436

image-20220306152215525

导入JS文件 src\main\assets\alert.html和alert.js

<!DOCTYPE html>
<html>
<head>
<title>alert.html</title>
<meta charset="UTF-8">
<meta name="content-type" content="text/html; charset=UTF-8">
</head>
<body>
	This is my HTML page.
	<br>
	<script type="text/javascript" src="alert.js"></script>
	<script type="text/javascript">
alert("我是一个消息提示框");
</script>
</body>
</html>
window.alert = function(msg, callback) {
	var div = document.createElement("div");
	div.innerHTML = "<style type=\"text/css\">"
			+ ".nbaMask { position: fixed; z-index: 1000; top: 0; right: 0; left: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); }                                                                                                                                                                       "
			+ ".nbaMaskTransparent { position: fixed; z-index: 1000; top: 0; right: 0; left: 0; bottom: 0; }                                                                                                                                                                                            "
			+ ".nbaDialog { position: fixed; z-index: 5000; width: 80%; max-width: 300px; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); background-color: #fff; text-align: center; border-radius: 8px; overflow: hidden; opacity: 1; color: white; }"
			+ ".nbaDialog .nbaDialogHd { padding: .2rem .27rem .08rem .27rem; }                                                                                                                                                                                                                         "
			+ ".nbaDialog .nbaDialogHd .nbaDialogTitle { font-size: 17px; font-weight: 400; }                                                                                                                                                                                                           "
			+ ".nbaDialog .nbaDialogBd { padding: 0 .27rem; font-size: 15px; line-height: 1.3; word-wrap: break-word; word-break: break-all; color: #000000; }                                                                                                                                          "
			+ ".nbaDialog .nbaDialogFt { position: relative; line-height: 48px; font-size: 17px; display: -webkit-box; display: -webkit-flex; display: flex; }                                                                                                                                          "
			+ ".nbaDialog .nbaDialogFt:after { content: \" \"; position: absolute; left: 0; top: 0; right: 0; height: 1px; border-top: 1px solid #e6e6e6; color: #e6e6e6; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scaleY(0.5); transform: scaleY(0.5); }               "
			+ ".nbaDialog .nbaDialogBtn { display: block; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; color: #09BB07; text-decoration: none; -webkit-tap-highlight-color: transparent; position: relative; margin-bottom: 0; }                                                                       "
			+ ".nbaDialog .nbaDialogBtn:after { content: \" \"; position: absolute; left: 0; top: 0; width: 1px; bottom: 0; border-left: 1px solid #e6e6e6; color: #e6e6e6; -webkit-transform-origin: 0 0; transform-origin: 0 0; -webkit-transform: scaleX(0.5); transform: scaleX(0.5); }             "
			+ ".nbaDialog a { text-decoration: none; -webkit-tap-highlight-color: transparent; }"
			+ "</style>"
			+ "<div id=\"dialogs2\" style=\"display: none\">"
			+ "<div class=\"nbaMask\"></div>"
			+ "<div class=\"nbaDialog\">"
			+ "	<div class=\"nbaDialogHd\">"
			+ "		<strong class=\"nbaDialogTitle\"></strong>"
			+ "	</div>"
			+ "	<div class=\"nbaDialogBd\" id=\"dialog_msg2\">弹窗内容,告知当前状态、信息和解决方法,描述文字尽量控制在三行内</div>"
			+ "	<div class=\"nbaDialogHd\">"
			+ "		<strong class=\"nbaDialogTitle\"></strong>"
			+ "	</div>"
			+ "	<div class=\"nbaDialogFt\">"
			+ "		<a href=\"javascript:;\" class=\"nbaDialogBtn nbaDialogBtnPrimary\" id=\"dialog_ok2\">确定</a>"
			+ "	</div></div></div>";
	document.body.appendChild(div);
 
	var dialogs2 = document.getElementById("dialogs2");
	dialogs2.style.display = 'block';
 
	var dialog_msg2 = document.getElementById("dialog_msg2");
	dialog_msg2.innerHTML = msg;
 
	// var dialog_cancel = document.getElementById("dialog_cancel");
	// dialog_cancel.onclick = function() {
	// dialogs2.style.display = 'none';
	// };
	var dialog_ok2 = document.getElementById("dialog_ok2");
	dialog_ok2.onclick = function() {
		dialogs2.style.display = 'none';
		callback();
	};
};

放置界面控件 res\layout\activity_main.xml

<?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">
    <Button
        android:id="@+id/btn_dialog"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="执行JAVASCRIPT代码并弹出提示框"
        android:layout_margin="8dp"
        android:textColor="@android:color/white"
        android:background="@drawable/btn_dialog_selector"/>
    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

创建背景选择器 res\drawable\btn_dialog_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/btn_dialog_selected"
        android:state_pressed="true" />
    <item android:drawable="@drawable/btn_dialog_normal" />
</selector>

实现加载JS代码功能 webviewjs\MainActivity.java

package cn.itcast.webviewjs;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final WebView webview = findViewById(R.id.webView);
        Button btn = findViewById(R.id.btn_dialog);
        webview.loadUrl("file:///android_asset/alert.html"); //指定要加载的网页
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 设置webview控件支持JavaScript代码
                webview.getSettings().setJavaScriptEnabled(true);
                // 显示网页中通过JavaScript代码弹出的提示框
                webview.setWebChromeClient(new WebChromeClient());
                webview.loadUrl("file:///android_asset/alert.html");
            };
        });
    }
}

三、JSON数据解析

目标

  • 掌握JSON数据的解析,能够通过不同的方式解析JSON数据

3.1 JSON数据

JSON数据的特点

(1)JSON即JavaScript Object Notation(对象表示法),是一种轻量级的数据交换格式

(2)JSON是基于纯文本的数据格式,它可以传输String、Number、Boolean类型的数据,也可以传输数组或者Object对象。

(3)JSON文件的扩展名为.json

(4)JSON分为JSON对象和JSON数组两种数据结构。

对象结构的JSON数据

​ 以“{”开始,以“}”结束。中间部分由0个或多个以“,”分隔的key:value对构成,注意关键字和值之间以“:”分隔。

​ 关键字key必须为String类型,值value可以是String、Number、Object、Array等数据类型。

image-20220306152538881

image-20220306152618944

数组结构的JSON数据

​ 以“[”开始,以“]”结束。中间部分由0个或多个以“,”分隔的值的列表组成。

值value可以是String、Number、Boolean、null等数据类型。

image-20220306152723534

image-20220306152727806

注意:

​ 使用JSON存储单个数据(如“abc”),一定使用数组结构,因为对象结构必须是由“key:value”的形式构成。

image-20220306152919626

3.2 JSON解析

两种解析方式

  • org.json
    • Android SDK中为开发者提供的,通过使用JSONObject和JSONArray两个类完成对JSON数据的解析。
  • Gson
    • 由Google公司提供的,在使用Gson库之前,首先需要将gson.jar添加到项目中,然后才能调用其提供的方法。

解析JSON对象

例如,要解析的JSON数据如下:

{ "name": "zhangsan", "age": 27, "married":true }         //json1 一个json对象
[{"name": "lisi","age": 25},{"name": "Jason","age": 20}]  //json2 一个json数组

使用JSONObject解析JSON对象

optXXX()方法在解析数据时比getXXX()方法更安全,如果对应字段不存在,optXXX()方法会返回空值或者0,而getXXX()方法会抛出异常。

JSONObject   jsonObj  =  new JSONObject(json1); 
String name = jsonObj.optString("name"); 
int age = jsonObj.optInt("age"); 
boolean married = jsonObj.optBoolean("married");

解析JSON数组

​ 使用JSONArray解析JSON数组:

数组的解析方法和对象类似,只是将key值替换为数组中的下标。

JSONArray jsonArray = new JSONArray(json2); 
for(int i = 0; i < jsonArray.length(); i++) {
     JSONObject jsonObj = jsonArray.getJSONObject(i);
     String name = jsonObj.optString("name"); 
     int age = jsonObj.optInt("age");
}

Gson库解析JSON数据

​ 例如,要解析的JSON数据如下(与org.json解析数据相同):

使用Gson库前,首先需要将gson.jar添加到项目中,并且创建JSON数据对应的实体类Person1与Person2,需要注意的是,实体类中的成员名称要与JSON数据中的key值一致

{ "name": "zhangsan", "age": 27, "married":true }         //json1 一个json对象
[{"name": "lisi","age": 25},{"name": "Jason","age": 20}]  //json2 一个json数组

使用Gson解析JSON对象

 Gson gson = new Gson(); 
 Person person1 = gson.fromJson(json1, Person1.class);	//将JSON数据转换成对象

使用Gson解析JSON数组

 Gson gson = new Gson(); 
//TypeToken是Google提供的一个解析JSON数据的类
 Type listType = new TypeToken<List<Person2>>(){}.getType();
 List<Person2> person2 = gson.fromJson(json2, listType);

Android Studio添加库文件

​ 在Android程序中添加库文件进行讲解,具体操作步骤如下:

  1. 在Android Studio中,选择【File】à【Project Structure…】选项,此时会弹出一个Project Structure窗口,如下图所示。

image-20220306160153179

  1. 选中Project Structure窗口中的【Dependencies】选项卡,接着单击该窗口右上角的“ ”,选择Library dependency选项,此时会弹出一个Choose Library Dependency窗口,在该窗口中找到Gson库com.google.code.gson:gson:2.8.5并选中,如下图所示。

image-20220306160301489

3.3 实战演练—仿拼多多砍价界面

​ 本节我们将通过仿拼多多砍价界面的案例来演示如何解析JSON数据并将数据显示到界面上。本案例的界面效果如下图所示。

image-20220306160346630

放置界面控件 res\layout\activity_main.xml

<?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:background="@android:color/black"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginRight="20dp"
        android:background="@drawable/title_bg"
        android:gravity="center_vertical"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="25dp"
            android:text="一刀砍成卡"
            android:textColor="#ce4032"
            android:textSize="24sp"
            android:textStyle="bold" />
        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:background="#af560e" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="商品直接带回家"
            android:textColor="#875a1e"
            android:textSize="18sp"
            android:textStyle="bold" />
    </LinearLayout>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

搭建商品的条目布局 res\layout\goods_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="230dp"
    android:layout_marginLeft="30dp"
    android:layout_marginTop="20dp"
    android:background="@android:color/black"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/goods_bg"
        android:gravity="center_horizontal"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_count"
            android:layout_width="wrap_content"
            android:layout_height="27dp"
            android:layout_gravity="center_horizontal"
            android:gravity="center"
            android:padding="5dp"
            android:textColor="#573516"
            android:textSize="12sp" />
        <TextView
            android:id="@+id/tv_goods_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:textColor="#573516"
            android:textSize="16sp"
            android:textStyle="bold" />
        <ImageView
            android:id="@+id/iv_img"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_marginTop="10dp" />
        <Button
            android:id="@+id/btn_free"
            android:layout_width="110dp"
            android:layout_height="35dp"
            android:layout_margin="10dp"
            android:background="@drawable/btn_free_bg"
            android:text="点击免费拿"
            android:textColor="@android:color/white"
            android:textSize="14sp"
            android:textStyle="bold" />
    </LinearLayout>
</LinearLayout>

封装商品信息的实体类 pinduoduo\GoodsInfo.java

package cn.itcast.pinduoduo;
public class GoodsInfo {
    private int id;             // 商品id
    private String count;      // 已砍商品的数量
    private String goodsName; // 商品名称
    private String goodsPic;  // 商品图片
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getCount() {
        return count;
    }
    public void setCount(String count) {
        this.count = count;
    }
    public String getGoodsName() {
        return goodsName;
    }
    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }
    public String getGoodsPic() {
        return goodsPic;
    }
    public void setGoodsPic(String goodsPic) {
        this.goodsPic = goodsPic;
    }
}

编写商品列表的适配器 pinduoduo\GoodsAdapter.java

package cn.itcast.pinduoduo;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.ArrayList;
import java.util.List;

public class GoodsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private Context mContext;
    private List<GoodsInfo> GoodsList = new ArrayList<>();

    public GoodsAdapter(Context context) {
        this.mContext = context;
    }

    /**
     * 获取数据更新界面
     */
    public void setData(List<GoodsInfo> GoodsList) {
        this.GoodsList = GoodsList;     //获取从Activity界面传递过来的数据GoodsList
        notifyDataSetChanged();
    }

//    inflate()方法加载布局文件
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = null;
        RecyclerView.ViewHolder holder = null;
        itemView = LayoutInflater.from(mContext).inflate(R.layout.goods_item, parent, false);
        holder = new MyViewHolder(itemView);
        return holder;
    }

//    数据绑定
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        GoodsInfo bean = GoodsList.get(position);
//        将已砍的商品数量和商品名称设置到界面控件上
        ((MyViewHolder) holder).tv_count.setText("已砍" + bean.getCount() + "件");
        ((MyViewHolder) holder).tv_goods_name.setText(bean.getGoodsName());
//        将商品图片数据设置到图片控件iv_img上
        Glide.with(mContext)
                .load(bean.getGoodsPic())
                .error(R.mipmap.ic_launcher)
                .into(((MyViewHolder) holder).iv_img);
    }

//    获取条目总数
    @Override
    public int getItemCount() {
        return GoodsList.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tv_count, tv_goods_name;
        ImageView iv_img;
        Button btn_free;

        public MyViewHolder(View view) {
            super(view);
            tv_count = view.findViewById(R.id.tv_count);
            tv_goods_name = view.findViewById(R.id.tv_goods_name);
            iv_img = view.findViewById(R.id.iv_img);
            btn_free = view.findViewById(R.id.btn_free);
        }
    }
}

实现商品显示功能 pinduoduo\MainActivity.java

package cn.itcast.pinduoduo;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {
    private GoodsAdapter adapter;             // 列表的适配器
    public static final int MSG_GOODS_OK = 1; // 获取数据
    private MHandler mHandler;
    // 内网接口
    public static final String WEB_SITE = "http://172.16.43.20:8080/goods";
    // 商品列表接口
    public static final String REQUEST_GOODS_URL = "/goods_list_data.json";
    private RecyclerView rv_list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MHandler();
        init();
        initData();
    }

    private void init() {
        rv_list = findViewById(R.id.rv_list);
//        this 表示上下文
//        2     表示商品列表的每个条目中显示两条商品信息
        GridLayoutManager manager = new GridLayoutManager(this, 2);
//        将manager对象设置到控件rv_list上
        rv_list.setLayoutManager(manager);
        adapter = new GoodsAdapter(MainActivity.this);
//        将数据适配器的对象adapter设置到控件rv_list上
        rv_list.setAdapter(adapter);
    }

    private void initData() {
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder().url(WEB_SITE +
                REQUEST_GOODS_URL).build();
        Call call = okHttpClient.newCall(request);
        // 开启异步线程访问网络,从服务器上获取商品列表的数据
        call.enqueue(new Callback() {
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String res = response.body().string(); // 获取商品数据
                Message msg = new Message();
                msg.what = MSG_GOODS_OK;
                msg.obj = res;
                mHandler.sendMessage(msg);
            }

            @Override
            public void onFailure(Call call, IOException e) {
            }
        });
    }

    /**
     * 事件捕获
     */
    class MHandler extends Handler {
        @Override
        public void dispatchMessage(Message msg) {
            super.dispatchMessage(msg);
            switch (msg.what) {
                case MSG_GOODS_OK:
                    if (msg.obj != null) {
//                        获取传递过来的JSON数据vlResult
                        String vlResult = (String) msg.obj;
                        // 解析获取的JSON数据vlResult,并将解析后的数据存放在集合goodsInfos中
                        List<GoodsInfo> goodsInfos = getGoodsList(vlResult);
//                        将集合goodsInfos设置到数据适配器的对象adapter中
                        adapter.setData(goodsInfos);
                    }
                    break;
            }
        }
    }

    public List<GoodsInfo> getGoodsList(String json) {
        Gson gson = new Gson(); // 使用gson库解析JSON数据
        // 创建一个TypeToken的匿名子类对象,并调用对象的getType()方法
        Type listType = new TypeToken<List<GoodsInfo>>() {
        }.getType();
        // 把获取到的集合数据存放到goodsInfos中
        List<GoodsInfo> goodsInfos = gson.fromJson(json, listType);
        return goodsInfos;
    }
}

3.4 安装配置Tomcat服务器

​ Tomcat运行稳定、可靠、效率高,不仅可以和目前大部分主流的Web服务器(如Apache、IIS服务器)一起工作,还可以作为独立的Web服务器软件。

1.下载Tomcat

​ 在Tomcat官网上下载apache-tomcat-8.5.59-windows-x64.zip文件,解压该文件可以看到Tomcat的目录结构,如下图所示。

image-20220306160429366

2. 启动Tomcat

在Tomcat安装目录的bin目录下,存放了许多脚本文件,其中startup.bat就是启动Tomcat的脚本文件,如下图所示。

image-20220306160508044

双击startup.bat文件,便会启动Tomcat服务器,Tomcat启动信息窗口如下图所示。

image-20220306160518981

Tomcat服务器启动后,在浏览器的地址栏中输入http://localhost:8080访问Tomcat服务器,如果浏览器中的显示Tomcat页面如下图所示,则说明Tomcat 服务器安装部署成功了。

image-20220306160544300

3. 关闭Tomcat

在Tomcat根目录下的bin文件夹中,运行shutdown.bat脚本文件即可关闭Tomcat或者直接关闭Tomcat启动信息窗口。

四、Handler消息机制

目标

  • 熟悉Handler消息机制的概述,能够归纳Handler消息机制的原理

Handler是一种异步回调机制,主要负责与子线程进行通信

Handler机制主要包括四个关键对象:

  • Message:是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。

  • Handler:是处理者的意思,它主要用于发送消息和处理消息。

  • MessageQueue:是消息队列的意思,它主要用来存放通过Handler发送的消息。通过Handler发送的消息会存在MessageQueue中等待处理,每个线程中只会有一个MessageQueue对象。

  • Looper:是每个线程中的MessageQueue的管家。调用Looper的loop()方法后,就会进入到一个无限循环中。每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。

image-20220306160757333

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一条大蟒蛇6666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值