文章目錄
-
- 寫在前邊:
-
- 依賴:
- 自定義Gradle配置
- gradle.properties配置簽名資訊
- app下build.gradle引入依賴:
- 隻用retrofit2進行網絡請求
-
- 實體類,用于接受傳回資料
- 封裝網絡請求接口
- Retrofit2的實作(使用EventBus更新UI)
- 結合RxJava2實作網絡請求輪詢(無條件)
- 結合RxJava2實作網絡請求輪詢(有條件)
- 結合RxJava2網絡請求嵌套回調(FlatMap)
-
- 接口封裝(注冊接口和登入接口)
- 使用flatMap進行嵌套請求
- 網絡請求出錯重連(結合Retrofit)
- 補充:
-
- retrofit封裝
- 線程排程
-
- 日志攔截器
- 請求頭攔截器
- 公共查詢參數攔截器
- 緩存攔截器
- presenter中請求網絡
- acvitity中處理資料
- 全局日志儲存和上傳
- 檢測網絡
- 全局Toast管理
根據前輩經驗,本文依然采用金山詞霸公用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("登入失敗");
}
});
}
網絡請求出錯重連(結合Retrofit)
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);
}
});
}
})
// .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
// // 參數Observable<Throwable>中的泛型 = 上遊操作符抛出的異常,可通過該條件來判斷異常的類型
// // 傳回Observable<?> = 新的被觀察者 Observable(任意類型)
// // 此處有兩種情況:
// // 1. 若 新的被觀察者 Observable發送的事件 = Error事件,那麼 原始Observable則不重新發送事件:
// // 2. 若 新的被觀察者 Observable發送的事件 = Next事件 ,那麼原始的Observable則重新發送事件:
// @Override
// public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
// // 1. 若傳回的Observable發送的事件 = Error事件,則原始的Observable不重新發送事件
// // 該異常錯誤資訊可在觀察者中的onError()中獲得
//
// return Observable.error(new Throwable("retryWhen終止啦"));
// // 2. 若傳回的Observable發送的事件 = Next事件,則原始的Observable重新發送事件(若持續遇到錯誤,則持續重試)
// // return Observable.just(1);
//
// }
// })
.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() {
}
});
補充:
retrofit封裝
package com.thesis.mentor.retrofit_v2;
import com.thesis.mentor.BuildConfig;
import com.thesis.mentor.api_v2.ApiService;
import com.thesis.mentor.retrofit_v2.interceptor.HeaderInterceptor;
import com.thesis.mentor.retrofit_v2.interceptor.LoggingInterceptor;
import java.io.File;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* @autor YangTianFu
* @Date 2019/3/22 17:10
* @Description Retrofit+RxJava聯網的統一管理類
*/
public class RetrofitManager {
private static volatile RetrofitManager mInstance;
private static final long DEFAULT_TIMEOUT = 60L;
private Retrofit retrofit = null;
// private String userAgent = "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36";
public static RetrofitManager getInstance() {
if (mInstance == null) {
synchronized (RetrofitManager.class) {
if (mInstance == null) {
mInstance = new RetrofitManager();
}
}
}
return mInstance;
}
private Retrofit getRetrofit() {
if (retrofit == null) {
synchronized (RetrofitManager.class) {
if (retrofit == null) {
OkHttpClient mClient = new OkHttpClient.Builder()
.addInterceptor(new HeaderInterceptor())
.addInterceptor(new LoggingInterceptor())
.retryOnConnectionFailure(true)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.LOCALHOST_BASEURL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(mClient)
.build();
}
}
}
return retrofit;
}
public ApiService getRequestService() {
return getRetrofit().create(ApiService.class);
}
/**
* Retrofit上傳檔案
*/
public RequestBody getUploadFileRequestBody(String mImagePath) {
File file = new File(mImagePath);
RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file))
.build();
return requestBody;
}
}
線程排程
package com.thesis.mentor.retrofit_v2;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* @autor YangTianFu
* @Date 2019/3/26 10:18
* @Description rxjava線程排程的封裝
*/
public class RxSchedulers {
public static <T> ObservableTransformer<T,T> io_main(){
return new ObservableTransformer<T, T>() {
@Override
public ObservableSource<T> apply(Observable<T> upstream) {
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
日志攔截器
package com.thesis.mentor.retrofit_v2.interceptor;
import android.util.Log;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
/**
* @autor YangTianFu
* @Date 2019/3/22 17:25
* @Description 應用日志攔截器
*/
public class LoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
//這個chain裡面包含了request和response
Request request = chain.request();
//請求發起的時間
long t1 = System.nanoTime();
String method = request.method();
JSONObject jsonObject = new JSONObject();
if ("POST".equals(method) || "PUT".equals(method)){
if (request.body() instanceof FormBody){
FormBody body = (FormBody) request.body();
if (body != null){
for (int i = 0; i < body.size(); i++) {
try {
jsonObject.put(body.name(i),body.encodedValue(i));
}catch (Exception e){
e.printStackTrace();
}
}
}
Log.i("request",String.format("發送請求 %s on %s %nRequestParams:%s%nMethod:%s",
request.url(),chain.connection(),jsonObject.toString(),request.method()));
}else {
Buffer buffer = new Buffer();
RequestBody requestBody = request.body();
if (requestBody != null){
request.body().writeTo(buffer);
String body = buffer.readUtf8();
Log.i("request", String.format("發送請求 %s on %s %nRequestParams:%s%nMethod:%s",
request.url(), chain.connection(), body, request.method()));
}
}
}else {
Log.i("request", String.format("發送請求 %s on %s%nMethod:%s",
request.url(), chain.connection(), request.method()));
}
Response response = chain.proceed(request);
//收到響應的時間
long t2 = System.nanoTime();
ResponseBody responseBody = response.peekBody(1024 * 1024);
Log.i("request",
String.format("Retrofit接收響應: %s %n傳回json:%s %n耗時:%.1fms",
response.request().url(),
responseBody.string(),
(t2 - t1) / 1e6d
));
return response;
}
}
請求頭攔截器
package com.thesis.mentor.retrofit_v2.interceptor;
import com.thesis.mentor.application.MyApplication;
import com.thesis.mentor.uitl_v2.LogUtil;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import static com.thesis.mentor.me.bean.UserInfoBean.getUserInfo;
/**
* @autor YangTianFu
* @Date 2019/3/26 9:17
* @Description 添加請求頭需要攜帶的參數
*/
public class HeaderInterceptor implements Interceptor {
//請求頭資訊
private final String HEADER_CONNECTION = "keep-alive";
String userAgent = "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.87 Safari/537.36";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Log.i(TAG, "HeaderInterceptor: "+request.toString());
LogUtil.i("HeaderInterceptor: "+request.toString());
Request requestBuilder = request.newBuilder()
.addHeader("Connection",HEADER_CONNECTION)
.addHeader("User-Agent",userAgent)
.addHeader("source", "Android")
.addHeader("token", getUserInfo(MyApplication.context).getToken())
.addHeader("uid", getUserInfo(MyApplication.context).getUid())
.method(request.method(),request.body())
.build();
return chain.proceed(requestBuilder);
}
}
公共查詢參數攔截器
package com.thesis.mentor.retrofit_v2.interceptor;
import java.io.IOException;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* @autor YangTianFu
* @Date 2019/3/26 9:22
* @Description 公共查詢參數攔截器
*/
public class CommonQueryParamsInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url().newBuilder()
.addQueryParameter("paramsA", "a")
.addQueryParameter("paramsB", "b")
.build();
return chain.proceed(request.newBuilder().url(url).build());
}
}
緩存攔截器
package com.thesis.mentor.retrofit_v2.interceptor;
import android.util.Log;
import com.thesis.mentor.application.MyApplication;
import com.thesis.mentor.uitl_v2.NetUtils;
import java.io.IOException;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
/**
* @autor YangTianFu
* @Date 2019/3/26 9:26
* @Description 設定緩存的攔截器
*/
public class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetUtils.isNetworkConnected(MyApplication.getApplicationContext1())){
request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
}
Response response = chain.proceed(request);
if (NetUtils.isNetworkConnected(MyApplication.getApplicationContext1())){
String cacheControl = request.cacheControl().toString();
Log.e("net","網絡連接配接正常");
return response.newBuilder().header("Cache-Control",cacheControl)
.removeHeader("Pragma").build();
}else {
Log.e("net","斷網了");
return response.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + "60 * 60 * 24 * 7")
.removeHeader("Pragma").build();
}
}
}
presenter中請求網絡
public class MyStudyPresenter {
private Context context;
private INetCallBackListener listener;
public MyStudyPresenter(Context context, INetCallBackListener listener) {
this.context = context;
this.listener = listener;
}
/**
* 擷取系統時間
*/
public void getAppTime(){
if (getUserInfo(context)!=null){
String utoken=getUserInfo(context).getToken();
String uid=getUserInfo(context).getUid();
if (utoken==null||uid==null){
listener.onFailure("403");
return;
}
}else {
listener.onFailure("403");
return;
}
Observable<AppTime> appTimeObservable = RetrofitManager.getInstance().getRequestService().getAppTime();
appTimeObservable.compose(RxSchedulers.io_main())
.subscribe(new Consumer<AppTime>() {
@Override
public void accept(AppTime appTime) throws Exception {
if (appTime != null){
listener.onSuccess(appTime);
}
}
});
}
public void getJobListPage(String course_id, int pageNum){
Map<String,String> map = new HashMap<>();
String utoken=getUserInfo(context).getToken();
String uid=getUserInfo(context).getUid();
if (utoken==null||uid==null){
return;
}
String url = "";
int student_id = 0;
if (UserInfoBean.getUserInfo(context) != null && UserInfoBean.getUserInfo(context).getStudent() != null) {
student_id = UserInfoBean.getUserInfo(context).getStudent().getId();
}
if (student_id != 0){
map.put("student_id",String.valueOf(student_id));
map.put("course_id",course_id);
map.put("pageNum",String.valueOf(pageNum));
}
Observable<HomeWorkResponse> homeWorkResponseObservable = RetrofitManager.getInstance()
.getRequestService()
.getJobListPage((HashMap<String, String>) map);
homeWorkResponseObservable.compose(RxSchedulers.io_main())
.subscribe(new Consumer<HomeWorkResponse>() {
@Override
public void accept(HomeWorkResponse homeWorkResponse){
if (homeWorkResponse != null){
listener.onSuccess(homeWorkResponse);
}
}
});
}
}
/**
* @autor YangTianFu
* @Date 2019/4/17 16:59
* @Description 網絡請求回調處理接口
*/
public interface INetCallBackListener<T> {
void onSuccess(T response);
void onError(Object error);
void onFailure(String dsc);
}
acvitity中處理資料
public void getAppTime(View view) {
if (myStudyPresenter != null){
myStudyPresenter.getAppTime();
}
}
public void getHomeWorkList(View view) {
if (myStudyPresenter != null){
myStudyPresenter.getJobListPage(courseId,1);
}
}
@Override
public void onSuccess(Object response) {
if (response instanceof AppTime){
tv_time.setText(((AppTime) response).getTime());
}else if (response instanceof HomeWorkResponse){
tv_homework.setText(((HomeWorkResponse) response).getList().toString());
}
}
@Override
public void onError(Object error) {
}
@Override
public void onFailure(String dsc) {
}
全局日志儲存和上傳
package com.thesis.mentor.uitl_v2;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @autor YangTianFu
* @Date 2019/4/12 17:20
* @Description 日志儲存SD卡并上傳伺服器的工具類
*/
public class LogUtil {
private static final String TAG = "LogUtil";
// 是否需要列印bug,在application的onCreate函數裡面初始化
public static boolean isDebug;
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashKZ/log/";
private static final String FILE_NAME = "crash";
private static final String FILE_NAME_SUFFIX = ".txt";
private static Context mContext;
public static void init(Context context) {
mContext = context.getApplicationContext();
}
// 下面四個是預設tag的函數
public static void i(String msg) {
if (isDebug) {
Log.i(TAG, msg);
dumpLogErrorToSDCard(msg);
}
}
public static void d(String msg) {
if (isDebug) {
Log.d(TAG, msg);
}
}
public static void e(String msg) {
if (isDebug) {
Log.e(TAG, msg);
dumpLogErrorToSDCard(msg);
}
}
public static void e(String tag, String msg) {
if (isDebug) {
Log.e(tag, msg);
dumpLogErrorToSDCard(msg);
}
}
public static void v(String msg) {
if (isDebug) {
Log.v(TAG, msg);
}
}
// 下面是傳入自定義tag的函數
public static void i(String tag, String msg) {
if (isDebug) {
Log.i(tag, msg);
dumpLogErrorToSDCard(msg);
}
}
/**
* 截斷輸出日志
* @param msg
*/
public static void eLength(String tag, String msg) {
if (isDebug) {
if (tag == null || tag.length() == 0
|| msg == null || msg.length() == 0) {
return;
}
int segmentSize = 3 * 1024;
long length = msg.length();
if (length <= segmentSize) {// 長度小于等于限制直接列印
Log.e(tag, msg);
} else {
while (msg.length() > segmentSize) {// 循環分段列印日志
String logContent = msg.substring(0, segmentSize);
msg = msg.replace(logContent, "");
Log.e(tag, logContent);
}
Log.e(tag, msg);// 列印剩餘日志
}
}
}
/**
* 分段列印出較長log文本
* @param log 原log文本
* @param
*/
public static void showLogCompletion(String string, String log) {
if (log.length() > 4000) {
for (int i = 0; i < log.length(); i += 4000) {
if (i + 4000 < log.length()) {
Log.i(string + i, log.substring(i, i + 4000));
} else {
Log.i(string + i, log.substring(i, log.length()));
}
}
} else {
Log.i(string, log);
}
}
/**
* @Author:yangtianfu
* @Date:{2019/4/16 16:36}
* @Description 将崩潰日志和自定義錯誤日志寫入SD卡根目錄
*/
public static void dumpLogErrorToSDCard(String msg) {
//如果SD卡不存在或無法使用,則無法把異常資訊寫入SD卡
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (isDebug) {
Log.e(TAG, "sdcard unmounted,skip dump exception");
return;
}
}
long current = System.currentTimeMillis();
String time = new SimpleDateFormat("yyyy-MM-dd HH:MM:SS").format(new Date(current));
File dir = new File(PATH);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(PATH + FILE_NAME + FILE_NAME_SUFFIX);
try {
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file, true)));
pw.println(time);
dumpPhoneInfo(pw);
pw.println(msg);
pw.println();
pw.close();
Log.i(TAG, "日志寫入成功");
} catch (Exception e) {
Log.e(TAG, e.getMessage());
Log.e(TAG, "日志寫入失敗");
}
}
/**
* @Author:yangtianfu
* @Date:{2019/4/16 16:37}
* @Description 擷取目前裝置資訊
*/
private static void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
pw.print("App Version: ");
pw.print(pi.versionName);
pw.print('_');
pw.println(pi.versionCode);
//Android版本号
pw.print("OS Version: ");
pw.print(Build.VERSION.RELEASE);
pw.print("_");
pw.print(Build.VERSION.SDK_INT);
//手機制造商
pw.print("Vendor: ");
pw.print(Build.MANUFACTURER);
//手機型号
pw.print("Model: ");
pw.println(Build.MODEL);
//CPU架構
pw.print("CPU ABI: ");
pw.println(Build.CPU_ABI);
}
}
檢測網絡
package com.thesis.mentor.uitl_v2;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import java.util.Locale;
/**
* @autor YangTianFu
* @Date 2019/3/26 9:26
* @Description 檢測網絡
*/
public class NetUtils {
/**
* 四種網絡類型
* WIFI: wifi
* CMNET:中國移動網際網路
* CMWAP :中國移動夢網
* NONE: 無網絡連接配接
*/
public static enum NetType {
WIFI, CMNET, CMWAP, NONE
}
/**
* 目前網絡是否可用
* @param context
* @return
*/
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo[] info = mgr.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
return false;
}
/**
* 網絡是否連接配接成功
* @param context
* @return
*/
public static boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable();
}
}
return false;
}
/**
* wifi網絡連接配接是否可用
* @param context
* @return
*/
public static boolean isWifiConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWiFiNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (mWiFiNetworkInfo != null) {
return mWiFiNetworkInfo.isAvailable();
}
}
return false;
}
/**
* 移動網絡連接配接是否可用
* @param context
* @return
*/
public static boolean isMobileConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mMobileNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (mMobileNetworkInfo != null) {
return mMobileNetworkInfo.isAvailable();
}
}
return false;
}
/**
* 網絡類型判斷
* @param context
* @return
*/
public static int getConnectedType(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null && mNetworkInfo.isAvailable()) {
return mNetworkInfo.getType();
}
}
return -1;
}
/**
* 網絡類型判斷
*
* @param context
* @return
*/
public static NetType getAPNType(Context context) {
ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo == null) {
return NetType.NONE;
}
int nType = networkInfo.getType();
if (nType == ConnectivityManager.TYPE_MOBILE) {
if (networkInfo.getExtraInfo().toLowerCase(Locale.getDefault()).equals("cmnet")) {
return NetType.CMNET;
} else {
return NetType.CMWAP;
}
} else if (nType == ConnectivityManager.TYPE_WIFI) {
return NetType.WIFI;
}
return NetType.NONE;
}
}
全局Toast管理
package com.thesis.mentor.uitl_v2;
import android.content.Context;
import android.view.Gravity;
import android.widget.Toast;
/**
* @autor YangTianFu
* @Date 2019/3/25 13:47
* @Description 全局Toast統一管理類,避免多個吐司重複彈出
*/
public class ToastUtil {
public static volatile ToastUtil instance;
public static Toast toast = null;
private ToastUtil() {
}
/**
* @Author:yangtianfu
* @Date:{2019/4/16 16:34}
* @Description 雙重驗鎖,保證全局單例模式
*/
public static ToastUtil getInstance() {
if (instance == null) {
synchronized (ToastUtil.class) {
if (instance == null) {
instance = new ToastUtil();
}
}
}
return instance;
}
// 是否顯示吐司的開關,可以在Application中進行設定,預設為true。
public static boolean isShow = true;
/**
* 短時間顯示Toast
*
* @param context
* @param message
*/
public static void show(Context context, CharSequence message) {
if (isShow) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
/**
* 中間吐司
*
* @param title
*/
public static void showDefultToast(Context context, String title) {
if (toast == null) {
toast = Toast.makeText(context, title, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else {
toast.setText(title);
toast.show();
}
}
}