rxjava在我看来就是一套比较先进的asynctask,用来处理异步请求的一种规范(得益于它的代码思路清晰),在android中可以用来代替asynctask甚至是handler。常与当前流行网络请求框架retrofit2一同使用。
rxjava的采用了观察者模式:Observer/Subscriber(观察者),Observable(主题)。个人喜欢把Observer/Subscriber当做事件的消费者,Observable当做是事件的产生者。
优点:线程切换(android 网络请求与UI刷新),逻辑清晰
android-studio 环境配置:
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
Observer/Subscriber(观察者)
Observer observer=new Observer() {
@Override
public void onCompleted() {
Log.i("rxjava::","onCompleted::");
}
@Override
public void onError(Throwable e) {
Log.i("rxjava::","onError::"+e.getMessage());
}
@Override
public void onNext(String s) {
/**
* 被观察者“发给” 观察者的消息
* 可以理解成 事件在这里消费
*/
Log.i("rxjava::","onNext::"+s);
}
};
Subscriber subscriber=new Subscriber() {
@Override
public void onCompleted() {
Log.i("rxjava::","onCompleted::");
}
@Override
public void onStart() {
super.onStart();
Log.i("rxjava::","onStart::");
}
@Override
public void onError(Throwable e) {
Log.i("rxjava::","onError::"+e.getMessage());
}
@Override
public void onNext(String s) {
Log.i("rxjava::","onNext::"+s);
}
};
观察者两种写法的区别:
1.Subscriber
对 Observer
接口进行了一些扩展,即Subscriber
里可以重写一个onstart的方法
2.onstart方法:在被观察者“发送”消息之前。我也不知道有什么软用,不能加载进度条(因为不在UI线程)
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
//事件中心
subscriber.onNext("hasaki");
subscriber.onNext("mianduijifengba");
subscriber.onCompleted();//此句话要是不调用 观察者里的oncompleted方法就不会调用
}
});
PublishSubject.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber super String> subscriber) {
subscriber.onNext("nihao");
}
}).subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
Log.i("TTTTT:::",s+"???");
}
});
/**
* 此方法默认在执行结束加上onCompleted();
*/
Observable observable = Observable.just("hasaki","mianduijifengba");
String[] requestArray={"hasaki","mianduijifengba"};
/**
* 此方法默认在执行结束加上onCompleted();
*/
Observable observable = Observable.from(requestArray);
//订阅
observable.subscribe(subscriber);
//订阅
Subscription subscribe = observable.subscribe(subscriber);
if(subscribe.isUnsubscribed()){
//判断是否解除订阅
Log.i("rxjava","没有解除订阅");
subscribe.unsubscribe();//解除订阅
}else{
Log.i("rxjava","解除订阅");
}
observable.subscribeOn();//设置被观察者里事件产生所处的线程,即:调用call方法所处的线程
observable.observeOn();//观察者,事件消费所处的线程即:onNext
Schedulers.immediate()//当前线程,即默认
Schedulers.newThread()//顾名思义,开启新线程
Schedulers.io()//相当于cacheThreadPool 维护一个不定数量的线程池,还可重复利用
AndroidSchedulers.mainThread()//android 专利,顾名思义,即主线程。
example:开启线程读取数据,UI线程跟新数据::
String[] requestArray = {"hasaki", "面对疾风吧"};
Observable.from(requestArray)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(String s) {
tv.setText(s);
}
});
所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。
什么是转成不同的事件?
说白了就是我要把现金存入支付宝,我的事件队列是现金,我最后想要的事件结果是支付宝里有钱,可是现金不能直接存支付宝,得先变换一下
,即先存银行卡转成电子货币,最后支付宝转账。(转单个事件)
/**
* 假设字符串格式为现金
* int格式为电子货币
* func:字符串转int
*/
Observable.just("100").map(new Func1() {
@Override
public Integer call(String s) {
//先转电子货币
return Integer.valueOf(s);
}
}).subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
Log.i("电子货币转支付宝::", integer + "");
}
});
func1:提供了一个有形参和返回值的call方法(call方法?秀等麻袋,请自行对比一下之前被观察者里产生事件的call方法,先埋一个坑)
那转不同的事件序列呢?(也就是转一堆)
example:假设一天马云爸爸闲的蛋疼,他想看看他的支付宝后台,他想知道每个人的名字,以及每个人都绑定了什么样的银行卡。就是循环打印人名和循环
打印card名,假设类该这么写:Person类里维护了一个name和card数组,card类里维护了cardname和cardmoney
Person[] persons = {new Person("张三", new Card[]{new Card("工商", "100"), new Card("农行", "200")}),
new Person("亚索", new Card[]{new Card("工商", "300"), new Card("农行", "400")})
};
Observable.from(persons).flatMap(new Func1>() {
@Override
public Observable call(Person person) {
Log.i("rxjava::", person.getName());
return Observable.from(person.getCards());
}
}).subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Card card) {
Log.i("rxjava::", card.toString());
}
});
flatMap:对象转新的事件序列,注意func1返回值的变换,他返回的是一个被观察者对象,然后又拿着这个对象去
请求新的序列。
这里的应用环境究竟是什么样的呢,假设你要读取列表中的某个key,然后拿着这key读取新的列表
会用到吧?我也没用过啊,我是边看别人的博客,写下自己的理解。
我的理解:将一堆事件序列转成一个Observerble再统一发出去
flatMap的交叉问题:假设现有一个数组person[3]{校长,老师,学生},经过flatMap要求得到每个人身上的钱,
也许取的第一个对象是校长,第二个是老师,也许取的第一个对象是校长,第二个对象是学生。也就是说只
保证铺平序列,不能保证发送序列的顺序。
问题:我写的例子里,都是正常发送的顺序未发现错乱,也许是数据源基数太小。
User[] users=new User[]{new User("张三","22"),new User("李四","23"),new User("老王","24")};
Observable.from(users).concatMap(new Func1>() {
@Override
public Observable call(User user) {
return Observable.just(user.getFirstName());
}
}).subscribe(new Action1() {
@Override
public void call(String s) {
Log.i("YYYYTTT::",s);
}
});
Observable.just(1, 2, 3, 4, 5)
.scan(new Func2() {
@Override
public Integer call(Integer integer, Integer integer2) {
Log.i("YYYYTTT????", integer+"/"+integer2);
return integer + integer2;
}
}).subscribe(new Action1() {
@Override
public void call(Integer integer) {
Log.i("YYYYTTT::", integer+"");
}
});
List users=new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "22"));
users.add(new User("大锤", "25"));
Observable.from(users).filter(new Func1() {
@Override
public Boolean call(User user) {
if (user.getFirstName().equals("张三")){
return false;//过滤掉
}else{
return true;//留着
}
}
}).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users=new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "22"));
users.add(new User("大锤", "25"));
Observable.from(users).take(3).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable.from(users).first().subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users=new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "22"));
users.add(new User("大锤", "25"));
Observable.from(users).takeLast(3).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users=new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "22"));
users.add(new User("大锤", "25"));
Observable.from(users).takeUntil(new Func1() {
@Override
public Boolean call(User user) {
if (user.getFirstName().equals("大锤")){
return true;//读到此处不再读取
}else{
return false;
}
}
}).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users=new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "22"));
users.add(new User("大锤", "25"));
Observable.from(users).skip(3).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users=new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "22"));
users.add(new User("大锤", "25"));
Observable.from(users).skipLast(3).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users=new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "22"));
users.add(new User("大锤", "25"));
Observable.from(users).elementAt(3).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable.from(users).distinct().subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::", user.toString());
}
});
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable.from(users).distinct(new Func1() {
@Override
public String call(User user) {
return user.getFirstName();//过滤掉重名
}
}).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable.from(users).last().subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("GGGDDD::",user.toString());
}
});
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable ob1 = Observable.from(users);
Observable ob2 = Observable.just(1, 2, 3, 4);
Observable.merge(ob1, ob2).subscribe(new Subscriber
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable.from(users).startWith(new User("帅哥","18")).subscribe(new Action1() {
@Override
public void call(User user) {
Log.i("TTTDDDD::",user.toString());
}
});
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable ob1 = Observable.from(users);
Observable ob2 = Observable.just(1, 2, 3, 4, 5);
Observable.concat(ob1, ob2).subscribe(new Action1
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable ob1 = Observable.from(users);
Observable ob2 = Observable.just(1, 2, 3, 4, 5,6);
Observable.zip(ob1, ob2, new Func2() {
@Override
public String call(User user, Integer integer) {
return user.getFirstName()+integer;
}
}).subscribe(new Action1() {
@Override
public void call(String s) {
Log.i("TGGDD::",s);
}
});
List users = new ArrayList();
users.add(new User("张三", "22"));
users.add(new User("李四", "24"));
users.add(new User("老王", "25"));
users.add(new User("大锤", "25"));
users.add(new User("大锤", "25"));
Observable ob1 = Observable.from(users);
Observable ob2 = Observable.just(1, 2, 3, 4, 5,6);
Observable.combineLatest(ob1, ob2, new Func2() {
@Override
public String call(User user, Integer integer) {
return user.getFirstName()+integer;
}
}).subscribe(new Action1() {
@Override
public void call(String s) {
Log.i("TGGDD::",s);
}
});
总结:既然rxjava里可以转换,那么转换处在哪个线程里呢,转换通通处在观察者的线程里
即你在转换之前observable.observeOn()设定的是什么线程,它就处在什么线程里。
高级进阶:
rxjava+retrofit2
关于retrofit2我也在学习中,先做简单的使用:
导入:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-scalars:2.0.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
addCallAdapterFactory(RxJavaCallAdapterFactory.create())//让rxjava能使用retrofit2
private void initData() {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://fdj.abc5.cn")
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
HttpUser httpUser = retrofit.create(HttpUser.class);
httpUser.getUsers("hcy", "1234")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
Log.i("网络请求的数据::", "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i("网络请求的数据::", "onError" + e.getMessage());
}
@Override
public void onNext(String s) {
Log.i("网络请求的数据::", s);
tv.setText(s);
}
});
}
public interface HttpUser {
@POST("/api/index/posttest")
@FormUrlEncoded
Observable getUsers(@Field("username") String username, @Field("password") String password);
}
关于Action1,Action0,Func1使用与区别:
Func1:有形参,有返回
Func2:多个形参,有返回
Action1:有形参,无返回
Action0:无形参,无返回
Action1,Action0可以代替观察者里的三个方法,OnNext,OnError,OnCompleted;
Action1 onNextAction = new Action1() {
// onNext()
@Override
public void call(String s) {
Log.i("onNext:::",s);
}
};
Action1 onErrorAction = new Action1() {
// onError()
@Override
public void call(Throwable throwable) {
Log.i("onError:::",throwable.getMessage());
}
};
Action0 onCompletedAction = new Action0() {
// onCompleted()
@Override
public void call() {
Log.i("onCompleted","");
}
};
Observable.just("hasaki","posigei").subscribe(onNextAction,onErrorAction,onCompletedAction);
如何多次切换线程::
(线程切换容易出现的异常::java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
找到异常的最后一般又可以看到::Caused by: android.os.NetworkOnMainThreadException)
example:像我所在的公司要强求服务器的一条连接,要请求两次,第一次请求服务器的时间,然后按
字母顺序将请求的参数排序在最后加载请求的时间和appkey字段形成sign,再把sign和时间放入
参数map中请求第二个连接才能得到想要的数据,是不是贼几把麻烦,以前封装的时候因为要考虑到
其实是两次网络请求(不能放在主线程中,android基础),但请求得到的数据我又想能切换到主线程
跟新UI,以前的代码一大堆,现在贴出我第一次封装的代码,感受下rxjava切换线程的好处
package utils;
import android.util.Log;
import com.alibaba.fastjson.JSON;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import contact.Contact;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.PartMap;
import retrofit2.http.Url;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
/**
* Created by hcy on 2016/12/24.
*/
public class WytHttpUtils {
public interface HttpServerTime {
@GET("/api/getservicetime.php")
Observable HttpTime();
}
public interface HttpResult {
@POST
@Multipart
Observable httpResult(@Url String url, @PartMap Map params);
}
private Map map;
public WytHttpUtils(Map map) {
this.map = map;
}
public void HttpRequest(String url) {
ServiceFactory.createServiceFrom(HttpServerTime.class, "http://iexue.quxue100.cn")
.HttpTime()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(new Func1>() {
@Override
public Observable call(HttpTime httpTime) {
Log.i("DDDSSS222::", httpTime.toString());
if (httpTime.getStatus() == 1) {
//时间获取成功
String sign = getValueFromMap(map).append(httpTime.getResult()).append(Contact.APPKEY).toString();
map.put("time", httpTime.getResult() + "");
map.put("sign", md5Encode(sign));
getValueFromMap2(map);
Observable observable1 = ServiceFactory.createServiceFrom(HttpResult.class,"http://iexue.quxue100.cn").httpResult(url, getValueFromMap2(map));
return observable1;
} else {
return null;
}
}
}).observeOn(AndroidSchedulers.mainThread())//切换到主线程
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
Log.i("rxjava::onCompleted:", "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i("rxjava::onError:", e.getMessage());
}
@Override
public void onNext(String s) {
Log.i("rxjava::onNext:", s);
}
});
//return observable;
}
private String md5Encode(String content) {
//选择摘要算法
MessageDigest md = null;
try {
md = MessageDigest.getInstance("md5");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
//设置要摘要的内容:
md.update(content.getBytes());
//生成摘要
byte[] value = md.digest();
//转16进制
String md5Value = toHexString(value);
return md5Value;
}
private String toHexString(byte[] value) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < value.length; i++) {
//消除负数 我们认为每一个byte都是正数
int values = value[i] & 255;
if (values < 16) {
//如果值小于16 那么使用Integer.toHexString算出来的值是0-f
//必须补0
sb.append("0");
}
sb.append(Integer.toHexString(values));
}
return sb.toString();
}
private StringBuffer getValueFromMap(Map map) {
Iterator iter = map.entrySet().iterator();
StringBuffer buffer = new StringBuffer();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object val = entry.getValue();
buffer.append(val + "");
}
return buffer;
}
private Map getValueFromMap2(Map map) {
Iterator iter = map.entrySet().iterator();
Map requestBodyMap = new HashMap();
Log.i("DSDSDS::", map.size() + "");
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String val = (String) entry.getValue();
String key = (String) entry.getKey();
Log.i("rxjava::", key + "???" + val);
requestBodyMap.put(key, RequestBody.create(null, val));
//photos.put("password", RequestBody.create(null, "123456"));
}
return requestBodyMap;
}
private class HttpTime {
public HttpTime(int status, String msg, int result) {
this.status = status;
this.msg = msg;
this.result = result;
}
public HttpTime() {
}
@Override
public String toString() {
return "HttpTime{" +
"status=" + status +
", msg='" + msg + '\'' +
", result=" + result +
'}';
}
/**
* status : 1
* msg : 获取成功
* result : 1482549878
*/
private int status;
private String msg;
private int result;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
}
}
附录:
rxjava 正计时 与 倒计时
package utils;
import android.content.Context;
import com.orhanobut.logger.Logger;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import lessonmodule.LessonActivity;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import wytnewhttp.WytHttpUtils;
/**
* Created by hcy on 2017/2/24.
*/
public class RxUtils {
//倒计时
public static Observable countDownTime(int time) {
if (time < 0) {
time = 0;
}
final int countTime = time;
return Observable.interval(0,1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1() {
@Override
public String call(Long aLong) {
//解决八小时的时区问题
String format = new SimpleDateFormat("HH:mm:ss").format((countTime - aLong.intValue()) * 1000- TimeZone.getDefault().getRawOffset());
Logger.i("倒计时::"+format);
return format;
}
})
.take(countTime + 1);
}
//正计时
public static Observable countUpTime() {
final int countTime = 0;
return Observable.interval(0,1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1() {
@Override
public String call(Long aLong) {
return new SimpleDateFormat("HH:mm:ss").format((countTime + aLong.intValue())*1000);
}
});
}
public static void unsubscribe(Subscription subscription) {
if (subscription != null && !subscription.isUnsubscribed()) {
subscription.unsubscribe();
}
}
}
countTimeSubscription = RxUtils.countDownTime(60).subscribe(new Action1() {
@Override
public void call(String s) {
tv_countTime.setText(s);
}
});