Android 网络框架以及封装

Android 中在【网络】部分常用的框架组合是:

OkHttp + Retrofit

一,准备

添加权限

image-20230326164537884

1
<uses-permission android:name="android.permission.INTERNET" />

添加依赖

image-20230326164757266

image-20230326164731781

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//region 请求网络相关
//提示:region这种语法是最新的,推荐使用这种,也更容易阅读,不建议在同一个文件同时使用
//因为可能会显示出错
//okhttp
//https://github.com/square/okhttp
implementation 'com.squareup.okhttp3:okhttp:4.9.3'

//用来打印okhttp请求日志
//当然也可以自定义
implementation("com.squareup.okhttp3:logging-interceptor:4.9.3")

//retrofit
//https://github.com/square/retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'

//使用gson解析json
//https://github.com/google/gson
implementation 'com.google.code.gson:gson:2.9.0'

//适配retrofit使用gson解析
//版本要和retrofit一样
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

//适配retrofit支持rxjava
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'

//使用了Android响应式编程
//RxJava和RxAndroid区别?
//简单来说:就是RxAndroid在RxJava的基础上
//优化了一些功能
//增强了Android特有的功能
//https://github.com/ReactiveX/RxAndroid
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
//endregion

上面直接所有网络相关的依赖都加进去了。

配置网络地址

也可以直接放在Config里面。

好处:

可以这样。

image-20230326173306893

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
android {
/**
* 下面被注释掉的文件是已经配置到 common.gradle 文件,所以这里就可以不写了
* 如果不一样,这里可以再写要用的配置,来覆盖公共配置文件内容
*/

defaultConfig {
...
//不加下面这个 sync 之后,那个java Config包出不来。
//渠道相关
flavorDimensions "versionCode"
}
buildFeatures {
buildConfig true
}
//配置不同的环境
productFlavors {
//本地开发环境
local {
//API端点
buildConfigField('String', "ENDPOINT", '"http://192.168.50.139:8080/"')

//资源端点
buildConfigField 'String', 'RESOURCE_ENDPOINT', '"http://course-music-dev.ixuea.com/%s"'

dimension = minSdkVersion
}

//开发环境(我们都在用这个)
dev {
//API端点
buildConfigField('String', "ENDPOINT", '"http://my-cloud-music-api-sp3-dev.ixuea.com/"')

//资源端点
buildConfigField 'String', 'RESOURCE_ENDPOINT', '"http://course-music-dev.ixuea.com/%s"'

dimension = minSdkVersion
}

//正式环境
prod {
//API端点
buildConfigField 'String', 'ENDPOINT', '"http://my-cloud-music-api-sp3.ixuea.com/"'

//资源端点
buildConfigField 'String', 'RESOURCE_ENDPOINT', '"http://course-music.ixuea.com/%s"'

dimension = minSdkVersion
}
}
}

image-20230326165901353

在添加了依赖之后,点击 sync 同步。然后 run 一下。

我们的资源网址就在BuildConfig这个Java类里面了。

我们这里直接放在一个系统的 Config 文件中方便管理。

让APP可以发送 HTTP 请求

一般来说,APP现在不允许发送 Http 请求。

但是 OKHttp 就是 Http 请求。

所以为了测试成功还要再加这模一句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.ixuea.courses.mymusic">

...

<application
android:usesCleartextTraffic="true"
....>
...
</application>

</manifest>

image-20230326172104068

二,OkHttp

用OKHttp 请求网络。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import android.os.Bundle;
import android.util.Log;

import com.example.testandroid.activity.BaseLogicActivity;
import com.example.testandroid.config.Config;

import java.io.IOException;

import io.reactivex.rxjava3.annotations.NonNull;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;


/**
* okhttp get请求
*/
private void testGet() {
//构建 OKHttp 客户端 client 实例
OkHttpClient client = new OkHttpClient();

//拼接好 绝对地址
String url = Config.ENDPOINT + "v1/sheets";

//建立OKHttp请求
Request request = new Request.Builder()
.url(url)
.build();

//利用上面创建的客户端实例去发送请求,同时观察结果。
//同时做回调处理
client.newCall(request).enqueue(new Callback() {
//失败 -> 执行下面这个方法
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
//失败就打印失败日志
Log.e(TAG, "onFailure: " + e.getLocalizedMessage());
}
//成功 -> 就执行这个方法
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
//成功就打印返回的数据流 String 类型
Log.d(TAG, "onResponse: " + response.body().string());
}
});

}

测试成功的样子。

image-20230326172406897

三,Retrofit

OKHttp的具体创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

/**
* 提供OkHttpClient
*/
public static OkHttpClient provideOkHttpClient() {
//初始化okhttp
OkHttpClient.Builder okhttpClientBuilder = new OkHttpClient.Builder();

//配置缓存
//AppContext.getInstance() 这个是拿到了这个 APP 的上下文(自己写的)
Cache cache = new Cache(AppContext.getInstance().getCacheDir(), Config.NETWORK_CACHE_SIZE);
//Config.NETWORK_CACHE_SIZE 是自己定义的,这里是100M
okhttpClientBuilder.cache(cache);

//设置超时时间
okhttpClientBuilder.connectTimeout(10, TimeUnit.SECONDS) //连接超时时间
.writeTimeout(10, TimeUnit.SECONDS) //写,也就是将数据发送到服务端超时时间
.readTimeout(10, TimeUnit.SECONDS); //读,将服务端的数据下载到本地

//Config.DEBUG 这个也是自己写的一个 boolean 变量
if (Config.DEBUG) {
//调试模式

//创建okhttp日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();

//设置日志等级
loggingInterceptor.level(HttpLoggingInterceptor.Level.BASIC);

//添加到网络框架中
okhttpClientBuilder.addInterceptor(loggingInterceptor);
}

return okhttpClientBuilder.build();
}

补充:

AppContext:

1
2
3
4
5
6
7
8
9
10
private static AppContext instance;
@Override
public void onCreate() {
super.onCreate();
instance = this;
...
}
public static AppContext getInstance() {
return instance;
}

Config:

1
2
3
4
5
6
7
8
9
/**
* 是否打开了调试模式
*/
public static final boolean DEBUG = BuildConfig.DEBUG;
/**
* 网络缓存目录大小
* 100M
*/
public static final long NETWORK_CACHE_SIZE = 1024 * 1024 * 100;

Retrofit 的具体创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 提供Retrofit实例
*
* @param okHttpClient
* @return
*/
public static Retrofit provideRetrofit(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
//让retrofit使用okhttp
.client(okHttpClient)

//api地址 Config.ENDPOINT这个是自己在特别类里面定义的
.baseUrl(Config.ENDPOINT)

//适配rxjava
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())

//使用gson解析json
//包括请求参数和响应
.addConverterFactory(GsonConverterFactory.create(JSONUtil.createGson()))

//创建retrofit
.build();
}

上面有些变量或者工具类要自己创建。

JSONUtil.createGson()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class JSONUtil {
public static Gson createGson() {
GsonBuilder gsonBuilder = new GsonBuilder();

//驼峰转下划线
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
Gson gson = gsonBuilder.create();

return gson;
}
}

创建 service

描述 API 请求方式,请求地址,请求参数是什么。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.ixuea.courses.mymusic.component.api;



import io.reactivex.rxjava3.core.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;

/**
* 默认远程数据源
*/
public interface DefaultService {
/**
* 音乐列表
*
* @return
*/
@GET("v1/sheets")
Observable<String> sheets(@Query(value = "category") String category, @Query(value = "size") int size);
}

Observable: 网络请求数据打包的返回对象

其实不应该是 String 而是具体返回的是什么 Java 类型在本地搞他的实例类。

Retrofit 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
private DefaultService service;

@Override
protected void initDatum() {
super.initDatum();
Retrofit retrofit = NetworkModule.provideRetrofit(NetworkModule.provideOkHttpClient());
service = retrofit.create(DefaultService.class);
testRetrofitGet();
}


/**
* retrofit get 请求
*/

private void testRetrofitGet() {
service.sheets(null, 2)
.subscribeOn(Schedulers.io())//网络申请放到子线程执行
.observeOn(AndroidSchedulers.mainThread())//网络访问结果官产放在主线程
.subscribe(new Observer<SheetWrapper>() {
@Override
public void onSubscribe(@NonNull Disposable d) {

}

@Override
public void onNext(@NonNull SheetWrapper sheetWrapper) {
Sheet sheet = sheetWrapper.getData().getData().get(0);
Log.d(TAG, "onNext" + sheet.getTitle());
}

@Override
public void onError(@NonNull Throwable e) {
Log.d(TAG, "onError" + e.getMessage());
}

@Override
public void onComplete() {

}
});

}

SheetWrapper:是这个网络请求返回的对象类型。(需要自己对照json去在本地定义对象。)

直接丢String是不行的。

具体问题要具体分析。

四,网络框架封装

网络响应格式封装

BaseResponse

因为网络请求的返回json最外面都是status这种什么的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

/**
* 通用网络请求响应模型
*/
public class BaseResponse {
/**
* 状态码
* 等于0表示成功
*/
private int status;
/**
* 出错的提示信息
* 发生了错误不一定有
*/
private String message;

public int getStatus() {
return status;
}

public void setStatus(int status) {
this.status = status;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

/**
* 是否成功
*
* @return
*/
public boolean isSucceeded() {
return status == 0;
}
}

DetailResponse

列表网络请求会有分页那个。详情就是直接真实data。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

/**
* 详情网络请求解析类
* <p>
* <p>
* 继承BaseResponse
* 定义了一个泛型T
*/
public class DetailResponse<T> extends BaseResponse {
/**
* 真实数据
* 类似是泛型
*/
private T data;

public T getData() {
return data;
}

public void setData(T data) {
this.data = data;
}
}


ListResponse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 解析列表网络请求
*/
public class ListResponse<T> extends BaseResponse {
private Meta<T> data;

public Meta<T> getData() {
return data;
}

public void setData(Meta<T> data) {
this.data = data;
}
}

Meta 就是那个网络请求列表的很多列,然后有分页的一下设置。

可以说 Meta 就是分页模型。

Meta:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.util.List;

/**
* 解析列表网络请求
*/
public class Meta<T> {

/**
* 下一页
*/
private Integer next;

private List<T> data;

/**
* 有多少条
*/
private Integer total;

/**
* 有多少页
*/
private Integer pages;

/**
* 当前每页显示多少条
*/
private Integer size;

/**
* 当前页
*/
private Integer page;
// /**
// * 获取下一页
// *
// * @param data
// * @return
// */
// public static int nextPage(Meta<Comment> data) {
// if (data == null || data.next == null) {
// return 1;
// }
// return data.next;
// }

public List<T> getData() {
return data;
}

public void setData(List<T> data) {
this.data = data;
}



public Integer getTotal() {
return total;
}

public void setTotal(Integer total) {
this.total = total;
}

public Integer getPages() {
return pages;
}

public void setPages(Integer pages) {
this.pages = pages;
}

public Integer getSize() {
return size;
}

public void setSize(Integer size) {
this.size = size;
}

public Integer getPage() {
return page;
}

public void setPage(Integer page) {
this.page = page;
}

public Integer getNext() {
return next;
}

public void setNext(Integer next) {
this.next = next;
}
}

上面应用

现在我们还想请求一个歌单==列表==。

因为是列表,所以用 ListResponse。

DefaultService 改成这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.example.testandroid.component.sheet.model.Sheet;
import com.example.testandroid.model.response.ListResponse;

import io.reactivex.rxjava3.core.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;

/**
* 默认远程数据源
*/
public interface DefaultService {
/**
* 音乐列表
*
* @return
*/
@GET("v1/sheets")
Observable<ListResponse<Sheet>> sheets(@Query(value = "category") String category, @Query(value = "size") int size);
}

然后对应调用的地方只用改一下反省的哪里就可以辣。

1
2
3
4
service.sheets(null, 2)
.subscribeOn(Schedulers.io())//网络申请放到子线程执行
.observeOn(AndroidSchedulers.mainThread())//网络访问结果官产放在主线程
.subscribe(new Observer<ListResponse<Sheet>>() {

其实理解了,还是这个好用。

如果要访问 DetailResponse就直接换对应的就可以。这个简单不赘诉。

公共模型封装

Base

所有模型父类

1
2
3
4
5
/**
* 所有模型父类
*/
public class Base {
}

BaseId

继承 Base 的带 Id 的父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.util.Objects;

/**
* 模型Id父类
*/
public class BaseId extends Base{
/**
* Id
*/
private String id;

public BaseId() {
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BaseId baseId = (BaseId) o;
return Objects.equals(id, baseId.id);
}

@Override
public int hashCode() {
return Objects.hash(id);
}
}

Common

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 所有模型父类
* <p>
* 这里时间不参与比较,所以不重写equals,hashCode
* 如果项目中需要参数比较,要重写
*/
public class Common extends BaseId {
/**
* 创建时间
*/
private String createdAt;
/**
* 更新时间
*/
private String updatedAt;

public String getCreatedAt() {
return createdAt;
}

public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}

public String getUpdatedAt() {
return updatedAt;
}

public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
}

创建时间和更新时间。

其他模型

按需继承上面的。

然后父类有的,子类全删了。就行了。

封装自动处理错误

image-20230601205146324

每次网络请求都要重写这么多,显得很臃肿。

就是想,没特殊情况就不用重修占领位置了。

ObserverAdapter

继承这个类,后面的只需要想写谁就行,不必须每个都实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;

public class ObserverAdapter<T> implements Observer<T> {
@Override
public void onSubscribe(@NonNull Disposable d) {

}

@Override
public void onNext(@NonNull T t) {

}

@Override
public void onError(@NonNull Throwable e) {

}

@Override
public void onComplete() {

}
}

HttpObserver

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import com.example.testandroid.model.response.BaseResponse;
import com.example.testandroid.utils.HttpUtil;

import retrofit2.Response;

public abstract class HttpObserver<T> extends ObserverAdapter<T>{

/**
* 请求成功
*
* @param data
*/
public abstract void onSucceeded(T data);

/**
* 请求失败
*
* @param data
* @param e
* @return true:自己处理;false:框架处理
*/
public boolean onFailed(T data, Throwable e) {

return false;
}

@Override
public void onNext(T t) {
super.onNext(t);
onEnd();

if (isSucceeded(t)) {
//请求正常
onSucceeded(t);
} else {
//请求出错了
handlerRequest(t, null);
}
}
@Override
public void onError(Throwable e) {
super.onError(e);
onEnd();

//处理错误
handlerRequest(null, e);
}
/**
* 网络请求是否成功了
*
* @param t
* @return
*/
private boolean isSucceeded(T t) {
if (t instanceof Response) {
//retrofit里面的响应对象

//获取响应对象
Response response = (Response) t;

//获取响应码
int code = response.code();

//判断响应码
if (code >= 200 && code <= 299) {
//网络请求正常
return true;
}

} else if (t instanceof BaseResponse) {
//判断具体的业务请求是否成功
BaseResponse response = (BaseResponse) t;

return response.isSucceeded();
}

return false;
}
/**
* 处理错误网络请求
*
* @param data
* @param error
*/
private void handlerRequest(T data, Throwable error) {
if (onFailed(data, error)) {
//回调了请求失败方法
//并且该方法返回了true

//返回true就表示外部手动处理错误
//那我们框架内部就不用做任何事情了
} else {
ExceptionHandlerUtil.handlerRequest(data,error);
}

}
/**
* 请求结束,成功失败都会调用(调用前调用),使用在这里隐藏加载提示
*/
public void onEnd() {

}
}

ExceptionHandlerUtil.handlerRequest

这个需要【封装Toast】和【Loading对话框】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import android.text.TextUtils;

import com.example.testandroid.AppContext;
import com.example.testandroid.R;
import com.example.testandroid.model.response.BaseResponse;

import org.apache.commons.lang3.StringUtils;

import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import retrofit2.HttpException;
import retrofit2.Response;

/**
* 错误处理辅助方法
*/
public class ExceptionHandlerUtil {
/**
* 网络请求错误处理
* @param data
* @param error
* @param <T>
*/
public static <T> void handlerRequest(T data, Throwable error) {
if (error != null){
//先处理有异常的请求
handleException(error);
}else {
if (data instanceof Response){
//retrofit 里面的对象

//获取响应对象
Response response = (Response) data;

//获取响应码
int code = response.code();

//判断响应码
if (code >= 200 && code <= 299) {
//网络请求正常

} else {
handleHttpError(code);
}
}else if (data instanceof BaseResponse) {
//判断具体的业务请求是否成功
BaseResponse response = (BaseResponse) data;

if (TextUtils.isEmpty(response.getMessage())) {
//没有错误提示信息
TipUtil.showError(R.string.error_unknown);
} else {
TipUtil.showError(response.getMessage());
}
}
}
}

/**
* 处理异常
* @param error
*/
private static void handleException(Throwable error) {
if (error.getCause() != null){
//如果发生的这个异常有原因,就先判断原因
error = error.getCause();
}

//判断错误类型
if (error instanceof UnknownHostException) {
TipUtil.showError(R.string.error_network_unknown_host);
} else if (error instanceof ConnectException) {
TipUtil.showError(R.string.network_error);
} else if (error instanceof SocketTimeoutException) {
TipUtil.showError(R.string.error_network_timeout);
} else if (error instanceof HttpException) {
HttpException exception = (HttpException) error;

//获取响应码
int code = exception.code();

handleHttpError(code);

} else if (error instanceof IllegalArgumentException) {
//本地参数错误
TipUtil.showError(R.string.error_parameter);
} else {
String message = error.getLocalizedMessage();
if (StringUtils.isNotBlank(message)) {
message = AppContext.getInstance().getString(R.string.error_unknown_format, message);
} else {
message = AppContext.getInstance().getString(R.string.error_unknown);
}
TipUtil.showError(message);

}

}

/**
* 处理Http错误
* @param code
*/
private static void handleHttpError(int code) {
if (code == 401) {
TipUtil.showError(R.string.error_network_not_auth);

AppContext.getInstance().logout();
} else if (code == 403) {
TipUtil.showError(R.string.error_network_not_permission);
} else if (code == 404) {
TipUtil.showError(R.string.error_network_not_found);
} else if (code == 500) {
TipUtil.showError(R.string.error_network_server);
} else {
TipUtil.showError(R.string.error_unknown);
}
}
}

TipUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.example.superutils.toast.SuperToast;

/**
* 提示工具类
* <p>
* 主要是判断是否有 placeholder,如果有就使用该控件显示提示
* 如果没有就用 Toast 提示
*/
public class TipUtil {

/**
* new
* @param toastResource
*/
public static void showError(int toastResource) {
SuperToast.error(toastResource);
}


public static void showError(String toast) {
SuperToast.error(toast);
}
}

封装到这里

我们可以这样使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
service.sheetDetail("","100000")
.subscribeOn(Schedulers.io())//网络申请放到子线程执行
.observeOn(AndroidSchedulers.mainThread())//网络访问结果官产放在主线程
.subscribe(new HttpObserver<DetailResponse<Sheet>>() {
@Override
public void onSucceeded(DetailResponse<Sheet> data) {
Log.d("烂",data.getData().getTitle());
}

@Override
public boolean onFailed(DetailResponse<Sheet> data, Throwable e) {
Log.e("烂","错误为:" + e.getLocalizedMessage());
return false;
}
});

直接 new HttpObserver

以前是直接 new Observer

Observer -> ObserverAdapter ->HttpObserver

ObserverAdapter :是让不用每次都重写四个方法

HttpObserver:则是让网络请求中的错误都能自动处理

我们现在用网络请求:就可以

直接new HttpObserver 然后必须重写 onSucceed 方法。

在这个方法里面去处理网络请求成功的数据结果。

然后错误会被我们封装的自动处理。

如果想自己查看和处理错误。

就重写 onFailed 方法。在这里可以打印查看错误问题。

return false 就还是会自动处理。

return true 就会放弃自动处理,就需要我们在 onFailed 里面写好对应的异常错误处理对策。

网络加载提示封装

就是在做一些网络操作的时候,会显示加载。

不让用户做其他操作。

HttpObserver 新添

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public abstract class HttpObserver<T> extends ObserverAdapter<T>{
private boolean isShowLoading;
......
public HttpObserver(BaseLogicActivity activity,boolean isShowLoading) {
super();
this.activity = activity;
this.isShowLoading = isShowLoading;
}

public HttpObserver(BaseLogicFragment fragment) {
super();
this.activity = (BaseLogicActivity) fragment.getActivity();
this.isShowLoading = true;
}

@Override
public void onSubscribe(Disposable d) {
super.onSubscribe(d);
if (isShowLoading){
//显示加载对话框
activity.showLoading();
}
}
/**
* 请求结束,成功失败都会调用(调用前调用),使用在这里隐藏加载提示
*/
public void onEnd() {
if (isShowLoading){
//显示加载对话框
activity.hideLoading();
}
}
......
}

然后,如果我们想在进行网络请求的时候,显示加载框。

就要传入 activity 和 true

1
2
3
4
5
6
7
8
9
service.sheetDetail("","100000")
.subscribeOn(Schedulers.io())//网络申请放到子线程执行
.observeOn(AndroidSchedulers.mainThread())//网络访问结果官产放在主线程
.subscribe(new HttpObserver<DetailResponse<Sheet>>(getHostActivity(),true) {
@Override
public void onSucceeded(DetailResponse<Sheet> data) {
Log.d("烂",data.getData().getTitle());
}
});

就像上面这样

如果传入的 Fragment ,默认是有加载框的。

Repository封装

因为现在请求网路,每次都要自己去制作service,但是这个service应该是单例的。

所以我们可以在封装完善一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import com.example.testandroid.component.ad.model.Ad;
import com.example.testandroid.component.api.DefaultService;
import com.example.testandroid.component.api.NetworkModule;
import com.example.testandroid.model.response.ListResponse;
import com.example.testandroid.utils.Constant;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.schedulers.Schedulers;

/**
* 本项目默认仓库
* 主要是从网络,数据库获取数据
* 目前项目中大部分操作都在这里
* <p>
* 如果项目每个模块之间有明显的区别,例如:有商城,有歌单,那可以放到对应模块的Repository
*/
public class DefaultRepository {

private static DefaultRepository instance;
private final DefaultService service;

public DefaultRepository(){
service = NetworkModule.provideRetrofit(NetworkModule.provideOkHttpClient()).create(DefaultService.class);
}

/**
* 返回当前对象的唯一实例
* @return
*/
public synchronized static DefaultRepository getInstance() {
if (instance == null) instance = new DefaultRepository();
return instance;
}



/**
* 广告列表
*
* @return
*/
public Observable<ListResponse<Ad>> ads(int position) {
return service.ads(position)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}

/**
* 首页banner界面广告
*
* @return
*/
public Observable<ListResponse<Ad>> bannerAd() {
return ads(Constant.VALUE0);
}
}

然后用的时候直接:

1
2
3
4
5
6
7
8
9
10
11
12
Observable<ListResponse<Ad>> ads = DefaultRepository.getInstance().bannerAd();

ads
.subscribe(new HttpObserver<ListResponse<Ad>>() {
@Override
public void onSucceeded(ListResponse<Ad> data) {
//添加轮播图
datum.add(new BannerData(
data.getData().getData()
));
}
});

这时候有一个问题,就是内存泄露:RxJava会持有这个Activity。

我们用一个框架来解决。

解决网络请求相关内存泄露

1
2
3
//自动释放RxJava相关资源
//https://github.com/uber/AutoDispose
implementation "com.uber.autodispose2:autodispose-androidx-lifecycle:2.1.1"

然后在申请的时候加一句:

1
2
3
4
5
6
7
8
9
10
11
12
13
Observable<ListResponse<Ad>> ads = DefaultRepository.getInstance().bannerAd();

ads
.to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(new HttpObserver<ListResponse<Ad>>() {
@Override
public void onSucceeded(ListResponse<Ad> data) {
//添加轮播图
datum.add(new BannerData(
data.getData().getData()
));
}
});

就可以了

封装到这里,我们的网络请求就只用:

  1. service里面写地址
  2. 直接repository写方法
  3. 外面调repository

五,总结

未命名文件 (1)