Retrofit +OKHttp简单封装使用
本文只是为了方便自己使用,是根据慕课网上的《Android通用框架设计与完整电商App开发》编写的
引用:
//网络依赖 api 'com.squareup.okio:okio:1.14.1' api 'com.squareup.okhttp3:okhttp:3.10.0' api 'com.squareup.retrofit2:retrofit:2.4.0' api 'com.squareup.retrofit2:converter-scalars:2.4.0'
目录结构:
一、最终调用:建造者模式,书写比较方便,逻辑比较清楚,易于扩展,并且不需要客户知道具体的过程
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
private void testRestClient(){ RestClient.builder() .url("https://news.baidu.com/") .loader(getContext()) .success(new ISuccess() { @Override public void onSuccess(String response) { Toast.makeText(getContext(),response,Toast.LENGTH_LONG).show(); } }) .failure(new IFailure() { @Override public void onFailure() { Toast.makeText(getContext(), "onFailure",Toast.LENGTH_LONG).show(); } }).error(new IError() { @Override public void onError(int code, String msg) { Toast.makeText(getContext(),"onError",Toast.LENGTH_LONG).show(); } }).build() .get(); }
二、Retrofit封装
RestService:接口
public interface RestService { @GET Call get(@Url String url, @QueryMap Map params); @FormUrlEncoded @POST Call post(@Url String url, @FieldMap Map params); @POST Call postRaw(@Url String url, @Body RequestBody body); @FormUrlEncoded @PUT Call put(@Url String url, @FieldMap Map params); @PUT Call putRaw(@Url String url, @Body RequestBody body); @DELETE Call delete(@Url String url, @QueryMap Map params); @Streaming @GET Call download(@Url String url, @QueryMap Map params); @Multipart @POST Call upload(@Url String url, @Part MultipartBody.Part file);}
在这里并没有将Url直接放在注解上,而是作为一个参数传入方法中,这样的话作为框架比较通用
RestCreator
public class RestCreator { private static final class ParamsHolder { public static final WeakHashMap PARAMS = new WeakHashMap<>(); } public static WeakHashMap getParams () { return ParamsHolder.PARAMS; } public static RestService getRestService() { return RestServiceHolder.REST_SERVICE; } private static final class RetrofitHolder { private static final String BASE_URL = (String) Latte.getConfigurations().get(ConfigType.API_HOST.name()); 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 OkHttpHolder { private static final int TIME_OUT = 60; private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder() .connectTimeout(TIME_OUT, TimeUnit.SECONDS) .build(); } private static final class RestServiceHolder { private static final RestService REST_SERVICE = RetrofitHolder.RETROFIT_CLIENT.create(RestService.class); }}
在这里初始化Retrofit,在这里使用单例模式对Retrofit以及OKHttp进行初始化,单例模式的写法有多种,这里采用了静态内部类的方式
单例的特点
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
静态内部类优点:
1.采用静态内部类的方式,作为单例,直接用classLoader(jvm类加载机制)进行处理异步加锁问题,并减少内存消耗
2.懒加载(饿汉式),即延迟加载。
3.线程安全。
RestClient
public class RestClient { private final String URL; private static final WeakHashMap PARAMS = RestCreator.getParams(); private final IRequest REQUEST; private final ISuccess SUCCESS; private final IFailure FAILURE; private final IError ERROR; private final RequestBody BODY; private final LoaderStyle LOAD_STYLE; private final Context CONTEXT; private final File FILE; private final String DOWMLOAD_DIR; private final String EXTENSION; private final String NAME; public RestClient(String url, Map params, IRequest request, ISuccess success, IFailure failure, IError error, RequestBody body, LoaderStyle loaderStyle, Context context, File file, String downloadDir, String extension, String name) { this.URL = url; PARAMS.putAll(params); this.REQUEST = request; this.SUCCESS = success; this.FAILURE = failure; this.ERROR = error; this.BODY = body; this.LOAD_STYLE = loaderStyle; this.CONTEXT = context; this.FILE = file; this.DOWMLOAD_DIR = downloadDir; this.EXTENSION = extension; this.NAME = name; } public static RestClientBuilder builder() { return new RestClientBuilder(); } private void request(HttpMethod method) { final RestService service = RestCreator.getRestService(); Call call = null; if (REQUEST != null) { REQUEST.onRequestStart(); } if (LOAD_STYLE != null) { LatterLoader.showLoading(CONTEXT,LOAD_STYLE); } switch (method) { case GET: call = service.get(URL, PARAMS); break; case POST: call = service.post(URL, PARAMS); break; case POST_RAW: call = service.postRaw(URL,BODY); break; case PUT: call = service.put(URL, PARAMS); break; case PUT_RAW: call = service.putRaw(URL,BODY); break; case DELETE: call = service.delete(URL, PARAMS); break; case UPLOAD: final RequestBody requestBody = RequestBody.create(MediaType.parse(MultipartBody.FORM.toString()),FILE); final MultipartBody.Part body = MultipartBody.Part.createFormData("file",FILE.getName(),requestBody); call = service.upload(URL,body); break; default: break; } if (call != null) { call.enqueue(getRequestCallbacks()); } } private Callback getRequestCallbacks() { return new RequestCallbacks(REQUEST, SUCCESS, FAILURE, ERROR,LOAD_STYLE); } public final void get() { request(HttpMethod.GET); } public final void post() { if (BODY == null) { request(HttpMethod.POST); } else { if (!PARAMS.isEmpty()) { throw new RuntimeException("PARAMS must be empty"); } request(HttpMethod.POST_RAW); } } public final void put() { if (BODY == null) { request(HttpMethod.PUT); } else { if (!PARAMS.isEmpty()) { throw new RuntimeException("PARAMS must be empty"); } request(HttpMethod.PUT_RAW); } } public final void delete() { request(HttpMethod.DELETE); } public final void download(){ new DownloadHandler(URL,REQUEST,SUCCESS,FAILURE,ERROR,DOWMLOAD_DIR,EXTENSION,NAME) .handlerDownload(); }}
具体的使用过程使用的是建造者模式进行构建
有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合,因此上述RestClient既是指挥者,同时也是建造者,其中的get()、post()等public方法就是对外暴露,供客户端使用的,,当然还有buider()方法返回了具体的RestClientBuilder产品
重点注意下request()方法,这个方法就是编写的真正去请求RestService中的接口以及调用RestCallBack的回调方法的逻辑
RestClientBuilder
public class RestClientBuilder { private String mUrl; private static final Map PARAMS = RestCreator.getParams(); private IRequest mIRquest; private ISuccess mISuccess; private IFailure mIFailure; private IError mError; private RequestBody mBody; private LoaderStyle mLoaderStyle; private Context mContext; private File mFIle; private String mDownloadDir; private String mExtension; private String mName; RestClientBuilder() { } public final RestClientBuilder url(String url) { this.mUrl = url; return this; } public final RestClientBuilder params(WeakHashMap params) { PARAMS.putAll(params); return this; } public final RestClientBuilder params(String key,Object value) { PARAMS.put(key,value); return this; } public final RestClientBuilder request(IRequest iRequest){ this.mIRquest = iRequest; return this; } public final RestClientBuilder raw(String raw) { this.mBody = RequestBody.create(MediaType.parse("application/json;charset=UTF-8"), raw); 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 file(File file){ this.mFIle = file; return this; } public final RestClientBuilder file(String file){ this.mFIle = new File(file); return this; } public final RestClientBuilder error(IError iError) { this.mError = iError; return this; } public final RestClientBuilder loader(Context context,LoaderStyle loaderStyle) { this.mLoaderStyle = loaderStyle; this.mContext = context; return this; } public final RestClientBuilder loader(Context context) { this.mLoaderStyle = LoaderStyle.BallClipRotatePulseIndicator; this.mContext = context; return this; } public final RestClientBuilder dir(String dir) { this.mDownloadDir = dir; return this; } public final RestClientBuilder extension(String extension) { this.mExtension = extension; return this; } public final RestClientBuilder name (String name) { this.mName = name; return this; } public final RestClient build(){ return new RestClient(mUrl,PARAMS,mIRquest,mISuccess,mIFailure,mError,mBody,mLoaderStyle,mContext,mFIle,mDownloadDir,mExtension,mName); }}
具体的产品生产方法,类似set方法,以及对外暴露产品的方法build()
HttpMethod相关的方法枚举
public enum HttpMethod { GET, POST, POST_RAW, PUT, PUT_RAW, DELETE, UPLOAD}
三、Retrofit回调callback相关
IRequest
public interface IRequest { void onRequestStart(); void onRequestEnd();}
ISuccess
public interface ISuccess { void onSuccess(String response);}
IFailure
public interface IFailure { void onFailure();}
IError
public interface IError { void onError(int code, String msg);}
RequestCallbacks
public class RequestCallbacks implements Callback { 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 = new Handler(); public RequestCallbacks(IRequest request, ISuccess success, IFailure failure, IError error, LoaderStyle loaderStyle) { this.REQUEST = request; this.SUCCESS = success; this.FAILURE = failure; this.ERROR = error; this.LOADER_STYLE = loaderStyle; } @Override public void onResponse(Call call, Response response) { if (response.isSuccessful()) { if (call.isExecuted()) { if (SUCCESS != null) { SUCCESS.onSuccess(response.body()); } } } else { if (ERROR != null) { ERROR.onError(response.code(), response.message()); } } stopLoading(); } @Override public void onFailure(Call call, Throwable t) { Log.e("response---------------",t.getMessage()); if (FAILURE != null) { FAILURE.onFailure(); } if (REQUEST != null) { REQUEST.onRequestEnd(); } stopLoading(); } private void stopLoading() { if (LOADER_STYLE != null) { HANDLER.postDelayed(new Runnable() { @Override public void run() { LatterLoader.stopLoading(); } },1000); } }}
四、download封装
DownloadHandler
public class DownloadHandler { private final String URL; private static final WeakHashMap PARAMS = RestCreator.getParams(); private final IRequest REQUEST; private final ISuccess SUCCESS; private final IFailure FAILURE; private final IError ERROR; private final String DOWMLOAD_DIR; private final String EXTENSION; private final String NAME; public DownloadHandler(String url, IRequest request, ISuccess success, IFailure failure, IError error, String downloadDir, String extension, String name) { this.URL = url; this.REQUEST = request; this.SUCCESS = success; this.FAILURE = failure; this.ERROR = error; this.DOWMLOAD_DIR = downloadDir; this.EXTENSION = extension; this.NAME = name; } public final void handlerDownload() { if (REQUEST != null){ REQUEST.onRequestStart(); } RestCreator.getRestService().download(URL,PARAMS) .enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { if(response.isSuccessful()) { final ResponseBody body = response.body(); final SaveFileTask task = new SaveFileTask(REQUEST,SUCCESS); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,DOWMLOAD_DIR, EXTENSION, body, NAME);// 这里一定要判断文件是否下载完成 if (task.isCancelled()) { if(REQUEST != null) { REQUEST.onRequestEnd(); } } } else { if (ERROR != null) { ERROR.onError(response.code(),response.message()); } } } @Override public void onFailure(Call call, Throwable t) { if (FAILURE != null) { FAILURE.onFailure(); } } }); }}
上面这是下载的具体使用,这里需要重新启用一个线程去下载,所以用到了AsyncTask
SaveFileTask
public class SaveFileTask extends AsyncTask
具体的下载逻辑在这里进行,doInBackground写的下载的线程,onPostExecute返回的主线程,autoInstallApk()安装apk
FileUtil
public final class FileUtil { //格式化的模板 private static final String TIME_FORMAT = "_yyyyMMdd_HHmmss"; private static final String SDCARD_DIR = Environment.getExternalStorageDirectory().getPath(); //默认本地上传图片目录 public static final String UPLOAD_PHOTO_DIR = Environment.getExternalStorageDirectory().getPath() + "/a_upload_photos/"; //网页缓存地址 public static final String WEB_CACHE_DIR = Environment.getExternalStorageDirectory().getPath() + "/app_web_cache/"; //系统相机目录 public static final String CAMERA_PHOTO_DIR = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath() + "/Camera/"; private static String getTimeFormatName(String timeFormatHeader) { final Date date = new Date(System.currentTimeMillis()); //必须要加上单引号 final SimpleDateFormat dateFormat = new SimpleDateFormat("'" + timeFormatHeader + "'" + TIME_FORMAT, Locale.getDefault()); return dateFormat.format(date); } /** * @param timeFormatHeader 格式化的头(除去时间部分) * @param extension 后缀名 * @return 返回时间格式化后的文件名 */ public static String getFileNameByTime(String timeFormatHeader, String extension) { return getTimeFormatName(timeFormatHeader) + "." + extension; } @SuppressWarnings("ResultOfMethodCallIgnored") private static File createDir(String sdcardDirName) { //拼接成SD卡中完整的dir final String dir = SDCARD_DIR + "/" + sdcardDirName + "/"; final File fileDir = new File(dir); if (!fileDir.exists()) { fileDir.mkdirs(); } return fileDir; } @SuppressWarnings("ResultOfMethodCallIgnored") public static File createFile(String sdcardDirName, String fileName) { return new File(createDir(sdcardDirName), fileName); } private static File createFileByTime(String sdcardDirName, String timeFormatHeader, String extension) { final String fileName = getFileNameByTime(timeFormatHeader, extension); return createFile(sdcardDirName, fileName); } //获取文件的MIME public static String getMimeType(String filePath) { final String extension = getExtension(filePath); return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); } //获取文件的后缀名 public static String getExtension(String filePath) { String suffix = ""; final File file = new File(filePath); final String name = file.getName(); final int idx = name.lastIndexOf('.'); if (idx > 0) { suffix = name.substring(idx + 1); } return suffix; } /** * 保存Bitmap到SD卡中 * * @param dir 目录名,只需要写自己的相对目录名即可 * @param compress 压缩比例 100是不压缩,值约小压缩率越高 * @return 返回该文件 */ public static File saveBitmap(Bitmap mBitmap, String dir, int compress) { final String sdStatus = Environment.getExternalStorageState(); // 检测sd是否可用 if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) { return null; } FileOutputStream fos = null; BufferedOutputStream bos = null; File fileName = createFileByTime(dir, "DOWN_LOAD", "jpg"); try { fos = new FileOutputStream(fileName); bos = new BufferedOutputStream(fos); mBitmap.compress(Bitmap.CompressFormat.JPEG, compress, bos);// 把数据写入文件 } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { if (bos != null) { bos.flush(); } if (bos != null) { bos.close(); } //关闭流 if (fos != null) { fos.flush(); } if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } refreshDCIM(); return fileName; } public static File writeToDisk(InputStream is, String dir, String name) { final File file = FileUtil.createFile(dir, name); BufferedInputStream bis = null; FileOutputStream fos = null; BufferedOutputStream bos = null; try { bis = new BufferedInputStream(is); fos = new FileOutputStream(file); bos = new BufferedOutputStream(fos); byte data[] = new byte[1024 * 4]; int count; while ((count = bis.read(data)) != -1) { bos.write(data, 0, count); } bos.flush(); fos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bos != null) { bos.close(); } if (fos != null) { fos.close(); } if (bis != null) { bis.close(); } is.close(); } catch (IOException e) { e.printStackTrace(); } } return file; } public static File writeToDisk(InputStream is, String dir, String prefix, String extension) { final File file = FileUtil.createFileByTime(dir, prefix, extension); BufferedInputStream bis = null; FileOutputStream fos = null; BufferedOutputStream bos = null; try { bis = new BufferedInputStream(is); fos = new FileOutputStream(file); bos = new BufferedOutputStream(fos); byte data[] = new byte[1024 * 4]; int count; while ((count = bis.read(data)) != -1) { bos.write(data, 0, count); } bos.flush(); fos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (bos != null) { bos.close(); } if (fos != null) { fos.close(); } if (bis != null) { bis.close(); } is.close(); } catch (IOException e) { e.printStackTrace(); } } return file; } /** * 通知系统刷新系统相册,使照片展现出来 */ private static void refreshDCIM() { if (Build.VERSION.SDK_INT >= 19) { //兼容android4.4版本,只扫描存放照片的目录 MediaScannerConnection.scanFile(Latte.getApplicationContext(), new String[]{Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath()}, null, null); } else { //扫描整个SD卡来更新系统图库,当文件很多时用户体验不佳,且不适合4.4以上版本 Latte.getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory()))); } } /** * 读取raw目录中的文件,并返回为字符串 */ public static String getRawFile(int id) { final InputStream is = Latte.getApplicationContext().getResources().openRawResource(id); final BufferedInputStream bis = new BufferedInputStream(is); final InputStreamReader isr = new InputStreamReader(bis); final BufferedReader br = new BufferedReader(isr); final StringBuilder stringBuilder = new StringBuilder(); String str; try { while ((str = br.readLine()) != null) { stringBuilder.append(str); } } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); isr.close(); bis.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } } return stringBuilder.toString(); } public static void setIconFont(String path, TextView textView) { final Typeface typeface = Typeface.createFromAsset(Latte.getApplicationContext().getAssets(), path); textView.setTypeface(typeface); } /** * 读取assets目录下的文件,并返回字符串 */ public static String getAssetsFile(String name) { InputStream is = null; BufferedInputStream bis = null; InputStreamReader isr = null; BufferedReader br = null; StringBuilder stringBuilder = null; final AssetManager assetManager = Latte.getApplicationContext().getAssets(); try { is = assetManager.open(name); bis = new BufferedInputStream(is); isr = new InputStreamReader(bis); br = new BufferedReader(isr); stringBuilder = new StringBuilder(); String str; while ((str = br.readLine()) != null) { stringBuilder.append(str); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } if (isr != null) { isr.close(); } if (bis != null) { bis.close(); } if (is != null) { is.close(); } assetManager.close(); } catch (IOException e) { e.printStackTrace(); } } if (stringBuilder != null) { return stringBuilder.toString(); } else { return null; } } public static String getRealFilePath(final Context context, final Uri uri) { if (null == uri) return null; final String scheme = uri.getScheme(); String data = null; if (scheme == null) data = uri.getPath(); else if (ContentResolver.SCHEME_FILE.equals(scheme)) { data = uri.getPath(); } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { final Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null); if (null != cursor) { if (cursor.moveToFirst()) { final int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); if (index > -1) { data = cursor.getString(index); } } cursor.close(); } } return data; }}
更多相关文章
- 一款常用的 Squid 日志分析工具
- GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
- RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
- Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
- android退出系统的绝杀方法
- Android(安卓)EditText修改显示方式
- Android(安卓)JNI实例代码(一)
- Android中级篇之区分系统程序和安装程序
- android Service理解