您的位置:首页 > 其它

一个基于Retrofit的单文件上传、下载框架

2016-12-31 15:26 465 查看
从事Android开发工作也有一段时间了,一直都停留在使用框架别人的框架来满足公司的业务需求,很少深入到一个框架的内部,去研究它的实现方式和实现原理,更加没有自己去写过框架,真的是非常惭愧。

最近老大让我用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下载地址
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息