Skip to content

Why resubscribe the source observable emit same output when I use retryWhen operator? #4840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
fanturbo opened this issue Nov 12, 2016 · 12 comments

Comments

@fanturbo
Copy link

fanturbo commented Nov 12, 2016

code:

str = "aaa";
        Observable.just(str).map(new Func1<String, String>() {
                    @Override
                    public String call(String s) {
                        Log.i("====", "s == " + s);
                        if ("aaa".equals(s)) throw new RuntimeException(s);
                        return s + "123";
                    }
                }).retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
                    @Override
                    public Observable<?> call(Observable<? extends Throwable> observable) {
                        return observable.zipWith(Observable.range(1, 4), new Func2<Throwable, Integer, Integer>() {
                            @Override
                            public Integer call(Throwable throwable, Integer i) {
                                str = "ggg";
                                return i;
                            }
                        }).flatMap(new Func1<Integer, Observable<? extends Long>>() {
                            @Override
                            public Observable<? extends Long> call(Integer retryCount) {
                                return Observable.timer(1, TimeUnit.SECONDS);
                            }
                        });
                    }
                }).subscribe(new Action1<String>() {
                    @Override
                    public void call(String s) {
                        Log.i("====k", "s = " + s);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.i("====", "throwable = " + throwable.getMessage());
                    }
                });

In my opinion,it should log

aaa
ggg
ggg
...

but in fact it always log

aaa
aaa
aaa
...

why?

@akarnokd
Copy link
Member

Observable.just(str) captures str's value and is a constant Observable in this regard, no matter how str changes afterwards. Use Observable.fromCallable(() -> str) to get a fresh value each time there is a retry.

@fanturbo
Copy link
Author

fanturbo commented Nov 12, 2016

@akarnokd When I use retrofit & rxjava in project,api return type is Observable,is it a constant Observable?I use cookie in request,and when cookie is invalid,i request new cookie(cookie is a String type member variable) in retrywhen and i got same error from server afterwards because the cookie is old cookie.Why is Observable.just(str) a constant Observabl?Is it because of str's String type?or other reasons?Thanks for your reply.

@akarnokd
Copy link
Member

Let me illustrate the situation with a classical example program:

public class Example {
    static String str;

    public static void main(String[] args) {
        str = "aaa";

        String local = str;

        str = "ggg";

        System.out.println(local);
        System.out.println(local);
        System.out.println(local);
    }
}

Can you tell what this program prints to the console?

@fanturbo
Copy link
Author

@akarnokd I got it.Thanks for your example.

@akarnokd akarnokd added the 1.x label Nov 12, 2016
@akarnokd
Copy link
Member

Great. If you have further input on the issue, don't hesitate to reopen this issue or post a new one.

@fanturbo
Copy link
Author

fanturbo commented Nov 22, 2016

@akarnokd
I am sorry about so late to ask this.
I use rxjava&retrofit in my project.
@GET("group/{id}/users") Observable<List<User>> groupList(@Path("id") int groupId);
Is this return Observable also a constant Observable ?

@akarnokd
Copy link
Member

No.

@fanturbo
Copy link
Author

@akarnokd okay,it's the same question.groupId is member variable,when i first request from server i set groupid 0 and server return 404.and in retrywhen i changed groupId value,but i find in charles that in Request groupId is 0,too.(Now i use OkHttp interceptor to resolve this problem.But i want to know why groupId didn't change in second retry?)

@akarnokd
Copy link
Member

How do you call groupList()? If you call it once and resubscribe to the returned Observable, that request path id is baked in:

int groupId = 0;

Observable<List<User>> obs1 = groupList(groupId);

groupId = 1;

obs1.subscribe(v -> { }, Throwable::printStackTrace);

Do you think the last line will request with groupId == 1 ?

@fanturbo
Copy link
Author

fanturbo commented Nov 22, 2016

ApiClient.groupList(groupId)
                .map(new Func1<List<User>, List<User>>() {
                    @Override
                    public List<User> call(List<User> list) {
                        if (list.size() == 0) {
                            throw new RuntimeException("gg");
                        }
                        return list;
                    }
                })
                .retryWhen(new RetryWithDelay(3, 2000))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<User>>() {
                    @Override
                    public void call(List<User> response) {
                        fillData(response);
                    }
                }, new Action1<Throwable>() {
                    @Override
                    public void call(Throwable throwable) {
                        Log.i("===========k3", throwable.toString());
                    }
                });

this is my code.I change groupId in RetryWithDelay's call method.what's wrong with the code?

@akarnokd
Copy link
Member

You don't seem to understand how a value read from a variable won't change if you change the variable. Use defer:

Observable.defer(() -> ApiClient.groupList(groupId))
   // ... the rest

@fanturbo
Copy link
Author

@akarnokd yes,I didn't understand where differences are between Observable.just and Observable.fromCallable.Since you say 'No',I think retrofit's return Observable is like Observable.fromCallable,it is not a constant Observable and it will use fresh value when retry again.
But i was wrong.retrywhen operator's meaning is resubscribe source Observable,in my code,it is constant because ApiClient.groupList(groupId)) has produced,it is like Observable.just.(I think you should say 'Yes' because I think retrofit's return Observable is like a constant Observable) But when use
Observable.defer,the result is different because defer operator decides the Observable is new(use fresh variable) when subscribe.
Thanks for your help!love you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants