前言
话不多说,记录下Android开发中比较重要的东西,网络请求就是其中之一
框架搭建
1、权限
2、导入依赖
implementation 'io.reactivex:rxjava:1.3.8'
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.squareup.okhttp3:okhttp:3.14.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.1'
我在项目中使用的是jdk1.8 中的Lambda表达式,如要使用请在gradle中添加
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
3、建立管理方法类 HttpMethods
public class HttpMethods {
//接口根地址
public static final String BASE_URL = "http://xxxx.com";
//设置超时时间
private static final long DEFAULT_TIMEOUT = 10_000L;
private Retrofit retrofit;
private OkHttpClient client;
///设为默认的 URL
private static class SingletonHolder {
private static final HttpMethods INSTANCE = new HttpMethods(BASE_URL);
}
//私有化构造方法 在这里添加 BASE_URL 的原因的话 是因为 在开发中 可能你用的并不仅仅有你们的后台写的接口还有可能不是用的你们公司域名的接口
//做一个保险起见
public HttpMethods(String BASE_URL) {
//OkHttp的使用
client = new OkHttpClient.Builder()
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
//添加请求头
//.addInterceptor(new HeaderInterceptor())
//添加日志打印拦截器 拓展
.addInterceptor(new LoggerInterceptor("===", true))
.build();
//retrofit 的使用
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
//引用OkHttp
.client(client)
//添加Gson解析
.addConverterFactory(GsonConverterFactory.create())
//添加rxjava
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
//下载 DownloadProgressListener这个监听是你自己定义的,不要搞错了
public HttpMethods(String BASE_URL, DownloadProgressListener listener) {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new DownloadProgressInterceptor(listener))
//连接失败时重试
.retryOnConnectionFailure(true)
//设置超时时间
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
public static HttpMethods getInstance() {
return SingletonHolder.INSTANCE;
}
public T createService(Class clazz) {
return retrofit.create(clazz);
}
}
其中
addConverterFactory(GsonConverterFactory.create())
addCallAdapterFactory(RxJavaCallAdapterFactory.create())
是非常重要的,一个是将服务器传给你的Json 转为对象来跟你的Bean匹配
第二个是决定你返回的对象是不是Observable(Retrofit2+RxJava 都是返回的Observable) 如果不加就是默认的Call
4、OkHttp拓展
请求头 HeaderInterceptor :
/**
* 添加全局header,token等等
*/
public class HeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request()
.newBuilder()
.addHeader("x-fcsc-token", "")
.build();
return chain.proceed(request);
}
}
日志拦截器 LoggerInterceptor :
public class LoggerInterceptor implements Interceptor {
public static final String TAG = "llll_";
private String tag;
private boolean showResponse;
public LoggerInterceptor(String tag, boolean showResponse) {
if (TextUtils.isEmpty(tag)) {
tag = TAG;
}
this.showResponse = showResponse;
this.tag = tag;
}
public LoggerInterceptor(String tag) {
this(tag, false);
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
logForRequest(request);
Response response = chain.proceed(request);
return logForResponse(response);
}
private Response logForResponse(Response response) {
try {
//===>response log
Log.e(TAG,"========response'log=======");
Response.Builder builder = response.newBuilder();
Response clone = builder.build();
Log.e(TAG,"code : " + clone.code());
if (!TextUtils.isEmpty(clone.message()))
Log.e(TAG,"message : " + clone.message());
if (showResponse) {
ResponseBody body = clone.body();
if (body != null) {
MediaType mediaType = body.contentType();
if (mediaType != null) {
Log.e(TAG,"responseBody's contentType : " + mediaType.toString());
if (isText(mediaType)) {
String resp = body.string();
Log.e(TAG,"responseBody's content : " + resp);
body = ResponseBody.create(mediaType, resp);
Log.e(TAG,"========response'log=======end");
return response.newBuilder().body(body).build();
} else {
Log.e(TAG,"responseBody's content : maybe [file part] , too large too print , ignored!");
}
}
}
}
Log.e(TAG,"========response'log=======end");
} catch (Exception e) {
// e.printStackTrace();
}
return response;
}
private void logForRequest(Request request) {
try {
String url = request.url().toString();
Headers headers = request.headers();
Log.e(TAG,"________request'log________");
Log.e(TAG,"method : " + request.method());
Log.e(TAG,"url : " + url);
if (headers != null && headers.size() > 0) {
Log.e(TAG,"headers : " + headers.toString());
}
RequestBody requestBody = request.body();
if (requestBody != null) {
MediaType mediaType = requestBody.contentType();
if (mediaType != null) {
Log.e(TAG,"requestBody's contentType : " + mediaType.toString());
if (isText(mediaType)) {
Log.e(TAG,"requestBody's content : " + bodyToString(request));
} else {
Log.e(TAG,"requestBody's content : maybe [file part] , too large too print , ignored!");
}
}
}
Log.e(TAG,"________request'log________end");
} catch (Exception e) {
// e.printStackTrace();
}
}
private boolean isText(MediaType mediaType) {
if (mediaType.type() != null && mediaType.type().equals("text")) {
return true;
}
if (mediaType.subtype() != null) {
if (mediaType.subtype().equals("json") ||
mediaType.subtype().equals("xml") ||
mediaType.subtype().equals("html") ||
mediaType.subtype().equals("webviewhtml")||
mediaType.subtype().equals("x-www-form-urlencoded")
)
return true;
}
return false;
}
private String bodyToString(final Request request) {
try {
final Request copy = request.newBuilder().build();
final Buffer buffer = new Buffer();
copy.body().writeTo(buffer);
return buffer.readUtf8();
} catch (final IOException e) {
return "显示请求体时出错";
}
}
}
Okhttp下载:
其中下载包括
DownloadProgressInterceptor(进度拦截器)
DownloadProgressListener(进度监听)
DownloadProgressResponseBody(下载进度的响应)
DownloadProgressInterceptor
public class DownloadProgressInterceptor implements Interceptor {
private DownloadProgressListener listener;
public DownloadProgressInterceptor(DownloadProgressListener listener) {
this.listener = listener;
}
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder()
.body(new DownloadProgressResponseBody(originalResponse.body(), listener))
.build();
}
}
DownloadProgressListener
public interface DownloadProgressListener {
void update(long bytesRead, long contentLength, boolean done);
}
DownloadProgressResponseBody
public class DownloadProgressResponseBody extends ResponseBody {
private ResponseBody responseBody;
private DownloadProgressListener progressListener;
private BufferedSource bufferedSource;
public DownloadProgressResponseBody(ResponseBody responseBody,
DownloadProgressListener progressListener) {
this.responseBody = responseBody;
this.progressListener = progressListener;
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
// read() returns the number of bytes read, or -1 if this source is exhausted.
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
if (null != progressListener) {
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
}
return bytesRead;
}
};
}
}
5、ApiService 接口类
在此类中每一个Post get 接口都是要有一个公共的Bean Response响应的就是这个类 在下面判断异常也需要这个类
这个公共Bean 根据自己公司需求来写
public class BaseEntity implements Serializable {
private String code;
private String msg;
private T data;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
接口定义
/**
* 接口定义
*/
public interface ApiService {
//Post
@FormUrlEncoded
@POST("/2.php")
Observable> postTest(@Field("name") String name, @Field("password") String password);
//Get
@GET("/api/food/index")
Observable> getFoot_lib(@Query("num") int num, @Query("page") int page, @Query("time") String time, @Query("token") String token);
//下载
@Streaming
@GET()
Observable download(@Url String Url);
//上传
@Multipart
@POST("/api/log/uploadlog")
Observable postUploadlog(@Query("time") String time, @Query("token") String token, @Part MultipartBody.Part file);
}
6、异常判断
public class ApiException extends IllegalArgumentException {
private int code;
public ApiException(int code, String msg) {
super(msg);
this.code = code;
}
public int getCode() {
return code;
}
}
public class DefaultTransformer implements Observable.Transformer {
@Override
public Observable call(Observable tObservable) {
return tObservable
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(t -> {// 通用错误处理,判断code
if (!((BaseEntity) t).getCode().equals("200")) {
throw new ApiException(Integer.parseInt(((BaseEntity) t).getCode()), ((BaseEntity) t).getMsg());
}
return t;
});
}
public static DefaultTransformer create() {
return new DefaultTransformer<>();
}
}
BaseEntity就是定义在ApiService中的公共BaseEntity
判断与返回
public abstract class ApiSubscriber extends Subscriber {
Context context;
//可以在这个类中添加一个LoadDialog 加载时显示
public ApiSubscriber() {
}
public ApiSubscriber(@NonNull Context context) {
this.context = context;
//实例化load
}
@Override
public void onStart() {
//show load
}
@Override
public void onCompleted() {
//dismiss load
}
/**
* 只要链式调用中抛出了异常都会走这个回调
*/
@Override
public void onError(Throwable e) {
//dismiss load
if (e instanceof ApiException) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
//服务器返回的错误
} else if (e instanceof ConnectException || e instanceof UnknownHostException) {
Toast.makeText(context, "网络异常,请检查网络", Toast.LENGTH_SHORT).show();
} else if (e instanceof TimeoutException || e instanceof SocketTimeoutException) {
Toast.makeText(context, "网络超时,请稍后再试!", Toast.LENGTH_SHORT).show();
} else if (e instanceof JsonSyntaxException) {
//这里数据解析异常 就是说你在绑定接口是 返回的数据跟你 填写的实体类中的属性 不一样
//还有就是 当你访问接口 获取的信息 为null 什么都没有 但是服务器给你的code为200 那就另加判断
Toast.makeText(context, "数据解析异常!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "服务端错误!", Toast.LENGTH_SHORT).show();
}
e.printStackTrace();
}
}
7、使用
Post 和 Get
Subscription subscribe = HttpMethods.getInstance()
.createService(ApiService.class)
.postFoodList(time, APIEncryptionUtils.TokenEncryption(time), "1")
.compose(DefaultTransformer.>create())
.subscribe(foodsBaseEntity -> {
runOnUiThread(() -> {
show.setText(foodsBaseEntity.toString());
});
});
addSubscription(subscribe);
下载
private void ProgressDialog(String Url) {
final ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setTitle("正在下载!");//2.设置标题
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.setCancelable(false);
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.show();//
//更新进度条
DownloadProgressListener listener = (bytesRead, contentLength, done) -> {
//不频繁发送通知,防止通知栏下拉卡顿
int progress = (int) ((bytesRead * 100) / contentLength);
progressDialog.setProgress(progress);
if ((downloadCount == 0) || progress > downloadCount) {
if (progress == 100) {
runOnUiThread(() ->
progressDialog.setTitle("下载完成!")
);
}
}
};
//下载操作开始
Subscription subscribe = new HttpMethods("自己域名", listener).createService(ApiService.class)
.download(Url).subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io()).map(responseBody ->
responseBody.byteStream()
).observeOn(Schedulers.computation()).doOnNext(inputStream -> {
try {
saveFile(inputStream, flie);
} catch (IOException e) {
e.printStackTrace();
}
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber() {
@Override
public void onCompleted() {
//下载完成后
progressDialog.incrementProgressBy(100);
progressDialog.dismiss();
Log.i("Update", flie.getPath());
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Log.i("Log---", "onError");
}
@Override
public void onNext(InputStream inputStream) {
Log.i("Log---", "onNext");
}
});
addSubscription(subscribe);
}
上传
final String Token = APIEncryptionUtils.TokenEncryption(time);
//MediaType 有多种类型
RequestBody requestBody = RequestBody.create(MediaType.parse("text/x-markdown; charset=utf-8"), flie);
Log.i("FilePath", requestBody.contentType().type() + "=====" + fileUploadlog.getName());
MultipartBody.Part body = MultipartBody.Part.createFormData("log", fileUploadlog.getName(), requestBody);
Subscription subscribe = HttpMethods.getInstance()
.createService(ApiService.class)
.postUploadlog(time, Token, body)
.compose(DefaultTransformer.create())
.subscribe(baseEntity -> {
Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show();
});
addSubscription(subscribe);
MediaType.parse 全部 http://www.w3school.com.cn/media/media_mimeref.asp
8、订阅和解除
/**
* 所有rx订阅后,需要调用此方法,用于在detachView时取消订阅
*/
private void addSubscription(Subscription subscribe) {
if (mCompositeSubscription == null)
mCompositeSubscription = new CompositeSubscription();
mCompositeSubscription.add(subscribe);
}
/**
* 取消本页面所有订阅
*/
public void onUnsubscribe() {
if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
mCompositeSubscription.unsubscribe();
}
}
CompositeSubscription可以订阅多个 subscribe,取消订阅时在onDestroy中取消就行了
结束
好了以上就是框架搭建和上传、下载的所有了,这些方法是我以前看了一位大佬了博客,然后根据自己的理解,新加了一些东西。
建议一些新手的话,还是要把Retrofit2、RxJava、Okhttp3 都单独用一下,在自己尝试着封装,总和。这样对于代码的理解会好一点
GitHub: https://github.com/ASheng-Bisheng/Okhttp3-Retrofit2-Rxjava
csdn: https://download.csdn.net/download/bisheng_xiong/11195440