RxJava - 操作员是一个任务还是整个链是一项任务?

时间:2015-08-19 13:19:48

标签: android retrofit rx-java sqlbrite

我正在编写一些代码以将记录插入Sqlite数据库(如果表为空)。在插入任何数据之前,它会使Web服务调用LoveToDo.basecampClient().fetchMe()以返回一些数据。

我使用SqlBrite进行数据库访问,使用Retrofit进行Web访问。这是我的代码:

    Observable.just(LoveToDo.briteDatabase())
        .map(new Func1<BriteDatabase, Integer>() {
            @Override
            public Integer call(BriteDatabase briteDatabase) {
                Cursor cursor = briteDatabase.query("SELECT * FROM Accounts");

                try {
                    return cursor.getCount();

                } finally {
                    cursor.close();
                }
            }
        })
        .flatMap(new Func1<Integer, Observable<Person>>() {
            @Override
            public Observable<Person> call(Integer count) {
                if ( count == 0 ) {
                    return LoveToDo.basecampClient().fetchMe();
                }

                return null;
            }
        })
        .map(new Func1<Person, Boolean>() {
            @Override
            public Boolean call(Person person) {
                if ( person == null ) return false;

                BriteDatabase database = LoveToDo.briteDatabase();

                long count = database.insert(Account.TABLE, new Account.Builder()
                    .accountId(Settings.accountId)
                    .userName(Settings.userName)
                    .password(Settings.password)
                    .agent(Settings.agent)
                    .personId(person.id)
                    .build()
                );

                return count > 0;
            }
        })

        .subscribeOn(Schedulers.io())
        .observeOn( Schedulers.io() )

        .subscribe();

毋庸置疑,我不认为这是很棒的代码。我想做的是找出如何将这段代码转化为好的东西。因此,让我们使用它并挑选它的可怕性。

首先,我应该在一个运营商中组合数据库和Web服务调用操作。例如:

    Observable.just(LoveToDo.briteDatabase())
        .flatMap(new Func1<BriteDatabase, Observable<Person>>() {
            @Override
            public Observable<Person> call(BriteDatabase briteDatabase) {
                Cursor cursor = briteDatabase.query("SELECT * FROM Accounts");

                int count;
                try {
                    count = cursor.getCount();

                } finally {
                    cursor.close();
                }

                if ( count == 0 ) {
                    return LoveToDo.basecampClient().fetchMe();
                }

                return null;
            }
        })
        .map(new Func1<Person, Boolean>() {
            @Override
            public Boolean call(Person person) {
                if ( person == null ) return false;

                BriteDatabase database = LoveToDo.briteDatabase();

                long count = database.insert(Account.TABLE, new Account.Builder()
                        .accountId(Settings.accountId)
                        .userName(Settings.userName)
                        .password(Settings.password)
                        .agent(Settings.agent)
                        .personId(person.id)
                        .build()
                );

                return count > 0;
            }
        })

        .subscribeOn(Schedulers.io())
        .observeOn(Schedulers.io())

        .subscribe();

或者是否有充分的理由将这些操作隔离在链中?

第二件让我感到困惑的是这是一个后台操作 - 由于此代码,不会直接更新用户界面。这就是无参数subscribe()函数调用的原因。但是当出现异常时会发生什么?这是否意味着我必须做以下事情?

        .subscribe(new Action1<Boolean>() {
            @Override
            public void call(Boolean aBoolean) {
                // Do nothing
            }
        }, new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                // Do something with the exception
            }
        });

顺便说一下,当subscribeOn设置为后台线程时,是否需要observeOn

第三,链是用SqlBrite观察者启动的。在链的后面我再次需要SqlBrite,所以我使用单例LoveToDo.briteDatabase()访问它。这是一个坏主意吗?有更好的方法吗?

最后,有没有办法break;链?如果我能放弃我正在做的事情而不是在每一步检查丢失的数据,那就太好了

1 个答案:

答案 0 :(得分:3)

我看到很多问题。

  1. 每个方法/运算符都是一个“任务”,它将根据之前的项目运行,并将项目发送给下一个运算符。
  2. 为了减少代码动词,我们通常将RetrolambdaGradle Retrolamda与RxJava一起使用。如果您不想使用Retolambda,您可以创建一个类NameModel,其中包含Observable创建到subscribe()之前的所有逻辑。在那里有所有需要的逻辑,孤立。
  3. 如果你有网络电话,总是在订阅中有一个onError Func是个好主意,除非你在某个地方之前发现所有可能的错误。使用onErrorReturn。如果出现问题,onError可以帮助您,例如通知用户。在订阅中更新某些内容而不是从链内部更新内容也是一种很好的做法,从而隔离了运营商的内容。
  4. subscribeOn使流程位于后台,而不是observeOn。所以不,如果您不更改帖子example here,则不需要observeOn
  5. “打破”链条的最佳方法是抛出错误,或者更复杂的是使用定制订阅者的自定义.lift()运算符从内部取消订阅链。
  6. 根据评论进行更新:

    从第二个上面的2个,但我更喜欢这样的东西:

    Observable.just(LoveToDo.briteDatabase())
            .flatMap(briteDatabase -> {
                Cursor cursor = briteDatabase.query("SELECT * FROM Accounts");
    
                int count;
                try {
                    count = cursor.getCount();
    
                } finally {
                    cursor.close();
                }
    
                if (count == 0) {
                    return LoveToDo.basecampClient().fetchMe()
                            .map(person -> insertPerson(person, briteDatabase));
                }
    
                // if you want to track the account creation
                return just(false);
            })
            .subscribeOn(Schedulers.io())
            .subscribe(personInserted -> {
                // do something if the person was created or not
            }, e -> {
            });
    
    
    private Boolean insertPerson(Person person, BriteDatabase briteDatabase) {
        long count = briteDatabase.insert(Account.TABLE, new Account.Builder()
                .accountId(Settings.accountId)
                .userName(Settings.userName)
                .password(Settings.password)
                .agent(Settings.agent)
                .personId(person.id)
                .build());
    
        return count > 0;
    }