2

我正在编写一些代码来将记录插入 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
            }
        });

顺便说一句,我需要将subscribeOnwhenobserveOn设置为后台线程吗?

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

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

4

1 回答 1

3

我看到很多问题。

  1. 每个方法/运算符都是一个“任务”,它将基于先前的项目运行并将项目发送到下一个运算符。
  2. 为了减少代码冗长,我们通常使用带有 RxJava 的RetrolambdaGradle Retrolamda。如果你不想使用 Retolambda,你可以创建一个类NameModel,它包含从 Observable 创建到 subscribe() 之前的所有逻辑。那里有所有需要的逻辑,隔离。
  3. 如果你有一个网络调用,那么在订阅中总是有一个onErrorFunc 是个好主意,除非你在之前的某个地方捕获了所有可能的错误,例如使用 onErrorReturn。如果出现问题,onError 可以帮助您,例如通知用户。在订阅中而不是从链内部更新某些内容也是一种很好的做法,从而隔离了运营商的内容。
  4. 使subscribeOn进程在后台,而不是observeOn. 所以不,observeOn如果您不更改线程,则不需要,例如here
  5. “破坏”链的最佳方法是抛出错误或更复杂的是使用自定义.lift()操作符和自定义订阅者从内部取消订阅链。

根据评论更新:

从第二个上面的 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;
}
于 2015-09-14T19:23:00.873 回答