Retrofit2 & RxJava2实现单文件和多文件上传

Retrofit2 是目前Android开发主流的网络库,RxJava2也是目前开发者使用的比较多用来更优雅实现异步的库,因为最近业务需求有用到这两个库,就简单分享下它的一个实际使用场景—上传文件

集成RxJava2和Retrofit2

    // Rx
    compile rootProject.ext.dependencies["rxjava"]
    compile rootProject.ext.dependencies["rxandroid"]
    compile rootProject.ext.dependencies["rxpermissions"]

    // network
    compile rootProject.ext.dependencies["retrofit"]
    compile rootProject.ext.dependencies["retrofit-converter-gson"]
    compile rootProject.ext.dependencies["retrofit-adapter-rxjava2"]
    compile rootProject.ext.dependencies["logging-interceptor"]

上面我将依赖统一抽取出来了,也建议大家这样做。

具体配置文件在根目录下的config.gradle

ext {
    android = [
            compileSdkVersion: 25,
            buildToolsVersion: '25.0.3',
            applicationId    : "com.tencent.bugly",
            minSdkVersion    : 16,
            targetSdkVersion : 25,
            javaVersion      : JavaVersion.VERSION_1_7,
            versionCode      : 1,
            versionName      : "1.0.0"
    ]


    def dependVersion = [
            rxJava             : "2.0.7",
            rxandroid          : "2.0.1",
            rxpermissions      : "0.9.3@aar",
            retrofit           : "2.2.0",
            okhttp3            : "3.4.1",
    ]

    dependencies = [
            // rx
            "rxjava"                            : "io.reactivex.rxjava2:rxjava:${dependVersion.rxJava}",
            "rxandroid"                         : "io.reactivex.rxjava2:rxandroid:${dependVersion.rxandroid}",
            "rxpermissions"                     : "com.tbruyelle.rxpermissions2:rxpermissions:${dependVersion.rxpermissions}",

            // network
            "retrofit"                          : "com.squareup.retrofit2:retrofit:${dependVersion.retrofit}",
            "retrofit-converter-gson"           : "com.squareup.retrofit2:converter-gson:${dependVersion.retrofit}",
            "retrofit-adapter-rxjava2"          : "com.squareup.retrofit2:adapter-rxjava2:${dependVersion.retrofit}",
            // 网络日志拦截
            "logging-interceptor"               : "com.squareup.okhttp3:logging-interceptor:${dependVersion.okhttp3}",
    ]
}

这是依赖的部分,集成之后会从maven仓库中将我们需要的库下载到本地,这样我就可以使用了 ,不用说,这些大家都懂。

封装OkHttpManager类

/**
 * OkHttp管理类.
 *
 * @author devilwwj
 * @since 2017/7/12
 */
public class OkHttpManager {

    private static OkHttpClient okHttpClient;

    /**
     * 获取OkHttp单例,线程安全.
     *
     * @return 返回OkHttpClient单例
     */
    public static OkHttpClient getInstance() {
        if (okHttpClient == null) {
            synchronized (OkHttpManager.class) {
                if (okHttpClient == null) {
                    OkHttpClient.Builder builder = new OkHttpClient.Builder();

                    if (BuildConfig.DEBUG) {
                        // 拦截okHttp的日志,如果开启了会导致上传回调被调用两次
                        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
                        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
                        builder.addInterceptor(interceptor);
                    }

                    // 超时时间
                    builder.connectTimeout(15, TimeUnit.SECONDS);// 15S连接超时
                    builder.readTimeout(20, TimeUnit.SECONDS);// 20s读取超时
                    builder.writeTimeout(20, TimeUnit.SECONDS);// 20s写入超时
                    // 错误重连
                    builder.retryOnConnectionFailure(true);
                    okHttpClient = builder.build();
                }
            }
        }
        return okHttpClient;
    }
}

这个类主要是获取OkHttpClient示例,设置它的一些参数,比如超时时间,拦截器等等.

封装RetrofitClient类

/**
 * RetrofitClient.
 *
 * @author devilwwj
 * @since 2017/7/12
 */
public class RetrofitClient {
    private static RetrofitClient mInstance;
    private static Retrofit retrofit;

    private RetrofitClient() {
        retrofit = RetrofitBuilder.buildRetrofit();
    }

    /**
     * 获取RetrofitClient实例.
     *
     * @return 返回RetrofitClient单例
     */
    public static synchronized RetrofitClient getInstance() {
        if (mInstance == null) {
            mInstance = new RetrofitClient();
        }
        return mInstance;
    }

    private <T> T create(Class<T> clz) {
        return retrofit.create(clz);
    }

    /**
     * 单上传文件的封装.
     *
     * @param url 完整的接口地址
     * @param file 需要上传的文件
     * @param fileUploadObserver 上传回调
     */
    public void upLoadFile(String url, File file,
                    FileUploadObserver<ResponseBody> fileUploadObserver) {

        UploadFileRequestBody uploadFileRequestBody =
                        new UploadFileRequestBody(file, fileUploadObserver);

        create(UploadFileApi.class)
                        .uploadFile(url, MultipartBuilder.fileToMultipartBody(file,
                                        uploadFileRequestBody))
                        .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                        .subscribe(fileUploadObserver);

    }

    /**
     * 多文件上传.
     *
     * @param url 上传接口地址
     * @param files 文件列表
     * @param fileUploadObserver 文件上传回调
     */
    public void upLoadFiles(String url, List<File> files,
                    FileUploadObserver<ResponseBody> fileUploadObserver) {

        create(UploadFileApi.class)
                        .uploadFile(url, MultipartBuilder.filesToMultipartBody(files,
                                        fileUploadObserver))
                        .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                        .subscribe(fileUploadObserver);

    }

}

这个是Retrofit客户端类,获取它的单例然后去调用它的上传文件的方法,可以看到我这里封装了两个方法,uploadFile是上传单个文件,uploadFiles方法上传多个文件.

因为我们需要构造一个Retrofit对象,所以这里有一个RetrofitBuilder类:

/**
 * Retrofit构造器.
 *
 * @author devilwwj
 * @since 2017/7/13
 */
public class RetrofitBuilder {
    private static Retrofit retrofit;

    public static synchronized Retrofit buildRetrofit() {
        if (retrofit == null) {
            Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
            GsonConverterFactory gsonConverterFactory = GsonConverterFactory.create(gson);
            retrofit = new Retrofit.Builder().client(OkHttpManager.getInstance())
                    .baseUrl(AppConfig.HTTP_SERVER)
                    .addConverterFactory(gsonConverterFactory)
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

可以看到,想构造Retrofit对象是需要获取OkhttpClient实例的。

定义上传文件接口

/**
 * 上传API.
 *
 * @author devilwwj
 * @since 2017/7/12
 */
public interface UploadFileApi {
    String UPLOAD_FILE_URL = AppConfig.HTTP_SERVER + "file/upload";

    @POST
    Observable<ResponseBody> uploadFile(@Url String url, @Body MultipartBody body);
}

这里就是Retrofit定义接口的形式,通过注解来表示各个参数,@POST表示发起post请求,@Url表示这是个请求地址,@Body表示这是请求体,关于Retrofit的各种注解的使用这里不多说,大家可以自行了解。

构造MultipartBody

上一步定义好了上传的接口,我们最终是要去构造MultipartBody,这一块就需要跟后台同学进行沟通了,根据接口定义来实现,这里是我们的实现:

/**
 * MultipartBuilder.
 *
 * @author devilwwj
 * @since 2017/7/13
 */
public class MultipartBuilder {

    /**
     * 单文件上传构造.
     *
     * @param file 文件
     * @param requestBody 请求体
     * @return MultipartBody
     */
    public static MultipartBody fileToMultipartBody(File file, RequestBody requestBody) {
        MultipartBody.Builder builder = new MultipartBody.Builder();

        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("fileName", file.getName());
        jsonObject.addProperty("fileSha", Utils.getFileSha1(file));
        jsonObject.addProperty("appId", "test0002");

        builder.addFormDataPart("file", file.getName(), requestBody);

        builder.addFormDataPart("params", jsonObject.toString());
        builder.setType(MultipartBody.FORM);
        return builder.build();
    }

    /**
     * 多文件上传构造.
     * 
     * @param files 文件列表
     * @param fileUploadObserver 文件上传回调
     * @return MultipartBody
     */
    public static MultipartBody filesToMultipartBody(List<File> files,
                    FileUploadObserver<ResponseBody> fileUploadObserver) {
        MultipartBody.Builder builder = new MultipartBody.Builder();
        JsonArray jsonArray = new JsonArray();

        Gson gson = new Gson();
        for (File file : files) {
            UploadFileRequestBody uploadFileRequestBody =
                            new UploadFileRequestBody(file, fileUploadObserver);
            JsonObject jsonObject = new JsonObject();

            jsonObject.addProperty("fileName", file.getName());
            jsonObject.addProperty("fileSha", Utils.getFileSha1(file));
            jsonObject.addProperty("appId", "test0002");

            jsonArray.add(jsonObject);
            LogUtil.d(jsonObject.toString());
            builder.addFormDataPart("file", file.getName(), uploadFileRequestBody);
        }

        builder.addFormDataPart("params", gson.toJson(jsonArray));

        LogUtil.d(gson.toJson(jsonArray));
        builder.setType(MultipartBody.FORM);
        return builder.build();
    }

}

自定义RequestBody

构造MultipartBody是需要去创建每个文件对应的ReqeustBody,但我们这边需要监听到文件上传成功、失败和进度的状态,所以需要去自定义:

/**
 * 上传文件请求body.
 *
 * @author devilwwj
 * @since 2017/7/12
 */
public class UploadFileRequestBody extends RequestBody {

    private RequestBody mRequestBody;
    private FileUploadObserver<ResponseBody> fileUploadObserver;

    public UploadFileRequestBody(File file, FileUploadObserver<ResponseBody> fileUploadObserver) {
        this.mRequestBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
        this.fileUploadObserver = fileUploadObserver;
    }


    @Override
    public MediaType contentType() {
        return mRequestBody.contentType();
    }

    @Override
    public long contentLength() throws IOException {
        return mRequestBody.contentLength();
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {

        CountingSink countingSink = new CountingSink(sink);
        BufferedSink bufferedSink = Okio.buffer(countingSink);
        // 写入
        mRequestBody.writeTo(bufferedSink);
        // 刷新
        // 必须调用flush,否则最后一部分数据可能不会被写入
        bufferedSink.flush();

    }

    /**
     * CountingSink.
     */
    protected final class CountingSink extends ForwardingSink {

        private long bytesWritten = 0;

        public CountingSink(Sink delegate) {
            super(delegate);
        }

        @Override
        public void write(Buffer source, long byteCount) throws IOException {
            super.write(source, byteCount);

            bytesWritten += byteCount;
            if (fileUploadObserver != null) {
                fileUploadObserver.onProgressChange(bytesWritten, contentLength());
            }

        }

    }
}

这里有个RxJava2的Observer的抽象类,主要是用来收到Rxjava2的事件:

/**
 * 上传文件的RxJava2回调.
 *
 * @author devilwwj
 * @since 2017/7/12
 *
 * @param <T> 模板类
 */
public abstract class FileUploadObserver<T> extends DefaultObserver<T> {

    @Override
    public void onNext(T t) {
        onUploadSuccess(t);
    }

    @Override
    public void onError(Throwable e) {
        onUploadFail(e);
    }

    @Override
    public void onComplete() {

    }

    // 上传成功的回调
    public abstract void onUploadSuccess(T t);

    // 上传失败回调
    public abstract void onUploadFail(Throwable e);

    // 上传进度回调
    public abstract void onProgress(int progress);

    // 监听进度的改变
    public void onProgressChange(long bytesWritten, long contentLength) {
        onProgress((int) (bytesWritten * 100 / contentLength));
    }
}

ok,到现在完整的代码实现已经说完。

具体使用方法

RetrofitClient.getInstance().upLoadFiles(UploadFileApi.UPLOAD_FILE_URL, files,
                new FileUploadObserver<ResponseBody>() {
                    @Override
                    public void onUploadSuccess(ResponseBody responseBody) {

                        if (responseBody == null) {
                            LogUtil.e("responseBody null");
                            return;
                        }

                        try {
                            JSONObject jsonObject = new JSONObject(responseBody.string());

                            ArrayList<String> fileIds = new ArrayList<String>();
                            fileIds.add(jsonObject.getString("fileId"));

                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }

                    }

                    @Override
                    public void onUploadFail(Throwable e) {
                    }

                    @Override
                    public void onProgress(int progress) {
                        LogUtil.d(String.valueOf(progress));
                    }
                });

笔者这里是上传到文件服务器,成功会返回对应的fileId。

总结

通篇代码实现很多,但可以看到使用Retrofit2和RxJava2的结合起来使用还是挺方便的,再也不用自己去控制线程的切换了,也不用去关注http的具体实现,少写了不少代码,实现起来也优雅不少,希望这篇文章能帮助到大家。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
OkHttp 是一个用于进行网络请求的开源框架,它基于 Java 的 HttpURLConnection 类进行封装,提供了更简、更强大的网络请求功能。 Retrofit2 是一个基于 OkHttp 的网络请求框架,它通过注解和反射的方式,将网络请求接口转换成具体的网络请求动作。同时,Retrofit2 也提供了许多强大的功能,例如请求头、请求体的自定义、请求解析器的设置、请求拦截器等。 RxJava2 是一个响应式编程框架,它提供了一种更优雅和简洁的处理异步操作的方式。通过使用观察者模式和链式调用的方式,我们可以简化对多个异步操作的管理和处理,同时提供了丰富的操作符,用于处理和组合异步数据流。 MVVM 是一种用于设计和实现用户界面的架构模式。它将应用程序的界面逻辑和数据逻辑分开,并通过数据绑定机制实现二者之间的通信。在 MVVM 中,Model 层负责数据的获取和处理,View 层负责界面的显示和用户输入的处理,而 ViewModel 则负责衔接二者之间的通信。 通过将 OkHttp、Retrofit2RxJava2 和 MVVM 结合使用,我们可以构建出一个功能强大、性能优秀、响应迅速的网络请求和数据处理框架。OkHttp 提供了稳定可靠的网络请求功能,Retrofit2 则提供了简易用的网络接口转换方式,RxJava2 则提供了异步数据处理和链式调用的能力,而 MVVM 则提供了一种清晰的架构模式,使得我们可以更好地组织应用程序的逻辑。总的来说,这些技术和框架的结合能够让我们在开发中更加高效、稳定和灵活。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小巫技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值