一个基于Retrofit的单文件上传、下载框架
2016-12-31 15:26
465 查看
从事Android开发工作也有一段时间了,一直都停留在使用框架别人的框架来满足公司的业务需求,很少深入到一个框架的内部,去研究它的实现方式和实现原理,更加没有自己去写过框架,真的是非常惭愧。
最近老大让我用Retrofit做一个单文件的上传和下载模块,几番折腾之后,花了三天时间,终于搞出来了,虽然很简单,但通过这么一个例子,让我学到了封装一个框架的基本思路,在这里做一个记录,顺便分享给大家。
代码下载地址在文章末尾。
首先,来说一下用Retrofit怎么实现文件的上传和下载。
1.需要在项目中新建一个接口,叫:UploadDownloadService,在其中可以使用注解的方式写三个方法:
前两个方法都是用于上传文件的,主要区别在于第一个方法的url后面不需要带任何参数,第二个方法的url后面需要携带参数,当然你也可以给第二个方法的参数传空,这里为了区别,就写的明显一点。第三个方法用于下载文件,参数就是一个url的地址。
然后还需要新建两个类,叫ManagerFactory和RetrofitFactory,主要作用就是生产UploadDownloadService的实例(工厂模式),源代码如下:
RetrofitFactory:
ManagerFactory:
写好了这三个类之后,我们就可以正常使用了,首先我们要通过工厂来创建UploadDownloadService的实例:
然后我们要对上传的信息做一些配置,比如要上传的文件,相关的参数等等
配置好上传信息之后,我们就可以使用UploadDownloadService的实例来执行具体的上传文件的操作,这里假定我们上传的文件是要带参数的:
使用Retrofit实现单个文件的下载与上传类似,这里就不在贴了。
如果我们想自己将上面这个上传文件的过程封装一下,该如何做呢?首先肯定是要提取一个类,这个类提供一个上传文件的方法,上传的过程中我们还要提供回调,因此还需要提取一个回调类,然后相关的配置信息也应该用一个单独的类来进行管理。于是我们就要创建三个类,分别叫RetrofitUploadManager、RetrofitUploadAdapter和RetrofitUoloadConfig,第一个类负责主要的上传业务,第二个类提供上传回调,第三个类的作用是将相关的配置信息统一管理。
我们分别来看一下这三个类的基本结构:
RetrofitUploadManager
接着来看RetrofitUploadAdapter这个类:
这是一个抽象类,来提供下载结果的回调信息。可能会有人会问,为什么不用接口呢?主要有两个原因,一个是因为这里需要用到泛形,需要在类初始化的得到泛形的真实类型,其次是如果用接口的话需要实现所有的方法,而抽象类可以选择性的复写相应的方法。
最后看RetrofitUploadConfig这个类:
这个类使用了建造者模式,来对外提供了相关变量的设置和获取方法。
最后来看一下如何使用:
使用起来还是蛮简单的,如果还有什么功能可以自己去扩展。下载基本上与上传类似,有兴趣的朋友可以去下载下来看看。另外,Demo的使用示例还用到了拍照和上传,如果看不懂的可以参考这篇博客:Android拍照及选择图片及裁剪及兼容6.0权限实现
Demo下载地址
最近老大让我用Retrofit做一个单文件的上传和下载模块,几番折腾之后,花了三天时间,终于搞出来了,虽然很简单,但通过这么一个例子,让我学到了封装一个框架的基本思路,在这里做一个记录,顺便分享给大家。
代码下载地址在文章末尾。
首先,来说一下用Retrofit怎么实现文件的上传和下载。
1.需要在项目中新建一个接口,叫:UploadDownloadService,在其中可以使用注解的方式写三个方法:
/** * Upload a file without any url params. * * @param url the url linking to your file server * @param description the file's description to upload * @param file the file to upload * @return the uploading file's results */ @Multipart @POST Call<ResponseBody> uploadFileWithoutParams(@Url String url, @Part("description") RequestBody description, @Part MultipartBody.Part file); /** * Upload a file with some params attached to the url. * * @param url the url linking to your file server * @param map the params attached to the url * @param description the file's description to upload * @param file the file to upload * @return the uploading file's result */ @Multipart @POST Call<ResponseBody> uploadFileWithParams(@Url String url, @QueryMap Map<String, Object> map, @Part("description") RequestBody description, @Part MultipartBody.Part file); /** * Download file from an url * * @param fileUrl the particular destination url * @return download information */ @Streaming @GET Call<ResponseBody> downloadFile(@Url String fileUrl);
前两个方法都是用于上传文件的,主要区别在于第一个方法的url后面不需要带任何参数,第二个方法的url后面需要携带参数,当然你也可以给第二个方法的参数传空,这里为了区别,就写的明显一点。第三个方法用于下载文件,参数就是一个url的地址。
然后还需要新建两个类,叫ManagerFactory和RetrofitFactory,主要作用就是生产UploadDownloadService的实例(工厂模式),源代码如下:
RetrofitFactory:
package com.yulin.download_upload.api; import android.support.annotation.NonNull; import okhttp3.OkHttpClient; import retrofit2.Converter; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; public class RetrofitFactory { private static Retrofit mRetrofit; private static OkHttpClient mClient; private static String baseUrl = "http://www.baidu.com"; public static void setBaseUrl(@NonNull String url) { baseUrl = url; } public static void setOkhttpClient(@NonNull OkHttpClient client) { mClient = client; } /** * 获取配置好的retrofit对象来生产Manager对象 */ public static Retrofit getRetrofit() { if (mRetrofit == null) { // if (baseUrl == null || baseUrl.length() <= 0) { // throw new IllegalStateException("请在调用getFactory之前先调用setBaseUrl"); // } Retrofit.Builder builder = new Retrofit.Builder(); builder.baseUrl(baseUrl) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()); if (mClient != null) builder.client(mClient); mRetrofit = builder.build(); } return mRetrofit; } /** * 获取配置好的retrofit对象来生产Manager对象 */ public static Retrofit getRetrofit(Converter.Factory factory) { if (baseUrl == null || baseUrl.length() <= 0) { throw new IllegalStateException("请在调用getFactory之前先调用setBaseUrl"); } Retrofit.Builder builder = new Retrofit.Builder(); builder.baseUrl(baseUrl) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .addConverterFactory(factory); if (mClient != null) builder.client(mClient); return builder.build(); } }
ManagerFactory:
package com. 4000 yulin.download_upload.api; import java.util.HashMap; import retrofit2.Converter; public class ManagerFactory { private static ManagerFactory factory; private static HashMap<String, Object> serviceMap = new HashMap<>(); public static ManagerFactory getFactory() { if (factory == null) { synchronized (ManagerFactory.class) { if (factory == null) factory = new ManagerFactory(); } } return factory; } public <T> T getManager(Class<T> clz) { Object service = serviceMap.get(clz.getName()); if (service == null) { service = RetrofitFactory.getRetrofit().create(clz); serviceMap.put(clz.getName(), service); } return (T) service; } public <T> T getManager(Class<T> clz, Converter.Factory factory) { Object service = serviceMap.get(clz.getName()); if (service == null) { service = RetrofitFactory.getRetrofit(factory).create(clz); serviceMap.put(clz.getName(), service); } return (T) service; } }
写好了这三个类之后,我们就可以正常使用了,首先我们要通过工厂来创建UploadDownloadService的实例:
UploadDownloadService service = ManagerFactory.getFactory().getManager(UploadDownloadService.class);
然后我们要对上传的信息做一些配置,比如要上传的文件,相关的参数等等
private static final String IMAGE_SAVE_DIR = Environment. getExternalStorageDirectory().getPath() + "/yulin/photo"; private static final String IMAGE_SAVE_PATH = IMAGE_SAVE_DIR + "/demo.jpg"; //要上传的文件 File file = new File(IMAGE_SAVE_PATH); private static final String url = "http://pic.test.com";//模拟的文件上传地址 RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file); MultipartBody.Part body = MultipartBody.Part.createFormData( “file”, file.getName(), requestFile ); RequestBody description = RequestBody.create( MediaType.parse("multipart/form-data"),"this is a test file");
配置好上传信息之后,我们就可以使用UploadDownloadService的实例来执行具体的上传文件的操作,这里假定我们上传的文件是要带参数的:
Call<ResponseBody> call = mService.uploadFileWithParams( getUploadUrl(), getParamsMap(), //获取参数 description, body ); call.enqueue(new Callback<ResponseBody>() { @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { //成功回调 } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { //失败回调 } );
使用Retrofit实现单个文件的下载与上传类似,这里就不在贴了。
如果我们想自己将上面这个上传文件的过程封装一下,该如何做呢?首先肯定是要提取一个类,这个类提供一个上传文件的方法,上传的过程中我们还要提供回调,因此还需要提取一个回调类,然后相关的配置信息也应该用一个单独的类来进行管理。于是我们就要创建三个类,分别叫RetrofitUploadManager、RetrofitUploadAdapter和RetrofitUoloadConfig,第一个类负责主要的上传业务,第二个类提供上传回调,第三个类的作用是将相关的配置信息统一管理。
我们分别来看一下这三个类的基本结构:
RetrofitUploadManager
public class RetrofitUploadManager { private static final String TAG = RetrofitUploadManager. class.getSimpleName(); private WeakReference<Context> mContext; private RetrofitUploadConfig mConfig; private RetrofitUploadAdapter mAdapter; private RetrofitUploadService mService; public RetrofitUploadManager( RetrofitUploadConfig retrofitUploadConfig) {} public void uploadFile(File file) {} public void release() {} }
接着来看RetrofitUploadAdapter这个类:
public abstract class RetrofitUploadAdapter<T> { private static final String TAG = RetrofitUploadAdapter.class. getSimpleName(); private Class<T> mClazz; public Class<T> getClazz() { return mClazz; } public RetrofitUploadAdapter() { try { ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); mClazz =(Class<T>) pt.getActualTypeArguments()[0]; }catch (Exception e) { Log.e(TAG, e + ""); mClazz = null; } } public void onUploadSuccess(int code, String message) {} public void onUploadFailure(int code, String message){} public void onUploadError(Throwable t){} }
这是一个抽象类,来提供下载结果的回调信息。可能会有人会问,为什么不用接口呢?主要有两个原因,一个是因为这里需要用到泛形,需要在类初始化的得到泛形的真实类型,其次是如果用接口的话需要实现所有的方法,而抽象类可以选择性的复写相应的方法。
最后看RetrofitUploadConfig这个类:
public class RetrofitUploadConfig { private Context mContext; private String mUploadUrl; private String mFileKey; private String mDescriptionString; private Map<String, Object> mParamsMap; private String mFileName; private RetrofitUploadAdapter mRetrofitUploadAdapter; private RetrofitUploadConfig() {} public Context getContext() { return mContext; } public String getUploadUrl() { return mUploadUrl; } public String getFileKey() { return mFileKey; } public String getDescriptionString() { return mDescriptionString; } public Map<String, Object> getParamsMap() { return mParamsMap; } public String getFileName() { return mFileName; } public RetrofitUploadAdapter getRetrofitUploadAdapter() { return mRetrofitUploadAdapter; } public static class Builder{ private static final String DEFAULT_FILE_KEY = "file"; private static final String DEFAULT_DESCRIPTION = "this is uploaded file description"; private Context mContext; private String mUploadUrl; private String mFileKey = DEFAULT_FILE_KEY; private String mDescriptionString = DEFAULT_DESCRIPTION; private Map<String, Object> mParamsMap; private String mFileName; private RetrofitUploadAdapter mRetrofitUploadAdapter; public Builder(Context context) { this.mContext = context; } public Builder setParamsMap( Map<String, Object> paramsMap) { this.mParamsMap = paramsMap; return this; } public Builder setFileName(String fileName) { this.mFileName = fileName; return this; } public Builder setUploadUrl(String uploadUrl) { this.mUploadUrl = uploadUrl; return this; } public Builder setFileKey(String fileKey) { this.mFileKey = fileKey; return this; } public Builder setDescriptionString( String descriptionString) { this.mDescriptionString = descriptionString; return this; } public Builder setRetrofitUploadAdapter( RetrofitUploadAdapter retrofitUploadListener) { this.mRetrofitUploadAdapter = retrofitUploadListener; return this; } private void applyConfig( RetrofitUploadConfig retrofitUploadConfig) { retrofitUploadConfig.mContext = this.mContext; retrofitUploadConfig.mUploadUrl = this.mUploadUrl; retrofitUploadConfig.mFileKey = this.mFileKey; retrofitUploadConfig.mDescriptionString = this.mDescriptionString; retrofitUploadConfig.mParamsMap = this.mParamsMap; retrofitUploadConfig.mFileName = this.mFileName; retrofitUploadConfig.mRetrofitUploadAdapter = this.mRetrofitUploadAdapter; } public RetrofitUploadConfig build() { RetrofitUploadConfig retrofitUploadConfig = new RetrofitUploadConfig(); applyConfig(retrofitUploadConfig); return retrofitUploadConfig; } } }
这个类使用了建造者模式,来对外提供了相关变量的设置和获取方法。
最后来看一下如何使用:
private String url = "http://test.pic.com/";//你的文件服务器地址 private String nameKey = "file"; private void uploadFile(File file) { Map<String, Object af87 > paramMap = new HashMap<>(); paramMap.put("key", "5fcfe94a91b1d2ae08867a4f3c55455c"); RetrofitUploadConfig retrofitUploadConfig = new RetrofitUploadConfig.Builder(this) .setUploadUrl(url) .setParamsMap(paramMap) .setFileKey(nameKey) .setDescriptionString("this is uploading test file") .setRetrofitUploadAdapter( new RetrofitUploadAdapter<PhotoBean>() { @Override public void onUploadSuccess(int code, PhotoBean photoBean) { if (photoBean != null && !TextUtils.isEmpty( photoBean.getUrl())) { mUploading.setText("Upload Success"); }else { mUploading.setText("Upload Failure"); } } public void onUploadFailure(int code, String message) { mUploading.setText("Upload Failure"); } @Override public void onUploadError(Throwable t) { mUploading.setText("Upload Error"); } }).build(); RetrofitUploadManager retrofitUploadManager = new RetrofitUploadManager(retrofitUploadConfig); retrofitUploadManager.uploadFile(file); }
使用起来还是蛮简单的,如果还有什么功能可以自己去扩展。下载基本上与上传类似,有兴趣的朋友可以去下载下来看看。另外,Demo的使用示例还用到了拍照和上传,如果看不懂的可以参考这篇博客:Android拍照及选择图片及裁剪及兼容6.0权限实现
Demo下载地址
相关文章推荐
- 基于SSH2框架下jquery插件ajaxfileupload文件上传,下载,预览(.doc)实例
- 基于Java的一个简单的文件上传下载功能
- 基于HttpClient 4.3.3 的一个上传、下载文件的例子
- 基于Java的一个简单的文件上传下载功能
- 基于Java的一个简单的文件上传下载功能
- Portal-Basic Java Web 应用开发框架:应用篇(四) —— 文件上传和下载
- [C# 网络编程系列]专题十一:实现一个基于FTP协议的程序——文件上传下载器
- 基于asp.net的文件上传和下载
- Linux下使用Shell脚本向FTP服务器上传或下载一个文件
- [C# 网络编程系列]专题十一:实现一个基于FTP协议的程序——文件上传下载器
- 自己写的一个JSP上传文件和下载文件的JavaBean
- 每天一个linux命令(26):用SecureCRT来上传和下载文件
- 基于asp.net的文件上传和下载
- 自己写的一个JSP上传文件和下载文件的JavaBean 自己写的一个JSP上传文件和下载文件的JavaBean
- Struts2 框架实现多文件上传下载
- [C# 网络编程系列]专题十一:实现一个基于FTP协议的程序——文件上传下载器
- Portal-Basic Web 应用开发框架:应用篇(四) —— 文件上传和下载
- JAVA文件上传下载开源框架
- 分享基于silverlight的一个大文件上传控件
- 自己写的一个JSP上传文件和下载文件的JavaBean