项目三:网络层和数据模型的封装
任务一:网络请求和数据解析
1,网络请求
(1)Android中的网络存储主要是使用HTTP/HTTPS协议访问服务器,与服务器发生数据交互。
(2)Android提供两种执行HTTP请求的方式:
HttpClient请求(有bugs,API23已经被弃用) HttpURLConnection请求
HttpUrlConnection执行http Get请求核心代码:
public class PostUtils {
public static String LOGIN_URL = "填URL地址";
public static String LoginByPost(String number, String passwd) {
String msg = "";
try {
HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection();
// 设置请求方式,请求超时信息
conn.setRequestMethod("POST");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
// 设置运行输入,输出:
conn.setDoOutput(true);
conn.setDoInput(true);
// Post方式不能缓存,需手动设置为false
conn.setUseCaches(false);
// 我们请求的数据:
String data = "passwd=" + URLEncoder.encode(passwd, "UTF-8") + "&number=" + URLEncoder.encode(number, "UTF-8");
// 这里可以写一些请求头的东东...
// 获取输出流
OutputStream out = conn.getOutputStream();
out.write(data.getBytes());
out.flush();
if (conn.getResponseCode() == 200) {
// 获取响应的输入流对象
InputStream is = conn.getInputStream();
// 创建字节输出流对象
ByteArrayOutputStream message = new ByteArrayOutputStream();
// 定义读取的长度
int len = 0;
// 定义缓冲区
byte buffer[] = new byte[1024];
// 按照缓冲区的大小,循环读取
while ((len = is.read(buffer)) != -1) {
// 根据读取的长度写入到os对象中
message.write(buffer, 0, len);
}
// 释放资源
is.close();
message.close();
// 返回字符串
msg = new String(message.toByteArray());
return msg;
}
} catch (Exception e) {
e.printStackTrace();
}
return msg;
}
}
(3)使用OKhttp框架进行网络请求(基于HttpURLConnection进行封装)
OKhttp可以实现的功能:
get/post同步,异步请求
HTTPS安全证书形式的网络请求
文件上传和下载
图片加载
支持请求回调,直接返回对象或者对象的集合
支持session的保存
OKhttp的特点:
Http/2支持多路复用
采用连接池减少请求延时
支持GZIP压缩
相应缓存
支持websocket
多IP切换(连接失败并且服务器有多IP)
OKhttp的使用
使用OKhttp执行 GET请求的使用步骤
第一步:新建一个OkhttpClient对象,这个对象最好在整个APP中只有一个实例存在。
第二步:新建一个Request请求对象,它使用Builder的方式进行创建,需要包含请求的接口URL
第三步:使用OkhttpClient对象新建一个Call请求,并传入Request对象
第四步:将Call加入请求队列,执行该请求
示例核心代码:
/**
* OkHttp 异步 Get 请求
*/
private void httpAsynchronousGet() {
// Request 中封装了请求相关信息
Request request = new Request.Builder()
.url("https://www.baidu.com") // 设置请求地址
.get() // 使用 Get 方法
.build();
// 异步 Get 请求
mOkHttpClient.newCall(request).enqueue(new Callback(){
@Override
public void onFailure(Call call, IOException e) {
// 请求失败的情况
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 请求成功 , 获取
String result = response.body().string();
Log.i(TAG, "result : " + result);
runOnUiThread(new Runnable() {
@Override
public void run() {
// 主线程中执行相关代码
}
});
}
});
}
使用Okhttp执行Http POST请求的使用步骤
第一步:新建一个OkhttpClient对象。
第二步:使用FormBody来构建包含键值对类型参数的请求体。
第三步:新建一个Request请求对象,需要包含请求的接口URL和请求体
第四步:使用OkhttpClient对象新建一个Call请求,并传入Request对象
第五步:将Call加入请求队列,执行该请求
示例核心代码:
/**
* OkHttp 异步 Post 请求
*/
private void httpAsynchronousPost() {
// 创建 Post 表单 , 主要用于设置 Post 请求键值对
FormBody formBody = new FormBody.Builder()
.add("Key", "Value")
.build();
// Request 中封装了请求相关信息
Request request = new Request.Builder()
.url("https://www.baidu.com") // 设置请求地址
.post(formBody) // 使用 Post 方法
.build();
// 创建异步回调
Callback callback = new Callback(){
@Override
public void onFailure(Call call, IOException e) {
// 请求失败的情况
}
@Override
public void onResponse(Call call, Response response) throws IOException {
// 请求成功 , 获取
String result = response.body().string();
Log.i(TAG, "result : " + result);
runOnUiThread(new Runnable() {
@Override
public void run() {
// 主线程中执行相关代码
}
});
}
};
// 异步 Get 请求
mOkHttpClient.newCall(request).enqueue(callback);
}
2,数据解析
(1)概述
我们与服务器进行数据交互的数据格式大部分都是JSON格式(取代XML的数据结构)
对JSON数据进行反序列化操作,转换成相应的对象或集合-------数据解析
JSON的语法(JSON本质是一种特定的字符串):“[ ]”表示数组,“{ }”表示对象,数据以键值对的进行表示。
JSON解析的结果有两种形式:一种是Javabean(单个对象实体类的对象);另一种是集合。它可以是List
(2)传统的数据解析(根据JSON字符串不同,解析的方式也不同)
常见的是JSONObject(对应一个实体类的对象)和JSONArray(对应一个数组)互相嵌套使用
(3)Gson框架解析
主要提供两个方法:
fromJson()方法--------实现将JSON数据转换为相应的Java对象。
核心代码示例:
Gson gson = new Gson();
String personTest = gson.toJson(p);
toJson()方法-----------实现将Java对象转换为相对应的JSON数据。
Person person = gson.fromJson(personTest, Person.class);
System.out.println(person);
Gson框架的优点:
实现Java对象和JSON之间的互相转换。
允许已经存在的无法改变的对象,转换成JSON,或者JSON转换成已存在的对象。
允许自定义对象的表现形式。
支持任意的复杂对象。
能够生成可压缩和可读的JSON的字符串输出。
Gson框架使用关键:
推荐把成员变量都声明成private修饰。
如果某个字段被transient关键词修饰,就不会被序列化或者反序列化。
当序列化的时候,如果对象的某个字段为null,是不会输出到JSON字符串中的。
当反序列化的时候,某个字段在JSON字符串中找不到对应的值,就会被赋值为null。
当内部类的某个字段和外部类的某个字段一样的话,就会被忽视,不会被序列化或者反序列化。
Gson框架的使用示例:(略)
任务二:数据模型的封装
1,理解MVP架构模式
MVP是传统MVC架构模式的扩展。使用MVC架构模式,开发者常会遇到以下困难:
大部分的核心业务逻辑放在Controller中,在应用程序的整个生命周期内,这个文件会变得越来越大,越来越难维护。
由于UI和业务逻辑的紧密耦合,Controller层和View层都将属于同一个activity或fragment。这将导致在更改应用程序功能时出现问题。
由于大多数测试的部分依赖Android SDK组件,因此针对不同层执行单元测试时变得困难了。
MVP模式克服了MVC模式的这些挑战,并且提供了一种简单的方法来构造项目代码。MVP模式之所以被广泛接受,因为它提供了模块化、可测试以及更干净和更易于维护的代码基准。它由以下三部分组成:
Model:用于存储数据。它负责处理领域逻辑以及与数据库或网络层的通信。
View:UI层,提供数据可视化界面,并跟踪用户的操作,以便通知presenter。
Presenter:从Model层获取数据,并且应用UI逻辑来决定显示什么。它管理View的状态,并且根据来自于View的用户的输入执行动作。
mvp架构要点
View和Presenter以及Presenter和Model之间通过接口(也称为contract)通信。
一个Presenter管理一个View,即:presenter和view是一对一的关系。
Model和View之间无关联。
2,数据模型的封装(略)
3,引入greenDao数据库框架
(1)用于生成数据库代码
任务三:网络框架的封装
1,Retrofit概述(Retrofit将您的HTTP API转换为Java接口;Retrofit 是一个 RESTful 的 HTTP网络请求框架的封装,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责网络请求接口的封装;)
(1)Square公司非常严格的RestFul客户端库,采用注解来描述HTTP请求,默认会集成URL参数替换。
(2)提供了自定义头信息,多请求体,文件上传下载,模拟响应等
(3)基本包含Okhttp库的所以特性和功能。
(4)引入
(5)使用说明
1,引用,在gradle文件中引用retrofit2、定义接口,Retrofit要求定义一个网络请求的接口,接口函数里要定义url路径、请求参数、返回类型。3、依次获得Retrofit对象、接口实例对象、网络工作对象 首先,需要新建一个retrofit对象。
然后,根据上一步的接口,实现一个retrofit加工过的接口对象。
最后,调用接口函数,得到一个可以执行网络访问的网络工作对象。
4、访问网络数据,用上一步获取的worker对象,执行网络请求,在回调函数里,取得我们需要的BizEntity数据对象。网络访问结束。
2,RxJava
(1)概述
1)是一个在Java VM上使用可观测的序列化来组成异步的,基于事件的程序的库。
2)质就是一个实现异步操作的库,异步操作的实现通过一种可扩展的观察者模式。
3)RxJava的观察者模式主要有四个概念:
被观察者:Observable,相当于Button。
观察者:Observer,相当于onClickListener
订阅:subscribe,相当于setOnClickListener。
事件:普通事件onNext(),相当于onClick;
4)它还有一些事件:onCompleted()/onError()/onStart()
2,引入()
1、引入 RxJava 依赖
Gradle 项目中 , 在 build.gradle 构建脚本中 , 添加如下依赖 ;
dependencies {
implementation 'io.reactivex.rxjava2:rxjava:2.2.21'
}
rxjava3 依赖 :
dependencies {
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
}
3,使用说明
1、定义 Observer 观察者
Observer(观察者): Observer 表示一个接收 Observable 发送消息 的观察者。 它可以处理从 Observable 发射的消息, 还可以处理错误和完成的事件。
Observer 观察者 是 操作的核心 , 定义在需要进行具体操作的位置 , 执行具体的 异步操作 或 事件 ;
如 : 在 UI 界面中 , 点击按钮 , 查询远程数据库服务器中的数据 , 查询完毕后更新 UI 界面 ;
该 Observer 观察者 就需要 定义在 UI 界面中 , 可以获取到相关的 UI 组件进行数据更新 ;Observable 被观察者可以定义在 Observer 观察者位置 , 也可以定义在消息发送的位置 , 这里 推荐定义在消息发送的位置 ;调用时 , 将 Observer 观察者 传递给对应的异步操作函数 ;在异步操作函数中 , 创建 Observable 被观察者 , 并且通过订阅将观察者订阅到被观察者中 ;订阅操作 , 就会同时发送消息给 观察者 ;
Observer 观察者定义代码 :
Observer observer = new Observer() {
@Override
public void onSubscribe(Disposable d) {
// 当观察者订阅时的回调
}
@Override
public void onNext(String value) {
// 当接收到新的事件时的回调
System.out.println(value);
}
@Override
public void onError(Throwable e) {
// 当发生错误时的回调
}
@Override
public void onComplete() {
// 当事件流结束时的回调
}
};
2、定义 Observable 被观察者
Observable(被观察者): Observable 是一个 可以发送消息的数据源 , 可以同时发送若干消息 , 消息的格式可以通过泛型进行定义 ; 消息发送完毕后 会 通知观察者。Observable 通过 订阅观察者 来实现 消息的传递。
Observable observable = Observable.just("Hello", "World");
3、Observable 被观察者订阅 Observer 观察者
Subscription(订阅): 订阅是 Observer 对 Observable 的绑定, 它表示观察者正在接收 Observable 的数据项。 订阅可以被取消, 取消订阅后 Observer 观察者将不再接收 Observable 被观察者 的消息。
调用 Observable 被观察者 的 subscribe 函数 , 订阅 Observer 观察者 ;
该订阅操作的同时 , 会将消息发送给 Observer 观察者 , 触发 Observer#onNext 函数 ;
observable.subscribe(observer);
二、代码示例
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public class Main {
public static void main(String[] args) {
Observable observable = Observable.just("Hello", "World", "RxJava");
Observer observer = new Observer() {
@Override
public void onSubscribe(Disposable d) {
// 当观察者订阅时的回调
}
@Override
public void onNext(String value) {
// 当接收到新的事件时的回调
System.out.println(value);
}
@Override
public void onError(Throwable e) {
// 当发生错误时的回调
}
@Override
public void onComplete() {
// 当事件流结束时的回调
}
};
observable.subscribe(observer);
}
}
执行结果 :
Hello
World
RxJava
4,线程控制(略)
5,变换(略)
Retrofit与RxJava的联合封装
2,引入(略)
3,封装的公共部分(略)