第三方开源库 RxJava - Android实用开发场景

到底什么情况下可以使用 RxJava ? 我们需要知道不是使用 RxJava 而是响应式编程,好吧,那到底什么时候可以响应式编程?按道理来讲,我们写任何代码都可以采用响应式编程的思想,只不过是有没有必要而已。罗列一些使用场景:

  1. 防止按钮重复点击;
RxView.clicks(mClearContent).debounce(300, TimeUnit.MILLISECONDS)
  1. EditText 添加 addTextChangedListener
RxTextView.textChanges(mUserNameEt)
  1. 延迟执行
Observable.timer(3, TimeUnit.SECONDS)
  1. 不断轮询
Observable.interval(3, 3, TimeUnit.SECONDS)
  1. 减少频繁调用
RxTextView.textChanges(mUserNameEt).debounce(1200, TimeUnit.MILLISECONDS)
  1. RxPermission请求权限
rxPermissions.request(Manifest.permission.CAMERA)

2.RxPermission 源码分析

上面的用法真的是少得可怜,可能是我也用得比较少,其实还有一些如 RxBus + RxRelay,OkHttp + RxJava + Retrofit,MVP + Dragger + RxJava 等等,但这些目前一时半会还讲不清楚,希望后面分析完 Retrofit 源码后能说清楚一些皮毛。我们还是老老实实来看看 RxPermission 的源码?不是说好讲使用怎么又讲源码,为什么非得跟源码过不去?待会后面你就知道了。先看下使用(申请相机权限):

RxPermissions rxPermissions = new RxPermissions(this);
rxPermissions.request(Manifest.permission.CAMERA).subscribe(new Consumer<Boolean>() {
  @Override
  public void accept(Boolean aBoolean) throws Exception {
      if(aBoolean){
        // 有权限
      }else {
        // 没权限
      }
  }
});

回想一下我们在不用任何框架的时候,去申请权限那得多少代码啊~,关键是还需要重载 Activity 的 onRequestPermissionsResult() ,都怀疑人生了。后来自己好不容易写了个注解权限框架,但是当看到 RxPermission 就弃坑了,看看怎么实现的:

    public Observable<Boolean> request(final String... permissions) {
        return Observable.just(TRIGGER).compose(ensure(permissions));
    }

    public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {
        return new ObservableTransformer<T, Boolean>() {
            @Override
            public ObservableSource<Boolean> apply(Observable<T> o) {
                return request(o, permissions)
                        // Transform Observable<Permission> to Observable<Boolean>
                        .buffer(permissions.length)
                        .flatMap(new Function<List<Permission>, ObservableSource<Boolean>>() {
                            @Override
                            public ObservableSource<Boolean> apply(List<Permission> permissions) throws Exception {
                                if (permissions.isEmpty()) {
                                    // Occurs during orientation change, when the subject receives onComplete.
                                    // In that case we don't want to propagate that empty list to the
                                    // subscriber, only the onComplete.
                                    return Observable.empty();
                                }
                                // 如果有用户没有授权的权限,返回false
                                // Return true if all permissions are granted.
                                for (Permission p : permissions) {
                                    if (!p.granted) {
                                        return Observable.just(false);
                                    }
                                }
                                // 返回授权成功
                                return Observable.just(true);
                            }
                        });
            }
        };
    }

    @TargetApi(Build.VERSION_CODES.M)
    private Observable<Permission> requestImplementation(final String... permissions) {
        // 根据需要申请的权限创建一个 Observable 集合
        List<Observable<Permission>> list = new ArrayList<>(permissions.length);
        // 用来存放没有授予的权限
        List<String> unrequestedPermissions = new ArrayList<>();

        // In case of multiple permissions, we create an Observable for each of them.
        // At the end, the observables are combined to have a unique response.
        for (String permission : permissions) {
            mRxPermissionsFragment.log("Requesting permission " + permission);
            // 把需要申请的权限加入集合
            if (isGranted(permission)) {
                // Already granted, or not Android M
                // Return a granted Permission object.
                list.add(Observable.just(new Permission(permission, true, false)));
                continue;
            }

            if (isRevoked(permission)) {
                // Revoked by a policy, return a denied Permission object.
                list.add(Observable.just(new Permission(permission, false, false)));
                continue;
            }

            PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);
            // Create a new subject if not exists
            if (subject == null) {
                unrequestedPermissions.add(permission);
                subject = PublishSubject.create();
                mRxPermissionsFragment.setSubjectForPermission(permission, subject);
            }

            list.add(subject);
        }
        // 如果有权限没有授权,去申请
        if (!unrequestedPermissions.isEmpty()) {
            String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);
            requestPermissionsFromFragment(unrequestedPermissionsArray);
        }
        return Observable.concat(Observable.fromIterable(list));
    }
    // 调用自己额外创建的 Fragment 的申请权限方法
    @TargetApi(Build.VERSION_CODES.M)
    void requestPermissionsFromFragment(String[] permissions) {
        mRxPermissionsFragment.log("requestPermissionsFromFragment " + TextUtils.join(", ", permissions));
        mRxPermissionsFragment.requestPermissions(permissions);
    }

源码其实很简单,就那么几个类,我们之所以不需要在 Activity 中重写 onRequestPermissionsResult 方法了,那是因为 RxPermission 自己额外创建了一个 Fragment ,加载到了我们的 Activity 中,so easy 。

3.RxLogin 库封装

最后我们根据响应式编程的思想,参照 RxPermission 的源码思想,自己动手来打造一个 RxLogin 第三方登录框架。我一般都是用友盟第三方的:

public class UserLoginActivity extends Activity implements UMAuthListener {
    private UMShareAPI mUmShareAPI;
    public static final String PLATFORM_KEY = "PLATFORM_KEY";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mUmShareAPI = UMShareAPI.get(this);
        mUmShareAPI.doOauthVerify(this,SHARE_MEDIA.QQ, this);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        UMShareAPI.get(this).onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onStart(SHARE_MEDIA share_media) {
        
    }

    @Override
    public void onComplete(SHARE_MEDIA share_media, int i, Map<String, String> map) {
        // 获取参数用户信息,调用后台接口,请求登录
    }

    @Override
    public void onError(SHARE_MEDIA share_media, int i, Throwable throwable) {
        // 获取等三方账号信息出错
    }

    @Override
    public void onCancel(SHARE_MEDIA share_media, int i) {
        // 取消第三方登录
    }
}

上面这种写法,我们初级刚入门的时候一般会这么写,这样写到底行不行,也不说不行,也不说不对。但是一般我们不会直接依赖第三方(隔离),而且其实还会多出来很多方法像 onActivityResult() 、onStar()、onCancel() 等等一些我们可能都不需要或者说不想关注,但是又不得已,哎。

下面我们自己动手写一个RxLogin,一方面不与第三方的框架直接依赖(隔离),还有一方面我们过滤掉不关心的方法,还有一方面我们项目中如果有 Retrofit 、MVP 这些那会更加完美一些,当然还有一些其他方面。需要额外新建 5 个类,5个类有点多,如果你想放弃:

public class RxLogin implements UMAuthListener {
    private Activity activity;
    private RxLoginResult mLoginResult;
    private PublishSubject<RxLoginResult> mLoginEmitter;
    static UMAuthListener STATIC_LISTENER;

    private RxLogin(Activity activity){
        this.activity = activity;
        mLoginResult = new RxLoginResult();
        STATIC_LISTENER = this;
        mLoginEmitter = PublishSubject.create();
    }

    public Observable<RxLoginResult> doOauthVerify(final RxLoginPlatform platform) {
        mShareResult.setPlatform(platform);
        Intent intent = new Intent(activity,RxLoginActivity.class);
        intent.putExtra(PLATFORM_KEY,platform);
        activity.startActivity(intent);
        activity.overridePendingTransition(0,0);
        List<Observable<RxLoginResult>> list = new ArrayList<>(1);
        list.add(mLoginEmitter);
        return Observable.concat(list);
    }

    @Override
    public void onStart(SHARE_MEDIA share_media) {

    }

    @Override
    public void onComplete(SHARE_MEDIA share_media, int i, Map<String, String> map) {
        mLoginResult.setIsSucceed(true);
        mLoginResult.setUserInfo(map);
        mLoginEmitter.onNext(mLoginResult);
    }

    @Override
    public void onError(SHARE_MEDIA share_media, int i, Throwable throwable) {
        mLoginResult.setIsSucceed(false);
        mLoginEmitter.onNext(mLoginResult);
        throwable.printStackTrace();
    }

    @Override
    public void onCancel(SHARE_MEDIA share_media, int i) {
        mLoginResult.setIsSucceed(false);
        mLoginEmitter.onNext(mLoginResult);
    }

    static final SHARE_MEDIA platformChange(RxLoginPlatform platform){
        switch (platform){
            case Platform_QQ:
                return SHARE_MEDIA.QQ;
            case Platform_WX:
                return SHARE_MEDIA.WEIXIN;
        }
        return SHARE_MEDIA.WEIXIN;
    }

    public static RxLogin create(Activity activity){
        return new RxLogin(activity);
    }
}

看下最后的使用:

public class UserLoginActivity extends Activity implements UMAuthListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // RxLogin 使用方式如下:
        RxLogin.create(this)
            // 第三QQ登录
            .doOauthVerify(RxLoginPlatform.Platform_QQ)
            .subscribe(new Consumer<RxLoginResult>() {
                @Override
                public void accept(RxLoginResult rxLoginResult) throws Exception {
                if(rxLoginResult.isSucceed()){
                    // 调用后台接口,请求登录
                }
            }
        });
    }
}

所有分享大纲:Android进阶之旅 - 系统架构篇

视频讲解地址:https://pan.baidu.com/s/1pKCrVeF

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,273评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,349评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,709评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,520评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,515评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,158评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,755评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,660评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,203评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,287评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,427评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,122评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,801评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,272评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,808评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,440评论 2 359