前言:MVP是什么我就不讲了,如果需要的可以去百度查查资料。
RxJava是观察者模式的一种实现,观察者为Consumer或者Subscriber 被观察者为Flowable
我们大致理一理,实现一个Present需要什么内容:
1.基本的,Present要得到Activity的实例对象,它们是一对一的关系。
2.首先,Present肯定是被观察者,他要处理完事件后返回结果给观察它的人。所以他要实现Flowable中具体的方法。
3.他要有接受事物处理的对象,也就是函数的参数,然后根据参数来进行处理和返回结果
4.他要有返回结果的能力,所以需要接受观察者的监听容器(Consumer,Subscriber),这也是需要写在函数之中的。
5.他要有处理多线程并能返回到主线程的能力,因为我调用了网络请求后不可能将子线程回到主线程的操作交由View处理。
6.他要避免内存泄漏,因为Present很多情况下需要用到多线程,如果持有Activity的实例,那么就会造成Activity释放的不及时。
这就是我整理出来的对Present的4点要求,下面我们一一实现。
以下主要涉及三个类,MainActivity(视图类),BasePresent(Present的基类),MainActivityPresent(MainActivity对应的Present实现类)
** 公共属性 **
我觉得把一些能够一起实现的放在基类里面,然后根据各有的特色进行继承,这样优化了代码的可读性和代码量
这里的公共属性我觉得有两个,一是回到主线程的能力,二是处理内存泄漏,不多说,先帖代码
public class BasePresent {
private WeakReference<Context> mContext ;
protected BasePresent(Context context){
mContext = new WeakReference<Context>(context);
}
protected Context getContext(){
return mContext.get();
}
protected void runOnUIThread(Runnable runnable){
//判断Context的合理
if(runnable==null || mContext.get() == null || !(mContext.get() instanceof Activity)){
return ;
}
((Activity)mContext.get()).runOnUiThread(runnable);
}
}
可以看到,我们通过WeakRefrence实现了对Activity的弱应用,而且通过给子类暴露的getContext方法,一是解决了内存泄漏的问题,二是对子类感受不到处理内存泄漏过程的存在。我们用构造方法来表明,我的Present必须持有Activity对象。
** View **
对于View来说,我只需要显示界面和调用Present,所以在View中必须实现对Present的初始化
public class MainActivity extends BaseActivity {
MainActivityPresent present ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
present = new MainActivityPresent(this);
}
}
** 业务 **
我现在有两个需求:
- 1.MainActivity需要进行登录操作
- 2.MainActivity需要下载图片并监听整个下载过程
这两点有什么不同?
确实有不同,第一个需求表示我传入一个值,返回给我一个结果就行了,但是第二个需求,需要我发送请求下载,然后下载的进度要实时的返回给我。
** RxJava2 实现 **
先第一个需求,首先我们不写MainActivity,我们写Present
首先我们需要有一个方法,
MainActivityPresent.java:
public void login(final Login login , final Consumer<String> consumer){
...
}
我们可以看到,我们登陆,需要接受View传给我的登陆信息,这里用Login,从而进行对数据的处理,处理完成后,需要返回结果给View,所以这里需要Consumer类,我们写一个完整的。 tip:使用final因为有用到线程读取数据
public class MainActivityPresent extends BasePresent {
public MainActivityPresent(Context context) {
super(context);
}
public void login(final Login login , final Consumer<String> consumer){
new Thread(new Runnable() {
@Override
public void run() {
//登陆代码.....
LogUtils.d("login");
//runOnUIThread
String result = "登陆成功";
final Flowable flowable = Flowable.just(result);
runOnUIThread(new Runnable() {
@Override
public void run() {
flowable.subscribe(consumer);
}
});
}
}).start();
}
}
重点有两句话
final Flowable flowable = Flowable.just(result);
flowable.subscribe(consumer);
这里我们创建了一个Flowable对象,传入结果即将要返回的结果result ,在登陆完成后我们通过第二句代码来反馈给View。我们可以看到第二句话写在了runOnUIThread中,第二句话执行意味着回馈View
接下来我们实现MainActivity的调用
MainActivity.java:
//对于只需要结果的数据,可以使用简单的Consumer
present.login(new Login(), new Consumer<String>() {
@Override
public void accept(@NonNull String s) throws Exception {
//登陆结果,s表示view需要present返回的类型
LogUtils.d("accept");
showTip(s);
}
});
可以看到,我们调用时实现了Consumer类,并重载了accept方法,这个方法便是在登陆处理完成后Present回调的方法。
下面我们实现第二个需求,第二个需求强调过程性,我们只能使用Subscriber来处理这个需求。
也是,第一步,写Present
方法
MainActivityPresent.java:
public void downloadImage(final String url ,final Subscriber<Integer> subscriber){}
一样的,就不多说了,下面帖全部代码
public class MainActivityPresent extends BasePresent {
public MainActivityPresent(Context context) {
super(context);
}
public void downloadImage(final String url ,final Subscriber<Integer> subscriber){
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(final FlowableEmitter<Integer> e) throws Exception {
LogUtils.d("subscribe");
//下载代码
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
//传回更新数据
runOnUIThread(new Runnable() {
@Override
public void run() {
e.onNext(47);
}
});
Thread.sleep(3000);
//传回下载完成数据
runOnUIThread(new Runnable() {
@Override
public void run() {
e.onComplete();
}
});
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}, BackpressureStrategy.BUFFER);
flowable.subscribe(subscriber);
}
}
我们可以看到过程很复杂,我们实现Flowable的方式也不同,这里最后也有一句,flowable.subscribe(subscriber); 这里注意的是,这句话执行并不意味着回馈消息,而是正式的注册监听,相当于关联起来。回馈消息重载的subscribe里面。
我们可以看到在subscribe 我开启了子线程,过3秒后我回到主线程执行了e.onNext(47); 又过3秒我回到主线程执行了e.onComplete(); 这里你可能不清楚含义,我先大致说一下,第一句话表示下载过程时的实时更新反馈,第二句话表示完成下载后的反馈。
好了 我们贴MainActivity的调用代码
MainActivity.java:
//对于需要过程的数据,需要用Subscriber,我们需要一个进度,这里选用Integer为进度消息
present.downloadImage("", new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
LogUtils.d("onSubscribe");
//开始请求
showTip("start", Toast.LENGTH_SHORT);
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
LogUtils.d("onNext");
//进度有更新
showTip(integer+"", Toast.LENGTH_SHORT);
}
@Override
public void onError(Throwable t) {
LogUtils.d("onError");
//下载出错
}
@Override
public void onComplete() {
LogUtils.d("onComplete");
//下载完成
showTip("complete");
}
});
看到了什么?我们实现Subscriber类时,我们重载了onNext方法,onComplete方法,是不是和上面对应起来了? 是的,在MainActivityPresent中的 ”过3秒后我回到主线程执行了e.onNext(47); 又过3秒我回到主线程执行了e.onComplete();” 和这里对应,那里反馈的消息在这里被调用。
😄,很难懂,自己慢慢理解
贴个所有的代码
public class BasePresent {
private WeakReference<Context> mContext ;
protected BasePresent(Context context){
mContext = new WeakReference<Context>(context);
}
protected Context getContext(){
return mContext.get();
}
protected void runOnUIThread(Runnable runnable){
//判断Context的合理
if(runnable==null || mContext.get() == null || !(mContext.get() instanceof Activity)){
return ;
}
((Activity)mContext.get()).runOnUiThread(runnable);
}
}
public class MainActivityPresent extends BasePresent {
public MainActivityPresent(Context context) {
super(context);
}
public void login(final Login login , final Consumer<String> consumer){
new Thread(new Runnable() {
@Override
public void run() {
//登陆代码.....
LogUtils.d("login");
//runOnUIThread
String result = "登陆成功";
final Flowable flowable = Flowable.just(result);
runOnUIThread(new Runnable() {
@Override
public void run() {
flowable.subscribe(consumer);
}
});
}
}).start();
}
public void downloadImage(final String url ,final Subscriber<Integer> subscriber){
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(final FlowableEmitter<Integer> e) throws Exception {
LogUtils.d("subscribe");
//下载代码
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
//传回更新数据
runOnUIThread(new Runnable() {
@Override
public void run() {
e.onNext(47);
}
});
Thread.sleep(3000);
//传回下载完成数据
runOnUIThread(new Runnable() {
@Override
public void run() {
e.onComplete();
}
});
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
}
}, BackpressureStrategy.BUFFER);
flowable.subscribe(subscriber);
}
}
public class MainActivity extends BaseActivity {
MainActivityPresent present ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
present = new MainActivityPresent(this);
//对于只需要结果的数据,可以使用简单的Consumer
present.login(new Login(), new Consumer<String>() {
@Override
public void accept(@NonNull String s) throws Exception {
//登陆结果,s表示view需要present返回的类型
LogUtils.d("accept");
//showTip(s);
}
});
//对于需要过程的数据,需要用Subscriber,我们需要一个进度,这里选用Integer为进度消息
present.downloadImage("", new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
LogUtils.d("onSubscribe");
//开始请求
showTip("start", Toast.LENGTH_SHORT);
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Integer integer) {
LogUtils.d("onNext");
//进度有更新
showTip(integer+"", Toast.LENGTH_SHORT);
}
@Override
public void onError(Throwable t) {
LogUtils.d("onError");
//下载出错
}
@Override
public void onComplete() {
LogUtils.d("onComplete");
//下载完成
showTip("complete");
}
});
}
}