Android 网络请求 Okhttp3+Retrofit2+Rxjava 框架搭建 实现PostGet 请求, 下载 上传

    xiaoxiao2022-07-05  200

    前言

      话不多说,记录下Android开发中比较重要的东西,网络请求就是其中之一

    框架搭建

    结构图

    1、权限

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

     

    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> T createService(Class<T> 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<T> 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<BaseEntity<Result>> postTest(@Field("name") String name, @Field("password") String password); //Get @GET("/api/food/index") Observable<BaseEntity<Food_library>> getFoot_lib(@Query("num") int num, @Query("page") int page, @Query("time") String time, @Query("token") String token); //下载 @Streaming @GET() Observable<ResponseBody> download(@Url String Url); //上传 @Multipart @POST("/api/log/uploadlog") Observable<BaseEntity> 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<T> implements Observable.Transformer<T, T> { @Override public Observable<T> call(Observable<T> tObservable) { return tObservable .subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(t -> {// 通用错误处理,判断code if (!((BaseEntity<T>) t).getCode().equals("200")) { throw new ApiException(Integer.parseInt(((BaseEntity<T>) t).getCode()), ((BaseEntity<T>) t).getMsg()); } return t; }); } public static <T> DefaultTransformer<T> create() { return new DefaultTransformer<>(); } }

    BaseEntity就是定义在ApiService中的公共BaseEntity 

     

    判断与返回

    public abstract class ApiSubscriber<T> extends Subscriber<T> { 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.<BaseEntity<Foods>>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<InputStream>() { @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.<BaseEntity>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

     

    最新回复(0)