接着上一篇:[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下载地址:点击打开链接