项目三:网络层和数据模型的封装
任务一:网络请求和数据解析
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方式不能缓存,需手动设置为falseconn.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(){
@Overridepublic void onFailure(Call call, IOException e) {// 请求失败的情况}
@Overridepublic void onResponse(Call call, Response response) throws IOException {// 请求成功 , 获取String result = response.body().string();Log.i(TAG, "result : " + result);runOnUiThread(new Runnable() {@Overridepublic 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(){
@Overridepublic void onFailure(Call call, IOException e) {// 请求失败的情况}
@Overridepublic void onResponse(Call call, Response response) throws IOException {// 请求成功 , 获取String result = response.body().string();Log.i(TAG, "result : " + result);runOnUiThread(new Runnable() {@Overridepublic void run() {// 主线程中执行相关代码}});}};// 异步 Get 请求mOkHttpClient.newCall(request).enqueue(callback);}
2,数据解析
(1)概述
-
我们与服务器进行数据交互的数据格式大部分都是JSON格式(取代XML的数据结构)
-
对JSON数据进行反序列化操作,转换成相应的对象或集合-------数据解析
-
JSON的语法(JSON本质是一种特定的字符串):“[ ]”表示数组,“{ }”表示对象,数据以键值对的进行表示。
-
JSON解析的结果有两种形式:一种是Javabean(单个对象实体类的对象);另一种是集合。它可以是List<String>,List<T>等
(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<String> observer = new Observer<String>() {@Overridepublic void onSubscribe(Disposable d) {// 当观察者订阅时的回调}
@Overridepublic void onNext(String value) {// 当接收到新的事件时的回调System.out.println(value);}
@Overridepublic void onError(Throwable e) {// 当发生错误时的回调}
@Overridepublic void onComplete() {// 当事件流结束时的回调}
};
2、定义 Observable 被观察者
Observable(被观察者): Observable 是一个 可以发送消息的数据源 , 可以同时发送若干消息 , 消息的格式可以通过泛型进行定义 ; 消息发送完毕后 会 通知观察者。Observable 通过 订阅观察者 来实现 消息的传递。
Observable<String> 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<String> observable = Observable.just("Hello", "World", "RxJava");
Observer<String> observer = new Observer<String>() {@Overridepublic void onSubscribe(Disposable d) {// 当观察者订阅时的回调}
@Overridepublic void onNext(String value) {// 当接收到新的事件时的回调System.out.println(value);}
@Overridepublic void onError(Throwable e) {// 当发生错误时的回调}
@Overridepublic void onComplete() {// 当事件流结束时的回调}};
observable.subscribe(observer);}
}
执行结果 :
Hello
World
RxJava
4,线程控制(略)
5,变换(略)
Retrofit与RxJava的联合封装
1,封装后的网络请求示意图
2,引入(略)
3,封装的公共部分(略)