网络请求框架基于Retrofit2构建,力求为开发者提供简洁、高效的函数调用接口,轻松地发送RESTful类型请求。 此框架采用建造者模式(Builder Pattern),具有完善的回调(Interface)。 程序代码在com.qilu.core.net包下。
若要使用Retrofit2,必须依照官方文档构建一个接口(Interface),这个接口中包含App将使用的一切有关网络请求的方法。 所有方法的返回值类型为retrofit2.Call,一个由Retrofit2提供的回调。
package com.qilu.core.net; import java.util.List; import java.util.WeakHashMap; import okhttp3.MultipartBody; import okhttp3.RequestBody; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Header; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; import retrofit2.http.PartMap; import retrofit2.http.QueryMap; import retrofit2.http.Url; public interface RestService { @GET Call<String> get(@Url String url, @QueryMap WeakHashMap<String, String> params); @GET Call<String> getWithToken(@Header("Authorization") String token, @Url String url, @QueryMap WeakHashMap<String, String> params); @GET Call<String> getNoParams(@Url String url); @GET Call<String> getNoParamsWithToken(@Header("Authorization") String token, @Url String url); @Multipart @POST Call<String> postRaw(@Url String url, @PartMap WeakHashMap<String, RequestBody> params); @Multipart @POST Call<String> postRawWithToken(@Header("Authorization") String token, @Url String url, @PartMap WeakHashMap<String, RequestBody> params); @Multipart @POST Call<String> upload(@Url String url, @Part MultipartBody.Part file); @Multipart @POST Call<String> uploadWithToken(@Header("Authorization") String token, @Url String url, @Part MultipartBody.Part file); @Multipart @POST Call<String> postWithFiles(@Url String url, @PartMap WeakHashMap<String, RequestBody> params, @Part List<MultipartBody.Part> parts); @Multipart @POST Call<String> postWithFilesWithToken(@Header("Authorization") String token, @Url String url, @PartMap WeakHashMap<String, RequestBody> params, @Part List<MultipartBody.Part> parts); }创建枚举类型HttpMethod,方便判断RESTful请求类型
package com.qilu.core.net; public enum HttpMethod { GET, GET_NO_PARAMS, POST_RAW, POST_WITH_FILES, UPLOAD, GET_WITH_TOKEN, GET_NO_PARAMS_WITH_TOKEN, POST_RAW_WITH_TOKEN, POST_WITH_FILES_WITH_TOKEN, UPLOAD_WITH_TOKEN, }RestCreator用于生成全局唯一的RESTful请求服务的实例,相关的配置信息包含超时时间、主机地址等。
package com.qilu.core.net; import com.qilu.core.app.ConfigKeys; import com.qilu.core.app.Qilu; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.converter.scalars.ScalarsConverterFactory; public class RestCreator { public static RestService getRestService() { return RestServiceHolder.REST_SERVICE; } private static final class OKHttpHolder{ private static final int TiME_OUT=600; private static final OkHttpClient.Builder BUILDER=new OkHttpClient.Builder(); private static final OkHttpClient OK_HTTP_CLIENT= BUILDER .connectTimeout(TiME_OUT,TimeUnit.SECONDS) .readTimeout(TiME_OUT,TimeUnit.SECONDS) .writeTimeout(TiME_OUT,TimeUnit.SECONDS) .build(); } private static final class RetrofitHolder { private static final String BASE_URL = Qilu.getConfiguration(ConfigKeys.API_HOST); private static final Retrofit RETROFIT_CLIENT = new Retrofit.Builder() .baseUrl(BASE_URL) .client(OKHttpHolder.OK_HTTP_CLIENT) .addConverterFactory(ScalarsConverterFactory.create()) .build(); } private static final class RestServiceHolder{ private static final RestService REST_SERVICE = RetrofitHolder.RETROFIT_CLIENT.create(RestService.class); } }请求出错
package com.qilu.core.net.callback; public interface IError { void onError(int code, String msg); }请求失败
package com.qilu.core.net.callback; public interface IFailure { void onFailure(); }请求开始/结束(弹出“加载圈”)
package com.qilu.core.net.callback; public interface IRequest { void onRequestStart(); void onRequestEnd(); }请求成功
package com.qilu.core.net.callback; public interface ISuccess { void onSuccess(String response); }所有以上接口的管理器 RequestCallbacks实现以上部分接口,完成具体的回调工作。
package com.qilu.core.net.callback; import android.os.Handler; import com.qilu.core.app.ConfigKeys; import com.qilu.core.app.Qilu; import com.qilu.core.ui.loader.LoaderStyle; import com.qilu.core.ui.loader.QiluLoader; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; public class RequestCallbacks implements Callback<String> { private final IRequest REQUEST; private final ISuccess SUCCESS; private final IFailure FAILURE; private final IError ERROR; private final LoaderStyle LOADER_STYLE; private static final Handler HANDLER =Qilu.getHandler(); //用static声明Handler,可以防止内存泄漏。 public RequestCallbacks(IRequest request, ISuccess success, IFailure failure, IError error, LoaderStyle loader_style) { REQUEST = request; SUCCESS = success; FAILURE = failure; ERROR = error; LOADER_STYLE = loader_style; } @Override public void onResponse(Call<String> call, Response<String> response) { if (response.isSuccessful()) { if (call.isExecuted()) { if (SUCCESS != null) { SUCCESS.onSuccess(response.body()); } } } else { if (ERROR != null) { ERROR.onError(response.code(), response.message()); } } onRequestFinish(); } @Override public void onFailure(Call<String> call, Throwable t) { if (FAILURE != null) { FAILURE.onFailure(); } if (REQUEST != null) { REQUEST.onRequestEnd(); } onRequestFinish(); } private void onRequestFinish() { final long delayed = Qilu.getConfiguration(ConfigKeys.LOADER_DELAYED); if (LOADER_STYLE != null) { HANDLER.postDelayed(QiluLoader::stopLoading, delayed); } } }类RestClientBuilder的角色是“建造者模式”中的“建造者”,用于构建具体的RESTful请求客户端(类RestClient)。
package com.qilu.core.net; import android.content.Context; import com.qilu.core.net.callback.IError; import com.qilu.core.net.callback.IFailure; import com.qilu.core.net.callback.IRequest; import com.qilu.core.net.callback.ISuccess; import com.qilu.core.ui.loader.LoaderStyle; import java.util.WeakHashMap; public final class RestClientBuilder { private final WeakHashMap<String, String> PARAMS = new WeakHashMap<>(); private String mUrl = null; private IRequest mIRequest = null; private ISuccess mISuccess = null; private IFailure mIFailure = null; private IError mIError = null; private Context mContext = null; private LoaderStyle mLoaderStyle = null; // upload private String mFile = null; private WeakHashMap<String, String> mFiles = new WeakHashMap<>(); RestClientBuilder() { } public final RestClientBuilder url(String url) { this.mUrl = url; return this; } public final RestClientBuilder params(WeakHashMap<String, String> params) { PARAMS.putAll(params); return this; } public final RestClientBuilder params(String key, String value) { PARAMS.put(key, value); return this; } public final RestClientBuilder file(String file) { this.mFile = file; return this; } public final RestClientBuilder file(String key, String val) { this.mFiles.put(key, val); return this; } public final RestClientBuilder onRequest(IRequest iRequest) { this.mIRequest = iRequest; return this; } public final RestClientBuilder success(ISuccess iSuccess) { this.mISuccess = iSuccess; return this; } public final RestClientBuilder failure(IFailure iFailure) { this.mIFailure = iFailure; return this; } public final RestClientBuilder error(IError iError) { this.mIError = iError; return this; } public final RestClientBuilder loader(Context context, LoaderStyle style) { this.mContext = context; this.mLoaderStyle = style; return this; } public final RestClientBuilder loader(Context context) { this.mContext = context; this.mLoaderStyle = LoaderStyle.BallClipRotatePulseIndicator; return this; } public final RestClient build() { return new RestClient(mUrl, PARAMS, mIRequest, mISuccess, mIFailure, mIError, mFiles, mFile, mContext, mLoaderStyle); } }发送RESTful请求的具体实现类。
package com.qilu.core.net; import android.content.Context; import com.qilu.core.net.callback.IError; import com.qilu.core.net.callback.IFailure; import com.qilu.core.net.callback.IRequest; import com.qilu.core.net.callback.ISuccess; import com.qilu.core.net.callback.RequestCallbacks; import com.qilu.core.ui.loader.LoaderStyle; import com.qilu.core.ui.loader.QiluLoader; import com.qilu.core.util.storage.QiluPreference; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.WeakHashMap; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import retrofit2.Call; import retrofit2.Callback; public final class RestClient { private final WeakHashMap<String, String> PARAMS; private final String URL; private final IRequest REQUEST; private final ISuccess SUCCESS; private final IFailure FAILURE; private final IError ERROR; private final String FILE; private final WeakHashMap<String, String> FILES; private final Context CONTEXT; private final LoaderStyle LOADER_STYLE; private final String TOKEN; RestClient(String url, WeakHashMap<String, String> params, IRequest request, ISuccess success, IFailure failure, IError error, WeakHashMap<String, String> files, String file, Context context, LoaderStyle loaderStyle) { this.URL = url; this.PARAMS = params; this.REQUEST = request; this.SUCCESS = success; this.FAILURE = failure; this.ERROR = error; // upload this.FILES = files; this.FILE = file; // loader this.CONTEXT = context; this.LOADER_STYLE = loaderStyle; this.TOKEN = "Bearer "+QiluPreference.getCustomAppProfile("token"); } public static RestClientBuilder builder() { return new RestClientBuilder(); } private void request(HttpMethod method) { final RestService service = RestCreator.getRestService(); Call<String> call = null; if (REQUEST != null) { REQUEST.onRequestStart(); } if (LOADER_STYLE != null) { QiluLoader.showLoading(CONTEXT, LOADER_STYLE); } switch (method) { case GET: call = service.get(URL, PARAMS); break; case GET_WITH_TOKEN: call = service.getWithToken(TOKEN, URL, PARAMS); break; case GET_NO_PARAMS: call = service.getNoParams(URL); break; case GET_NO_PARAMS_WITH_TOKEN: call = service.getNoParamsWithToken(TOKEN, URL); break; case POST_RAW: WeakHashMap<String, RequestBody> tempParams1 = new WeakHashMap<>(); for (String key : PARAMS.keySet()) { RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), PARAMS.get(key) == null ? "" : PARAMS.get(key)); tempParams1.put(key, requestBody); } call = service.postRaw(URL, tempParams1); break; case POST_RAW_WITH_TOKEN: WeakHashMap<String, RequestBody> tempParams2 = new WeakHashMap<>(); for (String key : PARAMS.keySet()) { RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), PARAMS.get(key) == null ? "" : PARAMS.get(key)); tempParams2.put(key, requestBody); } call = service.postRawWithToken(TOKEN, URL, tempParams2); break; case POST_WITH_FILES: WeakHashMap<String, RequestBody> requestBodyMap1 = new WeakHashMap<>(); List<MultipartBody.Part> partList1 = new ArrayList<>(); for (WeakHashMap.Entry<String, String> entry : PARAMS.entrySet()) { String key = entry.getKey(); String val = entry.getValue(); RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), val); requestBodyMap1.put(key, body); } for (WeakHashMap.Entry<String, String> entry : FILES.entrySet()) { String key = entry.getKey(); File val = new File(entry.getValue()); RequestBody body = RequestBody.create(MediaType.parse(MultipartBody.FORM.toString()), val); MultipartBody.Part part = MultipartBody.Part.createFormData(key, val.getName(), body); partList1.add(part); } call = service.postWithFiles(URL, requestBodyMap1, partList1); break; case POST_WITH_FILES_WITH_TOKEN: WeakHashMap<String, RequestBody> requestBodyMap2 = new WeakHashMap<>(); List<MultipartBody.Part> partList2 = new ArrayList<>(); for (WeakHashMap.Entry<String, String> entry : PARAMS.entrySet()) { String key = entry.getKey(); String val = entry.getValue(); RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), val); requestBodyMap2.put(key, body); } for (WeakHashMap.Entry<String, String> entry : FILES.entrySet()) { String key = entry.getKey(); File val = new File(entry.getValue()); RequestBody body = RequestBody.create(MediaType.parse(MultipartBody.FORM.toString()), val); MultipartBody.Part part = MultipartBody.Part.createFormData(key, val.getName(), body); partList2.add(part); } call = service.postWithFilesWithToken(TOKEN, URL, requestBodyMap2, partList2); break; case UPLOAD: File file1 = new File(FILE); final RequestBody requestFile1 = RequestBody.create(MediaType.parse(MultipartBody.FORM.toString()), file1); final MultipartBody.Part body1 = MultipartBody.Part.createFormData("file", file1.getName(), requestFile1); call = service.upload(URL,body1); break; case UPLOAD_WITH_TOKEN: File file2 = new File(FILE); final RequestBody requestFile2 = RequestBody.create(MediaType.parse(MultipartBody.FORM.toString()), file2); final MultipartBody.Part body2 = MultipartBody.Part.createFormData("file", file2.getName(), requestFile2); call = service.uploadWithToken(TOKEN, URL,body2); break; default: break; } if (call != null) { call.enqueue(getRequestCallback()); } } private Callback<String> getRequestCallback() { return new RequestCallbacks( REQUEST, SUCCESS, FAILURE, ERROR, LOADER_STYLE ); } public final void get() { request(HttpMethod.GET); } public final void getWithToken() { request(HttpMethod.GET_WITH_TOKEN); } public final void getNoParams() { request(HttpMethod.GET_NO_PARAMS); } public final void getNoParamsWithToken() { request(HttpMethod.GET_NO_PARAMS_WITH_TOKEN); } public final void postRaw() { request(HttpMethod.POST_RAW); } public final void postRawWithToken() { request(HttpMethod.POST_RAW_WITH_TOKEN); } public final void postWithFiles() { request(HttpMethod.POST_WITH_FILES); } public final void postWithFilesWithToken() { request(HttpMethod.POST_WITH_FILES_WITH_TOKEN); } public final void upload() { request(HttpMethod.UPLOAD); } public final void uploadWithToken() { request(HttpMethod.UPLOAD_WITH_TOKEN); } }