版权声明:天际流火叩响大地之门,岁月星辰刻画沧桑年轮! https://blog.csdn.net/ytfunnysite/article/details/82387975
根据前辈经验,本文依然采用金山词霸公用api进行测试
写在前边:
依赖:
implementation 'io.reactivex.rxjava2:rxjava:2.2.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'com.amitshekhar.android:rx2-android-networking:1.0.0'
//支持把json解析成Java对象
implementation 'com.squareup.okhttp3:okhttp:3.11.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
implementation 'org.greenrobot:eventbus:3.1.1'
implementation 'com.android.support:multidex:1.0.3'
自定义Gradle配置
config.buildgradle
ext.versions = [
// 本地仓库版本
compileSdkVersion: 28,
buildToolsVersion: "28.0.0 rc1",
minSdkVersion : 15,
targetSdkVersion : 28,
versionCode : 1,
versionName : "1.0",
supportVersion : "27.3.0"
]
// app 相关信息总配置
ext.appConfig = [
applicationId : "com.app.ytf.netdemo",
LOCALHOST_DEBUG : "\"http://fy.iciba.com/\"",
LOCALHOST_RELEASE: "\"http://fy.iciba.com/\"",
DEBUGABLE : true,
// GETUI_APP_ID : "PhmRY1Uy8A9VrK2L2H8Qz3",
GETUI_APP_KEY : "2pjtQwS6Oghremzsy8vVZE4CSLip6egajMYuUx8Vo8WvayD21ttphi5fuYiGjoaM",
// GETUI_APP_SECRET : "ZmO60vhutt6NxMauNZJjl8",
]
ext.channel = [
Baidu : "Baidu",
Xiaomi : "Xiaomi",
Huawei : "huawei",
Vivo : "vivo",
Oppo : "oppo",
Tencent: "tencent",
Server : "server"
]
gradle.properties配置签名信息
org.gradle.parallel=true
KEY_ALIAS =netdemo
KEY_PASSWORD=123456
STORE_FILE= C:/Users/ytf/netdemo.jks
STORE_PASSWORD=123456
app下build.gradle引入依赖:
apply plugin: 'com.android.application'
apply from:"$rootDir/config.gradle"
android {
signingConfigs {
debug {
// keyAlias KEY_ALIAS
// keyPassword KEY_PASSWORD
// storeFile file(STORE_FILE)
// storePassword STORE_PASSWORD
}
release {
keyAlias KEY_ALIAS
keyPassword KEY_PASSWORD
storeFile file(STORE_FILE)
storePassword STORE_PASSWORD
}
}
compileSdkVersion versions.compileSdkVersion
buildToolsVersion versions.buildToolsVersion
defaultConfig {
applicationId appConfig.applicationId
minSdkVersion versions.minSdkVersion
targetSdkVersion versions.targetSdkVersion
versionCode versions.versionCode
versionName versions.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
buildConfigField "String", "LOCALHOST", appConfig.LOCALHOST_RELEASE
minifyEnabled false
debuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// debuggable appConfig.DEBUGABLE
signingConfig signingConfigs.release
manifestPlaceholders=[
GETUI_APP_KEY : appConfig.GETUI_APP_KEY
]
}
debug {
buildConfigField "String", "LOCALHOST", appConfig.LOCALHOST_DEBUG
minifyEnabled false
debuggable true
signingConfig signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// debuggable appConfig.DEBUGABLE
manifestPlaceholders=[
GETUI_APP_KEY : appConfig.GETUI_APP_KEY
]
}
}
productFlavors {
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
只用retrofit2进行网络请求
实体类,用于接受返回数据
先来看不用RxJava2的情况
/**
* @author: yangtianfu
* @time: 2018/9/3 11:04
* @more: https://blog.csdn.net/ytfunnysite
* @Describe
*/
public class Translation {
private int status;
private content content;
private static class content {
private String from;
private String to;
private String vendor;
private String out;
private int errNo;
}
//定义 输出返回数据 的方法
public String show() {
System.out.println(status+"--"+content.from+"--"+content.to+"--"
+content.vendor+"--"+content.out+"--"+content.errNo);
return content.out;
}
}
封装网络请求接口
import com.app.ytf.netdemo.model.Translation;
import io.reactivex.Observable;
import retrofit2.http.GET;
/**
* @author: yangtianfu
* @time: 2018/9/3 16:36
* @more: https://blog.csdn.net/ytfunnysite
* @Describe 注解里传入 网络请求 的部分URL地址
* Retrofit把网络请求的URL分成了两部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里
* 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略
* 采用Observable<...>接口
* getCall()是接受网络请求数据的方法
*/
public interface GetRequest {
@GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20world")
Observable<Translation> getCall();
}
Retrofit2的实现(使用EventBus更新UI)
public void request() {
//步骤4:创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://fy.iciba.com/") // 设置 网络请求 Url
.addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖)
.build();
// 步骤5:创建 网络请求接口 的实例
GetRequest_Interface request = retrofit.create(GetRequest_Interface.class);
//对 发送请求 进行封装
Call<Translation> call = request.getCall();
//步骤6:发送网络请求(异步)
call.enqueue(new Callback<Translation>() {
//请求成功时候的回调
@Override
public void onResponse(Call<Translation> call, Response<Translation> response) {
//请求处理,输出结果
response.body().show();
EventBus.getDefault().post(response);
}
//请求失败时候的回调
@Override
public void onFailure(Call<Translation> call, Throwable throwable) {
System.out.println("连接失败");
}
});
}
此处我们采用了EventBus来更新UI,传递请求结果至主线程,要记得注销
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
EventBus.getDefault().register(this);
}
......
@Subscribe(threadMode = ThreadMode.MAIN)
public void setRestult(Response<Translation> response) {
tvContent.setText(response.body().show());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
结合RxJava2实现网络请求轮询(无条件)
场景:采用Get方法对 金山词霸API 按规定时间 重复发送网络请求,从而模拟 轮询 需求实现
实体类和接口同上不变,直接看处理方式:
/**
* 无条件轮询发送网络请求
* 参数1 = 第1次延迟时间;
* 参数2 = 间隔时间数字;
* 参数3 = 时间单位;
* 该例子发送的事件特点:延迟2s后发送事件,每隔1秒产生1个数字(从0开始递增1,无限个)
* 步骤2:每次发送数字前发送1次网络请求(doOnNext()在执行Next事件前调用)
* 即每隔1秒产生1个数字前,就发送1次网络请求,从而实现轮询需求
*/
private void getByRxJava() {
// 步骤1:采用interval()延迟发送
Observable.interval(2, 1, TimeUnit.SECONDS)
// 步骤2:每次发送数字前发送1次网络请求(doOnNext()在执行Next事件前调用)
// 即每隔1秒产生1个数字前,就发送1次网络请求,从而实现轮询需求
.doOnNext(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
Log.d(TAG, "第 " + aLong + " 次轮询");
// 步骤3:通过Retrofit发送网络请求
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.LOCALHOST)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
// 创建 网络请求接口 的实例
GetRequest getRequest = retrofit.create(GetRequest.class);
// c. 采用Observable<...>形式 对 网络请求 进行封装
Observable<Translation> observable = getRequest.getCall();
// d. 通过线程切换发送网络请求
observable.subscribeOn(Schedulers.io()) // 切换到IO线程进行网络请求
.observeOn(AndroidSchedulers.mainThread()) // 切换回到主线程 处理请求结果
.subscribe(new Observer<Translation>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Translation translation) {
String result = translation.show();
tvContent.setText(result);
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
});
}
}).subscribe(new Observer<Long>() {
@Override
public void onSubscribe(Disposable d) {
Log.e(TAG, "执行doOnNext->onSubscribe方法!");
}
@Override
public void onNext(Long aLong) {
Log.e(TAG, "网络初始化完成!");
}
@Override
public void onError(Throwable e) {
Log.e(TAG, "网络初始化失败!" + e.toString());
}
@Override
public void onComplete() {
Log.e(TAG, "执行doOnNext->onComplete方法!");
}
});
}
结合RxJava2实现网络请求轮询(有条件)
基本操作依然同上,加入轮询条件变量 int i=0;
/**
* @author: yangtianfu
* @time: 2018/9/4 16:26
* @more: https://blog.csdn.net/ytfunnysite
* @Describe 有条件的网络轮询,定义i为4的时候停止轮询
*/
private void getByRxJavaWithConditional() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.LOCALHOST)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
GetRequest getRequest = retrofit.create(GetRequest.class);
Observable<Translation> call = getRequest.getCall();
call.repeatWhen(new Function<Observable<Object>, ObservableSource<?>>() {
// 在Function函数中,必须对输入的 Observable<Object>进行处理,此处使用flatMap操作符接收上游的数据
@Override
public ObservableSource<?> apply(Observable<Object> objectObservable){
// 将原始 Observable 停止发送事件的标识(Complete() / Error())转换成1个 Object 类型数据传递给1个新被观察者(Observable)
// 以此决定是否重新订阅 & 发送原来的 Observable,即轮询 // 此处有2种情况:
// 1. 若返回1个Complete() / Error()事件,则不重新订阅 & 发送原来的 Observable,即轮询结束
// 2. 若返回其余事件,则重新订阅 & 发送原来的 Observable,即继续轮询
return objectObservable.flatMap(new Function<Object, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(Object o) {
// 加入判断条件:当轮询次数 = 5次后,就停止轮询
if (i > 3) {
// 此处选择发送onError事件以结束轮询,因为可触发下游观察者的onError()方法回调
Observable.error(new Throwable("轮询结束"));
}
// 若轮询次数<4次,则发送1Next事件以继续轮询
// 注:此处加入了delay操作符,作用 = 延迟一段时间发送(此处设置 = 2s)
return Observable.just(1).delay(2000,TimeUnit.SECONDS);
}
});
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Translation>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Translation translation) {
// e.接收服务器返回的数据
String result = translation.show();
tvContent.setText(result);
i++;
Log.e(TAG, "onNext: 第"+i+"次轮询" );
}
@Override
public void onError(Throwable e) {
// 获取轮询结束信息
Log.d(TAG, e.toString());
}
@Override
public void onComplete() {
}
});
}
结合RxJava2网络请求嵌套回调(FlatMap)
场景:在第1个网络请求成功后,继续再进行一次网络请求
如 先进行 用户注册 的网络请求, 待注册成功后回再继续发送 用户登录 的网络请求
结合 RxJava2中的变换操作符FlatMap()实现嵌套网络请求,采用Get方法对 金山词霸API 发送网络请求
即先翻译 Register(注册),再翻译 Login(登录)
两个网络请求对应的实体类
public class Translation {
private int status;
private content content;
private static class content {
private String from;
private String to;
private String vendor;
private String out;
private int errNo;
}
//定义 输出返回数据 的方法
public String show() {
System.out.println(status+"--"+content.from+"--"+content.to+"--"
+content.vendor+"--"+content.out+"--"+content.errNo);
return content.out;
}
}
public class Translation2 {
private int status;
private content content;
private static class content {
private String from;
private String to;
private String vendor;
private String out;
private int errNo;
}
//定义 输出返回数据 的方法
public String show() {
System.out.println(status+"--"+content.from+"--"+content.to+"--"
+content.vendor+"--"+content.out+"--"+content.errNo);
return content.out;
}
}
接口封装(注册接口和登录接口)
/**
* @author: yangtianfu
* @time: 2018/9/4 17:19
* @more: https://blog.csdn.net/ytfunnysite
* @Describe注解里传入 网络请求 的部分URL地址
* Retrofit把网络请求的URL分成了两部分:一部分放在Retrofit对象里,另一部分放在网络请求接口里
* 如果接口里的url是一个完整的网址,那么放在Retrofit对象里的URL可以忽略
* 采用Observable<...>接口 // getCall()是接受网络请求数据的方法
*/
public interface GetRequest_Interface {
// 网络请求1
@GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20register")
Observable<Translation> getCall();
// 网络请求2
@GET("ajax.php?a=fy&f=auto&t=auto&w=hi%20login")
Observable<Translation2> getCall_2();
}
使用flatMap进行嵌套请求
/**
* @author: yangtianfu
* @time: 2018/9/4 17:17
* @more: https://blog.csdn.net/ytfunnysite
* @Describe 通过 公共的金山词霸API 来模拟 “注册 - 登录”嵌套网络请求
* 即先翻译 Register(注册),再翻译 Login(登录)
*/
private void getByRxJavaInFlatMap() {
// 步骤1:创建Retrofit对象
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.LOCALHOST)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
// 步骤2:创建 网络请求接口 的实例
GetRequest_Interface getRequest_interface=retrofit.create(GetRequest_Interface.class);
// 步骤3:采用Observable<...>形式 对 2个网络请求 进行封装
observable1= getRequest_interface.getCall();
observable2=getRequest_interface.getCall_2();
observable1.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Translation>() {
@Override
public void accept(Translation translation) throws Exception {
Log.d(TAG, "第1次网络请求成功");
String result=translation.show();
tvContent.setText(result); // 对第1次网络请求返回的结果进行操作 = 显示翻译结果
}
})
//再次切回Io线程进行第二次网络请求
.observeOn(Schedulers.io())
// (新被观察者,同时也是新观察者)切换到IO线程去发起登录请求
// 特别注意:因为flatMap是对初始被观察者作变换,所以对于旧被观察者,它是新观察者,所以通过observeOn切换线程
.flatMap(new Function<Translation, ObservableSource<Translation2>>() {
@Override
public ObservableSource<Translation2> apply(Translation result) throws Exception {
// 将网络请求1转换成网络请求2,即发送网络请求2
return observable2;
}
})
// (初始观察者)切换到主线程 处理网络请求2的结果
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Translation2>() {
@Override
public void accept(Translation2 translation2) throws Exception {
Log.d(TAG, "第2次网络请求成功");
String result2 = translation2.show();
String text = tvContent.getText().toString();
tvContent.setText(text + "-" + result2);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
System.out.println("登录失败");
}
});
}