[Android]RxJava的简单介绍和基本使用(二):retrofit2的简单介绍

接着上一篇:[Android]RxJava的简单介绍和基本使用(一)

线程调度

在Android程序里模拟一个耗时任务,常规情况下,以前我们可以New一个worker线程,然后通过Handler更新UI上绑定的数据。

现在让我们来看看Rxjava和Rxandroid 直接如何默契的完成这样的场景

Flowable.create(new FlowableOnSubscribe<String>() {
            @Override
            public void subscribe(FlowableEmitter<String> e) throws Exception {
                //这边的任务将会在Schedulers.io()线程中执行,所以延时任务不会卡屏
                e.onNext("---开始模拟一个耗时10秒的任务---");
                Thread.sleep(10000);
                e.onNext("--onNext---[执行即将完成]--");
                e.onComplete();
            }
        }, BackpressureStrategy.BUFFER)
                .subscribeOn(Schedulers.io()) //subscribeOn方法可以调度订阅方法(subscribe)执行的所在线程,这里设置成io线程:一个缓存池的新线程
                .observeOn(AndroidSchedulers.mainThread()) //指定和调度由发射源Flowable发射出去的方法(onNext、onComplete)所运行的线程在AndroidUI主线程,就如同让一个观察者在UI线程观察数据的模样
                .subscribe(new Subscriber<String>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        s.request(2); //允许发射方法(onNext)的次数为2次
                    }

                    @Override
                    public void onNext(String s) {
                        Log.i(TAG, "onNext->string=" + s);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.i(TAG,"onError:",t);
                    }

                    @Override
                    public void onComplete() {
                        Log.i(TAG, "---onComplete---");
                    }
                });
subscribeOn(Schedulers.io()) :控制和切换订阅的方法(subscribe)执行的所在线程,就好像上一章开灯的案例,发射任务前我们执行的”开灯”的预期行为都是在大脑里(x相当于后台background线程)处理的。再仔细想想,这就是一个常规的响应式的逻辑。

observeOn(AndroidSchedulers.mainThread()):简单的说就是让OnNext方法在指定的线程里执行,这里指定了Android的主线程Ui线程操作。

打印结果:

04-25 11:23:07.103 5388-5388/org.jan.rxandroiddemo I/SimpleRxjvActivity: onNext->string=---开始模拟一个耗时10秒的任务---
04-25 11:23:17.103 5388-5388/org.jan.rxandroiddemo I/SimpleRxjvActivity: onNext->string=--onNext---[执行即将完成]--
04-25 11:23:17.103 5388-5388/org.jan.rxandroiddemo I/SimpleRxjvActivity: ---onComplete---

------------------------------------------------------------------------我是一条分割线----------------------------------------------------------------------------------------------------------------------------------

Retrofit2.0的简单介绍

这个框架耳熟能详,是一个基于OKHTTP的网络异步框架,在Android开发中简化了请求数据的代码,是个比较火的开源项目,官网简洁的描述:A type-safe HTTP client for Android and Java,这边我们稍微简单介绍一下。

1.首先在As的app中gradle里关联下载jar

compile 'com.squareup.retrofit2:retrofit:2.2.0'
2.定义一个API-Service接口,这个接口里的方法就是你需要调用的请求(包含了请求路径URL,请求方式GET、POST...,出参和入参),比如:

public interface UserService {
    @POST("services/shipper/driver/authenticate")
    Call<BaseResponse<Token>> upgradeToken(@Body Map<String,String> params);

    @GET("users/{user}/repos")
    Call<List<Repo>> listRepos(@Path("user") String user);
}
3.创建Retrofit对象,并生成我们定义的UserService接口的实现类:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://服务基层的路径必须以/结束/")
                .addConverterFactory(FastJsonConverterFactory.create())
                .build();
        UserService userService = retrofit.create(UserService.class);

4.开始请求和接收回调,因为demo中的请求体是JosnString,且用的是Fastjson,所以用@Body标签:

Map<String,String> params = new HashMap<>();
        params.put("username","123456");
        params.put("password",MD5Utils.encodeTwice("123456"));
        Call<BaseResponse<Token>> call = userService.upgradeToken(params);
        call.enqueue(new Callback<BaseResponse<Token>>() {
            @Override
            public void onResponse(Call<BaseResponse<Token>> call, Response<BaseResponse<Token>> response) {
                BaseResponse<Token> tokenRes = response.body();
                if(tokenRes.getCode()==0){
                    Token tokenBean = tokenRes.getData();
                    Log.i(TAG,"token="+tokenBean.token);
                }else {
                    Log.i(TAG,"--获取失败--原因:"+tokenRes.getMsg());
                }
            }

            @Override
            public void onFailure(Call<BaseResponse<Token>> call, Throwable t) {
                Log.e(TAG,"错误提示:",t);
                if(call.isCanceled()){
                    Log.w(TAG,"---用户选择取消---");
                }
            }
        });
以上是Retrofit2的基本使用方式。

接下来,我们会接触Retrofit2 和 RxJava2的结合使用,

 我的demo中依赖主要有如下支持:

    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    compile 'com.squareup.retrofit2:retrofit:2.2.0'
    compile 'org.ligboy.retrofit2:converter-fastjson-android:2.1.0'
    compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0' //适配Rxjava2
    compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'

注意:com.squareup.retrofit2:adapter-rxjava2是适配rxjava2的一个工具,一定要引入!

先来个简单的post请求,抽取一个登录的测试

1.创建一个Retrofit的服务接口,login方法包含了请求路径的后缀,请求体是一个map(Gson适配会转成json),注意的是返回类型是Flowable

public interface UserService {

    @POST("services/xxxx/user/login")
    Flowable<ResponseBody> login(@Body Map<String, String> request);

}
2.创建Retrofit对象,实现login方法并订阅通讯回调事件。

Retrofit retrofit = new Retrofit.Builder().baseUrl("http://127.0.0.1:6100/mobile/")
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(RetrofitUtils.StringConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        UserService userService = retrofit.create(UserService.class);
        Map<String, String> user = new HashMap<String, String>();
        user.put("username", "123456");
        user.put("password", "654321");
        user.put("devicetoken", "");
        user.put("isToken", "false");
        userService.login(user).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<ResponseBody>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        s.request(Long.MAX_VALUE);
                    }

                    @Override
                    public void onNext(ResponseBody userLoginResponse) {
                        Toast.makeText(mContext, "回调成功!", Toast.LENGTH_SHORT).show();
                        try {
                            String restr = userLoginResponse.string();
                            Log.i(TAG,"responseStr="+restr);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        t.printStackTrace();
                        Toast.makeText(mContext, "获取失败,请检查网络是否畅通", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onComplete() {
                        Log.i(TAG, "---onComplete---");
                    }
                });
打印结果:responseStr={"code":1,"data":"","msg":"密码错误,请重新输入!"}

我这的返回结构实体如下

public class BaseResponse<T> implements Serializable {
    private T data;
    private int code;
    private String msg;

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}
3.为了demo少写点代码,简单封装了个RetrofitUtil类,写完发现真的很烂,算了,过段时间再改改,只用于学习。

这里的Retirofit对象设置了header信息以及使用了fastjson的解析适配,还有对Http请求和回调都加入了拦截器和上传下载的进度监听功能等,不具体介绍了,我们主要是看这么请求和处理结果。

public class RetrofitUtils {
    public static volatile boolean isShowProgress = false;
    public static final String HEADER_APP_ID = "appId";
    public static final String HEADER_TIMESTAMP = "timestamp";
    public static final String HEADER_CLIENT_VER = "clientVersion";
    public static final String HEADER_CLIENT_TYPE = "clientType";
    public static final String HEADER_TOKEN = "token";
    public static final String HEADER_SEND_TIME = "sendTime";
    public static final String HEADER_SIGN = "sign";
    public static final String CONTENT_TYPE = "application/json";
    public static final String ENCODE = "utf-8";
    public static final String URL = "http://127.0.0.1/mobile/";
    private static final int READ_TIMEOUT = 60;//读取超时时间,单位  秒
    private static final int CONN_TIMEOUT = 12;//连接超时时间,单位  秒
    private static Retrofit mRetrofit;
    private static Retrofit mUploadRetrofit;
    private static RetrofitUtils mRetrofitUtil;
    private static volatile String tokenStr = "";


    /**
     * 新建一个Retrofit对象
     *
     * @param context
     * @param baseUrl
     * @return
     */
    private static Retrofit newRetrofit(Context context, String baseUrl, BaseHttpListener httpListener) {
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.readTimeout(READ_TIMEOUT, TimeUnit.MINUTES)
                .connectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(READ_TIMEOUT,TimeUnit.MINUTES)
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        SimpleDateFormat myFmt =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        String dateStr = myFmt.format(new Date());
                        Request original = chain.request();
                        Request request = original.newBuilder()
                                .header(HEADER_APP_ID, "100010")
                                .header(HEADER_CLIENT_TYPE, "V1.0")
                                .header(HEADER_CLIENT_VER, "1.0.0")
                                .header(HEADER_SIGN, "1234567890")
                                .header(HEADER_TIMESTAMP, new Date().getTime() + "")
                                .header(HEADER_TOKEN, tokenStr)
                                .header(HEADER_SEND_TIME, dateStr)
                                .method(original.method(), original.body())
                                .build();
                        Log.i(TAG,dateStr);
                        return chain.proceed(request);
                    }
                });
        builder.addInterceptor(new LoggingInterceptor())
                .addNetworkInterceptor(httpLoggingInterceptor) //如果需要查看网络通讯的日志,可以加上这个拦截器
                .addNetworkInterceptor(new DownloadInterceptor(httpListener));
//                .addNetworkInterceptor(new NetCacheInterceptor())
//                .cache(new CacheProvide(context.getApplicationContext()).provideCache());
        OkHttpClient okClient = builder.build();
        mRetrofit = new Retrofit.Builder()
                //添加一个client,不然retrofit会自己默认添加一个
                .client(okClient)
                .baseUrl(baseUrl)
                .addConverterFactory(FastJsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        Log.i(TAG, "baseUrl=" + baseUrl);
        return mRetrofit;
    }

    private static Retrofit newUploadRetrofit(String uploadURL) {
        if (mUploadRetrofit == null) {
            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.readTimeout(READ_TIMEOUT, TimeUnit.MINUTES)
                    .connectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS)
                    .addInterceptor(new Interceptor() {
                        @Override
                        public Response intercept(Chain chain) throws IOException {
                            Request original = chain.request();
                            Request request = original.newBuilder()
                                    .header(HEADER_APP_ID, "100010")
                                    .header(HEADER_CLIENT_TYPE, "V1.0")
                                    .header(HEADER_CLIENT_VER, "1.0.0")
                                    .header(HEADER_SIGN, "1234567890")
                                    .header(HEADER_TIMESTAMP, new Date().getTime() + "")
                                    .header(HEADER_TOKEN, tokenStr)
                                    .method(original.method(), original.body())
                                    .build();
                            return chain.proceed(request);
                        }
                    });
            //针对上传文件的情况,不需要加入数据拦截
            mUploadRetrofit = new Retrofit.Builder()
                    .client(builder.build())
                    .baseUrl(uploadURL)
                    .addConverterFactory(FastJsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
            Log.i(TAG, "upload url :" + uploadURL);
        }
        return mUploadRetrofit;
    }

    public static Retrofit getRetrofit(Context context) {
        return getRetrofit(context, URL, null);
    }

    public static Retrofit getRetrofit(Context context, BaseHttpListener listener) {
        return getRetrofit(context, URL, listener);
    }

    public static Retrofit getRetrofit(Context context, String url) {
        return getRetrofit(context, url, null);
    }


    public static Retrofit getRetrofit(Context context, String baseUrl, BaseHttpListener listener) {
        if (TextUtils.isEmpty(baseUrl)) {
            throw new IllegalArgumentException("baseUrl is empty!");
        }
        //针对普通的GET/POST 请求数据,加入适当的拦截功能
        isShowProgress = false;
        if (listener != null || mRetrofit == null || !TextUtils.equals(baseUrl, URL)) {
            //重置为真,确保非上传的httpClient已经加入拦截
            isShowProgress = true;
            return newRetrofit(context, baseUrl, listener);
        }
        return mRetrofit;
    }

    public static void uploadFile(String baseUrl, File file, Map<String, String> params, final BaseHttpListener<BaseResponse> listener) {
        if (TextUtils.isEmpty(baseUrl)) {
            throw new IllegalArgumentException("baseUrl can not empty!");
        }
        listener.onStart();
        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), new UploadRequestBody(file, listener));

        Iterator<Map.Entry<String, String>> it = params.entrySet().iterator();
        Map<String, RequestBody> requestBodyMap = new HashMap<>();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            RequestBody rb = RequestBody.create(
                    MediaType.parse("multipart/form-data"), entry.getValue());
            requestBodyMap.put(entry.getKey(), rb);
        }
        mUploadRetrofit = newUploadRetrofit(baseUrl);
        UploadService userService = mUploadRetrofit.create(UploadService.class);
        userService.uploadFile(filePart, requestBodyMap)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DefaultSubscriber<BaseResponse>() {
                    @Override
                    public void onNext(BaseResponse baseResponse) {
                        if(baseResponse.getCode()==0){
                            listener.onSuccess(baseResponse);
                        }else {
                            listener.onError(baseResponse.getMsg());
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        listener.onError(t.getMessage());
                    }

                    @Override
                    public void onComplete() {
                    }

                });

    }

    static class LoggingInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();

            long t1 = System.nanoTime();
            Log.i("RetrofitUtils", String.format("Sending request %s on %s%n%s",
                    request.url(), chain.connection(), request.headers()));

            Response response = chain.proceed(request);
            long t2 = System.nanoTime();
            Log.i("RetrofitUtils", String.format("Received response for %s in %.1fms%n%s",
                    response.request().url(), (t2 - t1) / 1e6d, response.headers()));
            return response;
        }
    }

    static class NetCacheInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            Response response = chain.proceed(request);
            String cache = request.header("Cache-Time");
            if (!Utils.checkNULL(cache)) {//缓存时间不为空
                Response newResponse = response.newBuilder()
                        .removeHeader("Pragma")
                        .removeHeader("Cache-Control")
                        //cache for cache seconds
                        .header("Cache-Control", "max-age=" + cache)
                        .build();
                return newResponse;
            }
            return response;
        }
    }

    /**
     * 自定义Converter实现RequestBody到String的转换
     */
    public static class StringConverter implements Converter<ResponseBody, String> {

        public static final StringConverter INSTANCE = new StringConverter();

        @Override
        public String convert(ResponseBody value) throws IOException {
            return value.string();
        }
    }

    /**
     * 用于向Retrofit提供StringConverter
     */
    public static class StringConverterFactory extends Converter.Factory {

        public static final StringConverterFactory INSTANCE = new StringConverterFactory();

        public static StringConverterFactory create() {
            return INSTANCE;
        }

        // 我们只关实现从ResponseBody 到 String 的转换,所以其它方法可不覆盖
        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
            if (type == String.class) {
                return StringConverter.INSTANCE;
            }
            //其它类型我们不处理,返回null就行
            return null;
        }
    }


    public static <T> FlowableTransformer<BaseResponse<T>, T> applyResponse() {
        return new FlowableTransformer<BaseResponse<T>, T>() {
            @Override
            public Publisher<T> apply(Flowable<BaseResponse<T>> upstream) {
                return upstream.map(new Function<BaseResponse<T>, T>() {
                    @Override
                    public T apply(BaseResponse<T> tBaseResponse) throws Exception {
                        if (tBaseResponse.getCode() != 0) {
                            Log.i("RetrofitUtils", "error message:" + tBaseResponse.getMsg());
                            throw new RuntimeException(tBaseResponse.getMsg());
                        }
                        T data = tBaseResponse.getData();
//                        if(data instanceof JSONObject&&(((JSONObject) data).containsKey("token"))){
//                            tokenStr = ((JSONObject) data).getString("token");
//                            Log.i("RetrofitUtils","tokenStr="+tokenStr);
//                        }

                        if(data instanceof Token){
                            tokenStr = ((Token) data).token;
                            Log.i("RetrofitUtils","tokenStr="+tokenStr);
                        }
                        return data;
                    }
                }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
            }
        };
    }
再来个Get请求的测试,同上我们定义一个接口和方法

@GET("services/shipper/user/getAdBanner")
    Flowable<BaseResponse<List<AdBannerResponse>>> getAdBanner(@Query("userType") String userType, @Query("cityCode")String cityCode);

RetrofitUtils.getRetrofit(this).create(UserService.class).getAdBanner("0", "320200")
                //这边利用compose操作符实现数据流的类型转换,这边为了方便onNext方法能直接得到List<AdBannerResponse>的回调结果,请看看applyResponse如何转换的吧。
                .compose(RetrofitUtils.<List<AdBannerResponse>>applyResponse())
                .subscribe(new Subscriber<List<AdBannerResponse>>() {

                    @Override
                    public void onSubscribe(Subscription s) {
                        s.request(Long.MAX_VALUE);
                    }

                    @Override
                    public void onNext(List<AdBannerResponse> adBannerResponses) {
                        //这边直接得到的类型就是List<AdBannerResponse> ,比之前的省心了
                        Log.i(TAG, "---onNext---size=" + adBannerResponses.size());
                    }

                    @Override
                    public void onError(Throwable t) {
                        t.printStackTrace();
                        Log.i(TAG, "---onError---");
                    }

                    @Override
                    public void onComplete() {
                        Log.i(TAG, "---onComplete---");
                    }
                });

这时候我们如果需要下载这个功能,怎么让Retrofit来实现呢?下面就写一个例子参考参考。

同样的,先定义一个下载文件方法,@Streaming标签就是数据流的意思,url 就是下载连接

@Streaming
    @GET
    Flowable<ResponseBody> downloadFile(@Url String url);

接着看看下载的具体代码(我写的比较乱,见怪了各位大佬):

  //通常情况下,我们会有一个进度框的显示
        final MaterialDialog mdialog = new MaterialDialog.Builder(mContext)
                .progress(false, 100, true)
                .progressNumberFormat("%1d/%2d")
                .progressPercentFormat(NumberFormat.getPercentInstance()).build();
        mdialog.show();
        HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
        httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
        //在OkHttp对象创建后,执行一个带下载进度监听的拦截器DownloadInterceptor
        OkHttpClient okClient = new OkHttpClient.Builder().readTimeout(READ_TIMEOUT, TimeUnit.MINUTES)
                .connectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(READ_TIMEOUT,TimeUnit.MINUTES)
                .addNetworkInterceptor(httpLoggingInterceptor) //如果需要查看网络通讯的日志,可以加上这个拦截器
                .addNetworkInterceptor(new DownloadInterceptor(new BaseHttpListener() {
                    @Override
                    public void onStart() {
                    }

                    @Override
                    public void onProgress(Integer progress, String percent) {
                        Log.d(TAG,"testDownloadFile---progress="+progress);
                        Log.d(TAG,"testDownloadFile---threadName="+Thread.currentThread().getName());
                        //在主线程中显示进度
                        Flowable.just(progress).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Integer>() {
                            @Override
                            public void accept(Integer s) throws Exception {
                                mdialog.setProgress(s);
                            }
                        });
                    }

                    @Override
                    public void onSuccess(Object o) {
                    }

                    @Override
                    public void onError(String errorMsg) {
                    }
                })).build();
        //创建一个用于下载文件的Retrofit对象
        Retrofit mRetrofit = new Retrofit.Builder()
                .client(okClient)
                .baseUrl("http://www.baidu.com/")
                .addConverterFactory(FastJsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

        String fileUrl = "http://cms-bucket.nosdn.127.net/c4478a144128466192db8e0b1ef9546320170323182004.jpeg?imageView&thumbnail=550x0";
        UserService userService = mRetrofit.create(UserService.class);
        userService.downloadFile(fileUrl)
                .subscribeOn(Schedulers.io())
                .subscribe(new DefaultSubscriber<ResponseBody>() {

                    @Override
                    public void onNext(final ResponseBody responseBody) {
                        Log.d(TAG, "-----下载完成----");
                        //下面我们把ResponseBody这个数据流写入到本地,这边是下了个778的图片
                        Flowable.just(responseBody).map(new Function<ResponseBody, Boolean>() {
                            @Override
                            public Boolean apply(ResponseBody responseBody) throws Exception {
                                String path = Environment.getExternalStorageDirectory() + "/DCIM/Camera/";
                                File file = new File(path, "778.jpg");
                                return Utils.writeResponseBodyToDisk(mContext, responseBody, file);
                            }
                        })
                                .observeOn(AndroidSchedulers.mainThread())
                                .subscribe(new DefaultSubscriber<Boolean>() {
                                    @Override
                                    public void onNext(Boolean aBoolean) {
                                        Log.i(TAG, "success=" + aBoolean);
                                        Toast.makeText(mContext, "success=" + aBoolean, Toast.LENGTH_SHORT).show();
                                    }

                                    @Override
                                    public void onError(Throwable t) {
                                        Log.e(TAG, "--- downloadFile -onError---", t);
                                    }

                                    @Override
                                    public void onComplete() {
                                        Log.i(TAG, "---create file-onComplete----");
                                        mdialog.dismiss();
                                    }
                                });
                    }

                    @Override
                    public void onError(Throwable t) {
                        t.printStackTrace();
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "----onComplete---");
                        DialogUtils.dismiss();
                    }
                });

DownloadInterceptor.java

public class DownloadInterceptor implements Interceptor {
    private BaseHttpListener mProgressListener;

    public DownloadInterceptor(BaseHttpListener mProgressListener) {
        this.mProgressListener = mProgressListener;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
        if (RetrofitUtils.isShowProgress) {
            return originalResponse.newBuilder().body(new LoadResponseBody(originalResponse.body(), mProgressListener)).build();
        }
        return originalResponse;
    }
}
Utils.java

package org.jan.rxandroiddemo.progress;

import android.content.Context;
import android.text.TextUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import okhttp3.ResponseBody;

/**
 * Created by jan on 2017/3/24.
 */
public class Utils {

    public static boolean writeResponseBodyToDisk(Context context, ResponseBody body, File destFile) {
        try {
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                byte[] fileReader = new byte[4096];
                long fileSize = body.contentLength();
                long fileSizeDownloaded = 0;
                inputStream = body.byteStream();
                outputStream = new FileOutputStream(destFile);
                while (true) {
                    int read = inputStream.read(fileReader);
                    if (read == -1) {
                        break;
                    }
                    outputStream.write(fileReader, 0, read);
                    fileSizeDownloaded += read;
//                    Log.d("Utils", "file download: " + fileSizeDownloaded + " of " + fileSize+",path="+destFile.getAbsolutePath());
                }
                outputStream.flush();
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    public static String accuracy(double num, double total, int scale){
        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
        //可以设置精确几位小数
        df.setMaximumFractionDigits(scale);
        //模式 例如四舍五入
        df.setRoundingMode(RoundingMode.HALF_UP);
        double accuracy_num = num / total * 100;
        return df.format(accuracy_num);
    }

    public static boolean checkNULL(String val){
        if(TextUtils.isEmpty(val)){
            return true;
        }
        return false;
    }
}

上传

既然都说到下载了,这边再讲讲怎么上传文件吧,我就试着写了个上传单个照片文件的示例:

1.一如既往,先写个上传的接口方法:

package org.jan.rxandroiddemo.http;

import org.jan.rxandroiddemo.bean.BaseResponse;

import java.util.Map;

import io.reactivex.Flowable;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;

/**
 * Created by Jan on 2017/5/3.
 */
public interface UploadService {
    @Multipart
    @POST("services/fileUpload")
    Flowable<BaseResponse> uploadFile(@Part MultipartBody.Part photo, @PartMap() Map<String, RequestBody> params);
}
2.德玛西亚!走起~
 String filePath = "/storage/FF38-13F8/DCIM/Camera/778.jpg";
        File file = new File(filePath);
        if (!file.exists()) {
            Log.w(TAG, "待上传文件不存在!");
            return;
        }
        //简单使用
        Map<String,String> params = new HashMap<>();
        params.put("fileName","1145e09810724871aadc6b026cecbbd7");
        params.put("uploadCode","10006");
        RetrofitUtils.uploadFile("http://127.0.0.1:6000/report/", file, params, new BaseHttpListener<BaseResponse>() {
            @Override
            public void onStart() {
                dialog.show();
            }

            @Override
            public void onProgress(Integer progress,String ps) {
                Log.i(TAG,"progress="+ps);
                dialog.setProgress(progress.intValue());
            }

            @Override
            public void onSuccess(BaseResponse baseResponse) {
                Log.d(TAG, "testRetrofitUpload->accept->baseResponse.code="
                                + baseResponse.getCode() +",filename="
                                + baseResponse.getData().toString());
                dialog.dismiss();
            }

            @Override
            public void onError(String errorMsg) {
                Log.e(TAG,"onError:"+errorMsg);
                dialog.dismiss();
            }
        });
    }

public static void uploadFile(String baseUrl, File file, Map<String, String> params, final BaseHttpListener<BaseResponse> listener) {
        if (TextUtils.isEmpty(baseUrl)) {
            throw new IllegalArgumentException("baseUrl can not empty!");
        }
        listener.onStart();
        MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(), new UploadRequestBody(file, listener));

        Iterator<Map.Entry<String, String>> it = params.entrySet().iterator();
        Map<String, RequestBody> requestBodyMap = new HashMap<>();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            RequestBody rb = RequestBody.create(
                    MediaType.parse("multipart/form-data"), entry.getValue());
            requestBodyMap.put(entry.getKey(), rb);
        }
        mUploadRetrofit = newUploadRetrofit(baseUrl);
        UploadService userService = mUploadRetrofit.create(UploadService.class);
        userService.uploadFile(filePart, requestBodyMap)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DefaultSubscriber<BaseResponse>() {
                    @Override
                    public void onNext(BaseResponse baseResponse) {
                        if(baseResponse.getCode()==0){
                            listener.onSuccess(baseResponse);
                        }else {
                            listener.onError(baseResponse.getMsg());
                        }
                    }

                    @Override
                    public void onError(Throwable t) {
                        listener.onError(t.getMessage());
                    }

                    @Override
                    public void onComplete() {
                    }

                });

    }
UploadRequestBody.java ,里面有对上传过程的进度监听方法哦。

package org.jan.rxandroiddemo.progress;

import java.io.File;
import java.io.IOException;

import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import okio.Sink;

/**
 * Created by jan on 2017/3/23.
 */
public class UploadRequestBody extends RequestBody {

    private RequestBody mRequestBody;
    private BaseHttpListener mProgressListener;
    private BufferedSink mBuffereSink;

    public UploadRequestBody(File file,BaseHttpListener progressListener){
        this.mRequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
        this.mProgressListener = progressListener;
    }

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

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

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        if (mBuffereSink == null) {
            //包装
            mBuffereSink = Okio.buffer(sink(sink));
        }
        //写入
        mRequestBody.writeTo(mBuffereSink);
        //必须调用flush,否则最后一部分数据可能不会被写入
        mBuffereSink.flush();
    }

    private Sink sink(Sink sink) {
        return new ForwardingSink(sink) {
            //当前写入字节数
            long bytesWritten = 0L;
            //总字节长度,避免多次调用contentLength()方法
            long contentLength = 0L;

            @Override
            public void write(Buffer source, long byteCount) throws IOException {
                super.write(source, byteCount);
                if (contentLength == 0) {
                    //获得contentLength的值,后续不再调用
                    contentLength = contentLength();
                }
                //增加当前写入的字节数
                bytesWritten += byteCount;
                //回调上传接口
                String progress = Utils.accuracy(bytesWritten,contentLength,1);
                if(mProgressListener!=null){
                    Float proFloat = Float.parseFloat(progress);
                    Integer proInt = proFloat.intValue();
                    mProgressListener.onProgress(proInt,progress);
                }
            }
        };
    }


}
public interface BaseHttpListener<T> {
    public void onStart();

    public void onProgress(Integer progress ,String percent);

    public void onSuccess(T t);

    public void onError(String errorMsg);
}

好了。我也是没用多久这个,以上都是自行写的测试代码,均可用。

demo下载地址:点击打开链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值