Skip to content

Commit 7ebe755

Browse files
author
Kaushik Gopal
committed
feat: (wip) add timeout feature
1 parent b2b51f0 commit 7ebe755

File tree

5 files changed

+287
-24
lines changed

5 files changed

+287
-24
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package com.morihacky.android.rxjava;
2+
3+
import android.content.Context;
4+
import android.os.Bundle;
5+
import android.os.Handler;
6+
import android.os.Looper;
7+
import android.support.annotation.Nullable;
8+
import android.support.v4.app.Fragment;
9+
import android.view.LayoutInflater;
10+
import android.view.View;
11+
import android.view.ViewGroup;
12+
import android.widget.ArrayAdapter;
13+
import android.widget.ListView;
14+
15+
import com.morihacky.android.rxjava.app.R;
16+
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import java.util.concurrent.TimeUnit;
20+
21+
import butterknife.ButterKnife;
22+
import butterknife.InjectView;
23+
import butterknife.OnClick;
24+
import rx.Observable;
25+
import rx.Observer;
26+
import rx.Subscriber;
27+
import rx.Subscription;
28+
import rx.android.observables.AndroidObservable;
29+
import rx.android.schedulers.AndroidSchedulers;
30+
import rx.schedulers.Schedulers;
31+
import timber.log.Timber;
32+
33+
34+
public class DemoTimeoutFragment
35+
extends Fragment {
36+
37+
@InjectView(R.id.list_threading_log) ListView _logsList;
38+
39+
private LogAdapter _adapter;
40+
private List<String> _logs;
41+
42+
private Subscription _subscription;
43+
44+
@OnClick(R.id.btn_demo_timeout_1_2s)
45+
public void onStart2sTask() {
46+
_subscription = AndroidObservable.bindFragment(DemoTimeoutFragment.this,
47+
_getObservableTask_2sToComplete())
48+
.observeOn(AndroidSchedulers.mainThread())
49+
.subscribe(_getEventCompletionObserver());
50+
}
51+
52+
@OnClick(R.id.btn_demo_timeout_1_5s)
53+
public void onStart5sTask() {
54+
_subscription = AndroidObservable.bindFragment(DemoTimeoutFragment.this, _getObservableFor5sTask())
55+
.timeout(2, TimeUnit.SECONDS, _getTimeoutObservable())
56+
.subscribeOn(Schedulers.computation())
57+
.observeOn(AndroidSchedulers.mainThread())
58+
.subscribe(_getEventCompletionObserver());
59+
}
60+
61+
// -----------------------------------------------------------------------------------
62+
// Main Rx entities
63+
64+
private Observable<String> _getObservableFor5sTask() {
65+
return Observable.create(new Observable.OnSubscribe<String>() {
66+
67+
68+
@Override
69+
public void call(Subscriber<? super String> subscriber) {
70+
_log(String.format("Starting a 5s task"));
71+
subscriber.onNext("5 s");
72+
try {
73+
Thread.sleep(1200);
74+
} catch (InterruptedException e) {
75+
e.printStackTrace();
76+
}
77+
subscriber.onCompleted();
78+
}
79+
});
80+
}
81+
82+
private Observable<String> _getObservableTask_2sToComplete() {
83+
return Observable.create(new Observable.OnSubscribe<String>() {
84+
85+
86+
@Override
87+
public void call(Subscriber<? super String> subscriber) {
88+
_log(String.format("Starting a 2s task"));
89+
subscriber.onNext("2 s");
90+
try {
91+
Thread.sleep(2000);
92+
} catch (InterruptedException e) {
93+
e.printStackTrace();
94+
}
95+
subscriber.onCompleted();
96+
}
97+
}).subscribeOn(Schedulers.computation()).timeout(3, TimeUnit.SECONDS);
98+
}
99+
100+
private Observable<? extends String> _getTimeoutObservable() {
101+
return Observable.create(new Observable.OnSubscribe<String>() {
102+
103+
104+
@Override
105+
public void call(Subscriber<? super String> subscriber) {
106+
_log("Timing out this task ...");
107+
subscriber.onCompleted();
108+
}
109+
});
110+
}
111+
112+
private Observer<String> _getEventCompletionObserver() {
113+
return new Observer<String>() {
114+
115+
116+
@Override
117+
public void onCompleted() {
118+
_log(String.format("task was completed"));
119+
}
120+
121+
@Override
122+
public void onError(Throwable e) {
123+
_log(String.format("Dang a task timeout"));
124+
onCompleted();
125+
Timber.e(e, "Timeout Demo exception");
126+
}
127+
128+
@Override
129+
public void onNext(String taskType) {
130+
_log(String.format("onNext %s task", taskType));
131+
}
132+
};
133+
}
134+
135+
@Override
136+
public void onDestroy() {
137+
super.onDestroy();
138+
_subscription.unsubscribe();
139+
}
140+
141+
// -----------------------------------------------------------------------------------
142+
// Method that help wiring up the example (irrelevant to RxJava)
143+
144+
@Override
145+
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
146+
super.onActivityCreated(savedInstanceState);
147+
_setupLogger();
148+
}
149+
150+
@Override
151+
public View onCreateView(LayoutInflater inflater,
152+
@Nullable ViewGroup container,
153+
@Nullable Bundle savedInstanceState) {
154+
View layout = inflater.inflate(R.layout.fragment_subject_timeout, container, false);
155+
ButterKnife.inject(this, layout);
156+
return layout;
157+
}
158+
159+
private void _setupLogger() {
160+
_logs = new ArrayList<String>();
161+
_adapter = new LogAdapter(getActivity(), new ArrayList<String>());
162+
_logsList.setAdapter(_adapter);
163+
}
164+
165+
private void _log(String logMsg) {
166+
167+
if (_isCurrentlyOnMainThread()) {
168+
_logs.add(0, logMsg + " (main thread) ");
169+
_adapter.clear();
170+
_adapter.addAll(_logs);
171+
172+
} else {
173+
_logs.add(0, logMsg + " (NOT main thread) ");
174+
175+
// You can only do below stuff on main thread.
176+
new Handler(Looper.getMainLooper()).post(new Runnable() {
177+
178+
179+
@Override
180+
public void run() {
181+
_adapter.clear();
182+
_adapter.addAll(_logs);
183+
}
184+
});
185+
}
186+
}
187+
188+
private boolean _isCurrentlyOnMainThread() {
189+
return Looper.myLooper() == Looper.getMainLooper();
190+
}
191+
192+
private class LogAdapter
193+
extends ArrayAdapter<String> {
194+
195+
public LogAdapter(Context context, List<String> logs) {
196+
super(context, R.layout.item_log, R.id.item_log, logs);
197+
}
198+
}
199+
}

app/src/main/java/com/morihacky/android/rxjava/MainFragment.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,14 @@ public void demoThrottling() {
5757
.commit();
5858
}
5959

60+
@OnClick(R.id.btn_demo_subject_timeout)
61+
public void demoTimeout() {
62+
getActivity().getSupportFragmentManager()
63+
.beginTransaction()
64+
.addToBackStack(this.toString())
65+
.replace(R.id.activity_main,
66+
new DemoTimeoutFragment(),
67+
this.toString())
68+
.commit();
69+
}
6070
}
Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,43 @@
1-
<LinearLayout
2-
android:orientation="vertical"
1+
<ScrollView
32
android:layout_height="match_parent"
43
android:layout_width="match_parent"
5-
android:paddingTop="@dimen/activity_vertical_margin"
6-
android:paddingBottom="@dimen/activity_vertical_margin"
7-
android:paddingLeft="@dimen/activity_horizontal_margin"
8-
android:paddingRight="@dimen/activity_horizontal_margin"
9-
xmlns:android="http://schemas.android.com/apk/res/android"
10-
xmlns:tools="http://schemas.android.com/tools"
11-
tools:context=".DemoActivity">
4+
xmlns:android="http://schemas.android.com/apk/res/android">
125

13-
<Button
14-
android:id="@+id/btn_demo_schedulers"
15-
android:layout_height="wrap_content"
6+
<LinearLayout
7+
android:orientation="vertical"
8+
android:layout_height="match_parent"
169
android:layout_width="match_parent"
17-
android:text="@string/btn_demo_schedulers"/>
10+
android:paddingTop="@dimen/activity_vertical_margin"
11+
android:paddingBottom="@dimen/activity_vertical_margin"
12+
android:paddingLeft="@dimen/activity_horizontal_margin"
13+
android:paddingRight="@dimen/activity_horizontal_margin"
14+
xmlns:android="http://schemas.android.com/apk/res/android"
15+
xmlns:tools="http://schemas.android.com/tools"
16+
tools:context=".DemoActivity">
1817

19-
<Button
20-
android:id="@+id/btn_demo_buffer"
21-
android:layout_height="wrap_content"
22-
android:layout_width="match_parent"
23-
android:text="@string/btn_demo_buffer"/>
18+
<Button
19+
android:id="@+id/btn_demo_schedulers"
20+
android:layout_height="wrap_content"
21+
android:layout_width="match_parent"
22+
android:text="@string/btn_demo_schedulers"/>
2423

25-
<Button
26-
android:id="@+id/btn_demo_subject_debounce"
27-
android:layout_height="wrap_content"
28-
android:layout_width="match_parent"
29-
android:text="@string/btn_demo_subject_debounce"/>
24+
<Button
25+
android:id="@+id/btn_demo_buffer"
26+
android:layout_height="wrap_content"
27+
android:layout_width="match_parent"
28+
android:text="@string/btn_demo_buffer"/>
29+
30+
<Button
31+
android:id="@+id/btn_demo_subject_debounce"
32+
android:layout_height="wrap_content"
33+
android:layout_width="match_parent"
34+
android:text="@string/btn_demo_subject_debounce"/>
35+
36+
<Button
37+
android:id="@+id/btn_demo_subject_timeout"
38+
android:layout_height="wrap_content"
39+
android:layout_width="match_parent"
40+
android:text="@string/btn_demo_timeout"/>
41+
</LinearLayout>
3042

31-
</LinearLayout>
43+
</ScrollView>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<LinearLayout
4+
android:orientation="vertical"
5+
android:layout_height="match_parent"
6+
android:layout_width="match_parent"
7+
xmlns:android="http://schemas.android.com/apk/res/android">
8+
9+
<TextView
10+
android:layout_height="wrap_content"
11+
android:layout_width="match_parent"
12+
android:padding="10dp"
13+
android:gravity="center"
14+
android:text="@string/msg_demo_timeout"/>
15+
16+
<LinearLayout
17+
android:layout_height="wrap_content"
18+
android:layout_width="match_parent">
19+
20+
<Button
21+
android:id="@+id/btn_demo_timeout_1_2s"
22+
android:layout_weight="1"
23+
android:layout_height="wrap_content"
24+
android:layout_width="0dip"
25+
android:text="Button 1"/>
26+
27+
<Button
28+
android:id="@+id/btn_demo_timeout_1_5s"
29+
android:layout_weight="1"
30+
android:layout_height="wrap_content"
31+
android:layout_width="0dip"
32+
android:text="Button 2"/>
33+
34+
</LinearLayout>
35+
36+
<ListView
37+
android:id="@+id/list_threading_log"
38+
android:layout_height="match_parent"
39+
android:layout_width="match_parent"/>
40+
</LinearLayout>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
<string name="msg_demo_concurrency_schedulers">This is a demo of how long running operations can be offloaded to a background thread. After the operation is done, we resume back on the main thread. All using RxJava! \n\n To really see this shine. Hit the button multiple times and see how the button click which is a ui operation is never blocked because the long operation only runs in the background</string>
99
<string name="msg_demo_buffer">This is a demo of how events can be accumulated using the "buffer" operation. Tap the button below repetitively and you will notice in the logs that button taps are collected over a span of 2s and printed below.</string>
1010
<string name="msg_demo_subject">As you type in the input box, it will not shoot out log messages at every single input character change, but rather only pick the lastly emitted event (i.e. input) and log that. \n\nThis is the debounce/throttleWithTimeout method in RxJava.</string>
11+
<string name="msg_demo_timeout">This is a demo of terminating events, that take too long to process. Events in this demo should timeout in 3 seconds. Button 1 is an event that takes 2s to process, and Button 2 is a n event that takes 5s to process. Hit Button 2, and see in the logs that it\'s been cancelled, while this is not the case for Button 1.</string>
1112

1213
<string name="btn_demo_schedulers">bg work (schedulers &amp; concurrency)</string>
1314
<string name="btn_demo_buffer">accumulate calls (buffer)</string>
1415
<string name="btn_demo_subject_debounce">search text listener(subject debouncing)</string>
16+
<string name="btn_demo_timeout">delayed jobs (timeout)</string>
1517

1618
</resources>

0 commit comments

Comments
 (0)