android 积累一些关于rxjava的知识点
16lz
2021-01-23
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 = Observable.create(new Observable.OnSubscribe() { @Override public void call(Subscriber<? super String> subscriber) { //事件中心 subscriber.onNext("hasaki"); subscriber.onNext("mianduijifengba"); subscriber.onCompleted();//此句话要是不调用 观察者里的oncompleted方法就不会调用 } });
Subject:也是一种事件的产生与发送者,可替代Observable
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+"???"); } });
当然被观察者还可以使用固定的方法来“发送数据给观察者”
just(传入要发送的数据序列并用逗号隔开);
/*** 此方法默认在执行结束加上onCompleted();*/ Observable observable = Observable.just("hasaki","mianduijifengba");
from(可传入对象数组);
String[] requestArray={"hasaki","mianduijifengba"}; /** * 此方法默认在执行结束加上onCompleted(); */ Observable observable = Observable.from(requestArray);
订阅:“被观察者去订阅观察者”:原因是:流式API设计(什么鬼?)
//订阅 observable.subscribe(subscriber);
取消订阅:(对于异步请求结束后,要解除引用关系,避免内存泄漏)
一般订阅之后都会产生一个Subscription对象
//订阅 Subscription subscribe = observable.subscribe(subscriber); if(subscribe.isUnsubscribed()){ //判断是否解除订阅 Log.i("rxjava","没有解除订阅"); subscribe.unsubscribe();//解除订阅 }else{ Log.i("rxjava","解除订阅"); }
初级进阶(线程控制)
因为被观察者是"事件的产生者",而观察者是"事件的消费者",通常在网络请求中,被观察者里开启子线程做网络请求,观察者里要在UI线程中跟新UI,主要运用到两个方法:
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); } });
中级进阶(变换)
所谓变换,就是将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。
1.Map(转换类操作符)单个转换
什么是转成不同的事件?
说白了就是我要把现金存入支付宝,我的事件队列是现金,我最后想要的事件结果是支付宝里有钱,可是现金不能直接存支付宝,得先变换一下
,即先存银行卡转成电子货币,最后支付宝转账。(转单个事件)
/** * 假设字符串格式为现金 * 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 + ""); } });
map的功能:即对象的一对一转换,当被观察者事件产生了,先执行map里的方法,再到观察者消费事件(对象的直接变换)
func1:提供了一个有形参和返回值的call方法(call方法?秀等麻袋,请自行对比一下之前被观察者里产生事件的call方法,先埋一个坑)
2.flatMap(转换类操作符)
那转不同的事件序列呢?(也就是转一堆)
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要求得到每个人身上的钱,
也许取的第一个对象是校长,第二个是老师,也许取的第一个对象是校长,第二个对象是学生。也就是说只
保证铺平序列,不能保证发送序列的顺序。
问题:我写的例子里,都是正常发送的顺序未发现错乱,也许是数据源基数太小。
3.contatMap(转换类操作符)
作用类似于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); } });
4.scan(转换类操作符)
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+""); } });
打印结果::
先直接返回第一个结果,然后此结果作为下一个结果的第一个条件参数。 对一个序列的数据应用一个函数,并将这个函数的结果发射出去作为下个数据应用合格函数时的第一个参数使用。
5.filter(过滤操作符)
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()); } });
6.take(过滤操作符)
//找出数据序列前三个数据
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()); } });
//找出第一项数据 first == take(1)
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()); } });
7.skip(过滤操作符)
忽略掉数据序列中的前多少项 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()); } });
8.elementAt(过滤操作符)
只获取数据序列中的某一项数据//获取第四项数据
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()); } });
9.distinct(过滤操作符)
过滤掉重复的元素
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()); } });
10.last last == takeLast(1)
最后一项
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()); } });
11.merge(组合操作符)
将多个发射序列合并成一个发射序列,新序列里的数据是无序的
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
???说好无序的呢?难道测试数据过小? 12.startWith(组合操作符) 发射数据之前插入一条相同类型的新数据或者相同类型的新发射序列
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()); } });
13.concat(组合操作符) 多个发射序列,逐个序列数据发出,接收的数据一定是顺序的
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
14.zip(组合操作符) 1+1组合,
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); } });
15.combineLatest(组合操作符)
第一个发射序列的最后一个元素与第二个发射序列的所有元素组合
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); } });
更多相关文章
- Android中XML文件的序列化生成与解析
- Android:week 12总结 内容观察者、广播、服务
- Android中的设计模式——DataSetObservable 观察者模式
- Android中实现跨进程通信(IPC)的方式(三)之观察者模式
- android 获取正在运行的应用程序列表
- android获取ro._修改Android序列号(Serial Number)
- android KEY ---MD5 证书序列号
- android观察者模式