一、后台与APP 交互过程分析
1 、后台与APP 交互过程分析
基于http/https协议的app前后台交互包含以下几个步骤:
1.建立连接:前台后台建立连接。
2.发送请求:建立连接后,app向后台发送请求。
3.发送响应:后台处理完请求后,要向app发送响应消息。
比如:android代表客户端开发,php代表服务端开发。json在其中是一种通用语言的角色。中国人(客户端)和日 本人(服务器)就用英语(json)交流。
4.断开连接:以上交互完成后可以断开连接了。
img02.jpg
二、Tomcat基本使用
1、 Tomcat简介★★★★
Tomcat服务器由Apache提供,开源免费。由于Sun和其他公司参与到了Tomcat的开发中,所以最新JSP/Servlet规范总是能在Tomcat中体现出来。当前最新版本是Tomcat10,我们课程中使用Tomcat7。Tomcat7支持Servlet3.0,而Tomcat6只支持Servlet2.5!
访问:http://localhost:8080
localhost是指访问自己电脑上的tomcat服务器
格式:http://ip地址:8080,Tomcat默认的端口号是8080,可以修改,打开Tomcat安装路径,%CATALANA_HOME%\conf\server.xml文件:
2、 Tomcat部署json★★★★
Tomacat下载安装:
下载Tomcat可以到http://tomcat.apache.org下载。
Tomcat分为安装版和解压版:
1.安装版:一台电脑上只能安装一个Tomcat;
2.解压版:无需安装,解压即可用,解压多少份都可以,所以我们选择解压版。
在启动Tomcat之前,我们必须要配置环境变量:
1.JAVA_HOME:必须先配置JAVA_HOME,因为Tomcat启动需要使用JDK;
2.CATALANA_HOME:如果是安装版,那么还需要配置这个变量,这个变量用来指定Tomcat的安装路径,例如:F:\apache-tomcat-7.0.42。
3.启动:进入%CATALANA_HOME%\bin目录,找到startup.bat,双击即可;(linux系统,双击startup.sh)
4.关闭:进入%CATALANA_HOME%\bin目录,找到shutdown.bat,双击即可;注意:我们必须在启动Tomcat之前把JAVA_HOME配置正确。
启动问题:点击startup.bat后窗口一闪即消失:检查JAVA_HOME环境变量配置是否正确;
启动成功后再浏览器中访问http://localhost:8080/,如果能看到如下图所示界面,说明Tomcat启动成功。
Tomcat部署json:
可以在Tomcat安装目录下找到webapps目录,新建一个文件夹,将资源放入文件夹,例如新建res目录,放入class.json资源,访问路径:http://192.168.1.103:8080/res/class.json(192.168.1.103为ip,res为资源目录路径),或者:http://localhost:8080/res/class.json。
三、HttpURLConnection
1、 HttpURLConnection 简介
-
客户端向网络请求数据,需要借助URLConnection。任何网络连接都需要经过socket才能连接,URLConnection 不需要设置socket,所以URLConnection并不是底层的连接,而是在底层连接上的一个请求。这就是为什么URLConneciton只是一个抽象类,自身不能被实例化的原因。URLConnection只能通过url.openConnection()方法创建具体的实例。而HttpURLConnection是URLConnection的子类,加入了http规范相关的操作。
-
虽然底层的网络连接可以被多个HttpURLConnection实例共享,但每一个HttpURLConnection实例只能发送一个请求。请求结束之后,应该调用HttpURLConnection实例的InputStream或OutputStream的close()方法以释放请求的网络资源,不过这种方式对于持久化连接没用。对于持久化连接,得用disconnect()方法关闭底层连接的socket。
-
HttpURLConnection是一个抽象类,其对象不能通过构造器直接产生,应用中通常通过URL对象调用openConnection()方法获取。http协议中最常用两种请求方式:get请求和post请求。
区别:
1.get请求没有请求体,post请求有请求体
2.get请求请求参数拼接到url地址上,post请求请求参数通过请求体发送给服务器
3.get请求的网页可以被收藏,post请求不能被收藏
4.get请求的url地址限制长度为2048个字符,post请求请求体长度没有限制
2、 HttpURLConnection 获取服务器数据★★★★★
- Get请求
1.首先创建URL对象,注意如果有请求参数应该拼接到这个对象中
URL url = new URL("http://localhost/day07/AutoLoginServlet?name=zs");
2.然后获取HttpURLConnection对象,注意需要强转。
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
3.设置相关参数
a)conn.setConnectTimeout(10);//请求超时时间10秒
b)conn.setUseCaches(false);// 设置是否缓存,不缓存
c)conn.setRequestMethod("GET");// 可以不写,默认get请求
d)conn.setDoOutput(true);// 有请求体就要设置为true,默认为false
e)conn.setDoInput(true);// 有响应体就设置为true,默认为true
f)conn.connect();//连接服务器,这个方法调用与否都不影响
g)conn.getResponseCode();//获取响应码,该语句会自动执行连接服务器的动作
4.获取响应数据
案例代码:
private void initData() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//网址不能写这个,http://localhost:8080/res/class.json,必须使用带ip的地址
//1.创建URL对象
URL url = new URL("http://192.168.1.103:8080/res/class.json");
//2.然后获取HttpURLConnection对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//3.获取响应码
int responseCode = conn.getResponseCode();
//4.判断响应码,成功的响应码为200
if (responseCode == HttpURLConnection.HTTP_OK){
//获取响应码描述信息,可以不写
String msg = conn.getResponseMessage();
//获取响应头信息,可以不写
String field=conn.getHeaderField("content-type");
Log.d("tag", "msg: "+msg+",field:"+field);
//5.获取输入流
InputStream is = conn.getInputStream();
//6.创建输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = -1;
while ((len = is.read(buf)) != -1) {
//7.将输入流数据读取出来写入输出流
baos.write(buf, 0, len);
}
//8.打印数据
Log.d("tag", "结果:"+baos.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
*注意:
1.清单文件添加网络权限
2.如果模拟器或者真机试Android9.0以上的系统,需要在清单文件中配置支持http网络请求(因为Android9.0开始系统不支持http请求,只支持加密的https请求,如果需要支持http请求,需要做配置)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xts.day09">
<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>
<!--android:usesCleartextTraffic="true",android9.0支持http请求配置-->
<application
android:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
- Post请求
相比get请求,post请求也并无太多不同之处。不同之处主要有,post请求有请求体,必须通过请求体发送请求参数,不能将请求参数拼接到url中。
这里我们拿一个真实的接口讲解案例
搜索接口:
https://www.wanandroid.com/article/query/0/json
请求方式:post
参数:k:“android”; //关键字
案例代码:
private void query() {
new Thread(new Runnable() {
@Override
public void run() {
URL url = null;
try {
//1.创建URL对象
url = new URL("https://www.wanandroid.com/article/query/0/json");
//2.然后获取HttpURLConnection对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//3.设置请求方式为post,下面两句代码都要有
conn.setRequestMethod("POST");
//4.有请求体就要设置为true,默认为false
conn.setDoOutput(true);
//有响应体就设置为true,默认为true
//conn.setDoInput(true);
//5.通过请求体发送请求数据
OutputStream os = conn.getOutputStream();
os.write(("k=android").getBytes());
// 如有中文需要url编码
// String userInfo = "user=" + URLEncoder.encode("zs", "utf-8");
// os.write(userInfo.getBytes());
os.flush();
//6.接收响应信息
int code = conn.getResponseCode();
if (code == 200) {
//7.读取响应信息
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = conn.getInputStream();
byte[] buf = new byte[1024];
int len = -1;
while ((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
Log.d("tag", baos.toString());
//8.关闭资源
os.close();
is.close();
}
//conn.disconnect();//这个方法调用与否都不影响,彻底关闭网络连接
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
3、 解析从服务器获取的jsonObject★★★★★
4、 解析从服务器获取的jsonArray★★★★★
返回的数据如果是以“{}”括起来的,那么我们需要使用JSONObject解析,如果是以“[]”括起来的,则需要使用JSONArray,以get请求为例,我们将返回的数据进行解析,服务器返回的json数据如下:
{"error":false,"results":[{"_id":"5b3ed2d5421aa91cfe803e35","createdAt":"2018-07-06T10:24:21.907Z","desc":"2018-07-06","publishedAt":"2018-07-06T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fszxi9lmmzj30f00jdadv.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b3d883f421aa906e5b3c6f1","createdAt":"2018-07-05T10:53:51.361Z","desc":"2018-07-05","publishedAt":"2018-07-05T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsysqszneoj30hi0pvqb7.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b3ae394421aa906e7db029b","createdAt":"2018-07-03T10:46:44.112Z","desc":"2018-07-03","publishedAt":"2018-07-03T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fswhaqvnobj30sg14hka0.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b398cf8421aa95570db5491","createdAt":"2018-07-02T10:24:56.546Z","desc":"2018-07-02","publishedAt":"2018-07-02T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsvb1xduvaj30u013175p.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b33ccf2421aa95570db5478","createdAt":"2018-06-28T01:44:18.488Z","desc":"2018-06-28","publishedAt":"2018-06-28T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsq9iq8ttrj30k80q9wi4.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b32807e421aa95570db5471","createdAt":"2018-06-27T02:05:50.227Z","desc":"2018-06-27","publishedAt":"2018-06-27T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsp4iok6o4j30j60optbl.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b31aa33421aa9556d2cc4a7","createdAt":"2018-06-26T10:51:31.60Z","desc":"2018-06-26","publishedAt":"2018-06-26T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsoe3k2gkkj30g50niwla.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b2f8847421aa9556b44c666","createdAt":"2018-06-24T20:02:15.413Z","desc":"2018-06-24","publishedAt":"2018-06-25T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsmis4zbe7j30sg16fq9o.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b0d6ac0421aa97efda86560","createdAt":"2018-05-29T22:59:12.622Z","desc":"2018-06-02","publishedAt":"2018-06-22T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1frslruxdr1j30j60ok79c.jpg","used":true,"who":"lijinshanmx"},{"_id":"5b27c7aa421aa923c0fbfda0","createdAt":"2018-06-18T22:54:34.199Z","desc":"2018-06-19","publishedAt":"2018-06-21T00:00:00.0Z","source":"web","type":"\u798f\u5229","url":"http://ww1.sinaimg.cn/large/0065oQSqly1fsfq1k9cb5j30sg0y7q61.jpg","used":true,"who":"lijinshanmx"}]}
分析:
1.最外层的是{}括起来的,所以返回的数据需要放入JSONObject解析
2.results对应的value是以[]括起来的,所以对应的value需要放入JSONArray解析
3.results对应的value集合中,每一个都是使用{}括起来的,所以需要JSONObject去解析
编写实体类Bean,具体代码这里就不贴了,以下是Java原生的解析方式:
//服务器返回的数据
String json = baos.toString();
//创建实体类对象
Bean bean = new Bean();
ArrayList<Bean.ResultsBean> list = new ArrayList<>();
//将list设置到bean中
bean.setResults(list);
//JsonObject解析数据
JSONObject jsonObject = new JSONObject(json);
//获取到值为boolean类型,key为error的value,
boolean error = jsonObject.optBoolean("error");
bean.setError(error);
//通过格式化json串,我们看到key为results的value是个集合
JSONArray results = jsonObject.optJSONArray("results");
//通过for循环将JSONArray中的数据分别取出
for (int i = 0; i < results.length(); i++) {
//JSONArray中的每一个元素都是JSONObject
JSONObject item = (JSONObject) results.get(i);
//获取JSONObject中的每一个key对应的value
String _id = item.optString("_id");
String createdAt = item.optString("createdAt");
String desc = item.optString("desc");
String publishedAt = item.optString("publishedAt");
String source = item.optString("source");
String type = item.optString("type");
String _url = item.optString("url");
boolean used = item.optBoolean("used");
String who = item.optString("who");
//创建一个Bean.ResultsBean,将解析出的数据设置到这个对象中
Bean.ResultsBean resultsBean = new Bean.ResultsBean();
resultsBean.set_id(_id);
resultsBean.setCreatedAt(createdAt);
resultsBean.setDesc(desc);
resultsBean.setPublishedAt(publishedAt);
resultsBean.setSource(source);
resultsBean.setType(type);
resultsBean.setUrl(_url);
resultsBean.setUsed(used);
resultsBean.setWho(who);
//将resultsBean 添加到集合中
list.add(resultsBean);
}
//打印bean
Log.d("tag", "list: "+bean.toString());
四、Gson解析json
1、Gson解析jsonObject★★★★★
2、 Gson解析jsonArray★★★★★
通过上面的案例我们发现,如果自己使用原生的解析,写起来比较费劲,需要判断是jsonObject还是jsonArray,然后再去解析,还需要通过key去解析出对应的value,然后设置到实体类对象中。接下来我们介绍一个工具类Gson,它可以帮助我们解析Json串,省去很多原生解析的麻烦,使用如下:
1. 添加依赖:
implementation 'com.google.code.gson:gson:2.8.2'
2. 使用Gson对象将json串解析成实体类:
//服务器返回的json串
String json = baos.toString();
//创建Gson对象
Gson gson = new Gson();
//解析,第一个参数:json字符串,第二个参数:实体类对应的class对象
Bean data = gson.fromJson(json, Bean.class);
Log.d("tag", "data: "+data.toString());
五、 Handler
Handler是Android中的消息机制,主要用来做线程之间通信的。在Android中,为了保障线程安全,规定只能由主线程来更新UI信息。而在实际开发中,会经常遇到多个子线程都去操作UI信息的情况,那么就会导致UI线程不安全。这时,我们就需要借助 Handler 作为媒介,让 Handler 通知主线程按顺序一个个去更新UI,避免UI线程不安全。
1、 子线程给主线程发送数据★★
2、使用TextView显示获取的json★★★
1.在Activity中创建Handler对象
//1.Handler在哪个线程中创建,最终handleMessage处理消息就在哪个线程
//这里是属于Acivity,是主线程
Handler mHandler = new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
//处理消息的回调方法,这里是主线程
//msg:这个msg就是子线程发送过来的对象
int what = msg.what;
if (what == 0){
//注意,子线程message对象携带的是什么对象,这里拿到的就是什么对象
//我们子线程传递的是Bean对象,这里可以强转成Bean
Bean bean = (Bean) msg.obj;
//将数据设置到TextView上
mTv.setText(bean.toString());
}
}
};
2.在获取网络数据的子线程中,通过Handler将携带数据的Message对象发送到主线程
//子线程不能刷新ui,需要将子线程中获取的网络数据发送到主线程
//2.创建/获取Message对象
//一般不直接new,会额外消耗内存,
// Message message = new Message();
//可以通过Message.obtain()获取Message对象,这个对象是Message消息池中已有的对象
Message message = Message.obtain();
//将数据通过message携带过去
message.obj = bean;
//可以设置一个what,区分是哪个线程发送的数据
message.what = 0;
//通过handler将message发送到主线程
mHandler.sendMessage(message);
网友评论