I'm building an Android app, and new to Rxjava, having a beginner's question:
How can i subscribe to an event on activity starts, and close the subscription on activity stops?
I confused how to implement it.
You have to create an instance of PublishSubject and subscribe to it. and when you want to send and event you need to call onNext() of the PublishSubject instance.
Note: you have to hold one instance for each type of subscriptions.
First of all you must create EventSubscription class to hold specific PublishSubject instance:
EventSubscription.java:
public class EventSubscription {
private static EventSubscription instance;
private PublishSubject<String> subject = PublishSubject.create();
/**
* Singleton
*/
public static EventSubscription getInstance() {
if (instance == null) {
instance = new EventSubscription();
}
return instance;
}
/**
* Pass any event down to event listeners.
*/
public void setString(String eventName) {
subject.onNext(eventName);
}
/**
* Subscribe to this Observable. On event
*/
public Observable<String> getEvents() {
return subject;
}
}
And in your activity you also need to hold a subscription instance to be able to unsubscribe it on activity stop. see the following activity class:
public class MyActivity extends Activity {
private Subscription mMySubscription;
#Override
public void onStart(){
// Start the subscription
mMySubscription = EventSubscription.getInstance().getEvents()
.subscribe(new Action1<String>() {
#Override
public void call(String eventName){
// Handle new event
}
});
}
#Override
public void onStop(){
if(mMySubscription != null && ! mMySubscription.isUnsubscribed()){
// if your subscription already unsubscribed
mMySubscription.unsubscribe();
}
}
}
Related
I am looking to unsubscribe the Rx Singles after the operation.
This is how I have unsubscribed Observable in RxJava.
public class BooksRepositoryImpl implements IBooksRepository {
private ReplaySubject<Book> bookSubject;
private Subscription bookSubscription;
BooksApi BooksApi;
ISchedulerProvider scheduler;
#Inject
public BooksRepositoryImpl(BooksApi BooksApi, ISchedulerProvider scheduler) {
this.BooksApi = BooksApi;
this.scheduler = scheduler;
}
#Override
public Observable<Book> getBooks(String author) {
if (bookSubscription == null || bookSubscription.isUnsubscribed()) {
bookSubscription = ReplaySubject.create();
bookSubscription = BooksApi.getBooks(author)
.subscribeOn(scheduler.backgroundThread());
}
return bookSubscription.asObservable();
}
#Override
public void unbind() {
if (bookSubscription != null && !bookSubscription.isUnsubscribed())
bookSubscription.unsubscribe();
}
}
But I am not sure how to unsubscribe the Single Observable.
It would be great if someone can able to share some idea.
public class BooksRepositoryImpl implements IBooksRepository {
BooksApi BooksApi;
ISchedulerProvider scheduler;
#Inject
public BooksRepositoryImpl(BooksApi BooksApi, ISchedulerProvider scheduler) {
this.BooksApi = BooksApi;
this.scheduler = scheduler;
}
#Override
public Single<Book> getBooks(String author) {
return BooksApi.getBooks(author)
.subscribeOn(scheduler.backgroundThread());
}
#Override
public void unbind() {
//?? HOW Can I unsubscribe here
}
}
According to the documentation Single, you should do nothing, the subscription end by itself:
A Single will call only one of these methods, and will only call it
once. Upon calling either method, the Single terminates and the
subscription to it ends.
Hope this helps.
Sorry for my english.
I am using the Model-View-Presenter design pattern coupled with an EventBus (Otto). The entire reason I implemented this pattern is to decouple events to the presenter only, and have the presenter update the views.
This is an example of some of the code I have, I'll use getting Events as an example. (Please note that Events is different from the EventBus Event, meaning an Event in Events is an event like "Dad's Birthday", but an Event in the EventBus is a Bus-event).
Fragment
public class EventFragment extends Fragment {
private EventPresenter mEventPresenter;
// Initialize boilerplate code...
private void init() {
mEventPresenter = new EventPresenter();
mEventPresenter.loadEvents();
}
// I WANT TO MOVE THESE SUBSCRIPTION METHODS TO
// MY PRESENTER OR SUBSCRIBER, BUT THEY ARE
// COUPLED TO THE ADAPTER OR A VIEW
#Subscribe
public void onEventsLoaded(EventsLoaded eventsLoaded) {
List<Event> events = eventsLoaded.getEvents();
mAdapter.setEvents(events);
}
#Subscribe
public void onEventJoined(EventJoined eventJoined) {
mItemView.doAnimation();
mTextView.setText("Leave");
mAdapter.joinEvent(eventJoined.getEvent());
}
#Subscribe
public void onLeaveEvent(LeftEvent leftEvent) {
mItemView.doAnimation();
mTextView.setText("Join");
mAdapter.leftEvent(leftEvent.getEvent());
}
}
Presenter
public class EventPresenter {
// This is the only method I have right now kind of defeats the purpose of
// having a presenter
public void loadEvents() {
EventBus.getInstance().post(new LoadEvents());
}
}
Subscriber
public class EventSubscriber extends Subscriber {
// This class is registered on the event bus
#Subscribe
public void onLoadEvents(LoadEvents loadEvents) {
sClient.getEvents(new Callback<List<Event>>() {
#Override
public void onSuccess(List<Event> events, Response response) {
EventBus.post(new EventsLoaded(events));
}
#Override
public void onFailure(.....) {
// Handle failure
}
};
}
}
How can I get the Presenters and the Subscribers to handle all the business logic, and have the Fragment only handle views?
This is not the best possibility but May Be help you out.
subscribing events in presenter , with an instance of view inside it can help. Like presenter gets events from eventbus and call appropriate view method to update the UI.
Mind that I have not changed method names of your fragment that updates UI and have passed event object directly from presenter which is indeed now a subscriber of events of eventbus. You can change it accordingly.
Fragment---
public class EventFragment extends Fragment {
private EventPresenter mEventPresenter;
// Initialize boilerplate code...
#Override
public void onResume()
{
mEventPresenter.onResume();
}
#Override
public void onPause()
{
mEventPresenter.onPause();
}
private void init() {
mEventPresenter = new EventPresenter(this);
mEventPresenter.loadEvents();
}
// I WANT TO MOVE THESE SUBSCRIPTION METHODS TO
// MY PRESENTER OR SUBSCRIBER, BUT THEY ARE
// COUPLED TO THE ADAPTER OR A VIEW
public void onEventsLoaded(EventsLoaded eventsLoaded) {
List<Event> events = eventsLoaded.getEvents();
mAdapter.setEvents(events);
}
public void onEventJoined(EventJoined eventJoined) {
mItemView.doAnimation();
mTextView.setText("Leave");
mAdapter.joinEvent(eventJoined.getEvent());
}
public void onLeaveEvent(LeftEvent leftEvent) {
mItemView.doAnimation();
mTextView.setText("Join");
mAdapter.leftEvent(leftEvent.getEvent());
}
}
Presenter----
public class EventPresenter {
private EventFragment targetView;
public EventPresenter(EventFragment myView)
{
targetView = myView;
}
// This is the only method I have right now kind of defeats the purpose of
// having a presenter
public void loadEvents() {
EventBus.getInstance().post(new LoadEvents());
}
#Subscribe
public void onEventsLoaded(EventsLoaded eventsLoaded) {
targetView.onEventsLoaded(eventsLoaded);
}
#Subscribe
public void onEventJoined(EventJoined eventJoined) {
targetView.onEventJoined(eventJoined);
}
#Subscribe
public void onLeaveEvent(LeftEvent leftEvent) {
targetView.onLeaveEvent(leftEvent);
}
public void onResume()
{
//subscride event bus
}
public void onPause()
{
//unsubscride event bus
}
}
This is responsability to presenters to send or receive events and after to notify fragment or activities
An Activity contains a Fragment which in turn contains a child Fragment, which requests a Service. The app tries to implement dobjanschi rest architecture.
When the Service is done working it has to propagate operation result. I tried using a PendingIntent but it seems to only be caught by the activity, while I need the child fragment to get notified. Could you suggest anything? Binder? greenRobot Eventbus? RxJava (which I already have in the project)?
Thanks.
RxJava
A simple way con be to use a Singleton to wrap a synchronized ´PublishSubject´
* Singleton
*
* to send an event use EventBusRx.getInstance().topic1.onNext("completed");
*/
public class EventBusRx {
private static EventBusRx ourInstance = new EventBusRx();
public static EventBusRx getInstance() {
return ourInstance;
}
private EventBusRx() {}
/**
* Use of multiple topics can be usefull
* SerializedSubject avoid concurrency issues
*/
public final Subject<String, String> topic1 = new SerializedSubject<>(PublishSubject.create());
public final Subject<Integer, Integer> topic2 = new SerializedSubject<>(PublishSubject.create());
}
And You can send events from service
EventBusRx.getInstance().topic1.onNext("completed");
and respond to event in fragments or whenever you want
public class MyFragment extends Fragment {
// [...]
Subscription subscription_topic1;
#Override
public void onResume() {
super.onResume();
subscription_topic1 = EventBusRx.getInstance().topic2
.subscribeOn(AndroidSchedulers.mainThread()) // or on other sheduler
.subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
// update ui
}
});
}
#Override
public void onPause() {
// important to avoid memory leaks
subscription_topic1.unsubscribe();
super.onPause();
}
}
do not forget to unsubcribe the Subscription
The idea is similar to Roger'one use a singleton but enforce ThreadSafety wrapping PublishSubject.
there is no need for Observable.switchOnNext(subject)
EventBus Libraries
greenRobot Eventbus and Otto are nice and has the same functionality, but the disadvantage is that they make the connection more smoky (expecialy EventBus) . If you already use rx i think is better to stay with it
Here is an insipring article about the topic
Implementing an Event Bus With RxJava
LocalBroadcast
The classic way to do this is to use LocalBroadcastManager but in my aopinion they are a pain
I would suggest using an Event Bus for this sort of thing. It will allow you to send messages to components within your system, without requiring creating special handlers.
Otto is a popular open source library for this, and there are others. http://square.github.io/otto/
Try this way hope it help you.
For Example:
YourService
public class MyService extends Service{
public static MyServiceListener getMyServiceListener() {
return MyService.myServiceListener;
}
public static void setMyServiceListener(MyServiceListener myServiceListener) {
MyService.myServiceListener = myServiceListener;
}
private static MyServiceListener myServiceListener;
public interface MyServiceListener{
void onResult(String response);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
executeYourTask();
}
private void executeYourTask(){
String result = "SomeResultMaybeFromServer";
if(getMyServiceListener()!=null){
getMyServiceListener().onResult(result);
}
}
}
YourFragment
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = null; // some view
// Start service
MyService.setMyServiceListener(new MyService.MyServiceListener() {
#Override
public void onResult(String response) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// To handle memory/window leaks
}
});
}
});
return v;
}
}
I'm currently developing a Bus based solely on RxJava. Since you already have RxJava on your project, you can use it for this. You should use a BehaviorSubject and Observable.switchOnNext().
For example:
private BehaviorSubject<Observable<Whatever>> subject = BehaviorSubject.create();
public void post(){
subject.onNext(...);
}
public Observable<Whatever> subscribe(){
return Observable.switchOnNext(subject);
}
You should have this as part of a Singleton so the same BehaviorSubject is used. All you have to do is post() from one fragment and subscribe() on the other one or in any other interested fragment or activity. You can have as many subscriptions as you want, plus if you implement it correctly then the last emitted Observable will survive orientation changes.
More info on BehaviorSubject can be found here: https://github.com/ReactiveX/RxJava/wiki/Subject
I'm currently using this Pub/Sub pattern with rxjava and enum class.
public enum Events {
public static PublishSubject <Object> myEvent = PublishSubject.create ();
}
//where you want to publish something
Events.myEvent.onNext(myObject);
//where you want to receive an event
Events.myEvent.subscribe (...);
I would use event bus, which is based on rx.
Make this as a sigletone and subscribe on particular class type.
public class RxBus {
private static final RxBus sBus = new RxBus();
private final Subject<Object, Object> mBus = new SerializedSubject<>(PublishSubject.create());
private RxBus() {
}
public static RxBus getInstance() {
return sBus;
}
public void send(Object o) {
mBus.onNext(o);
}
public Observable<Object> observe() {
return mBus;
}
#SuppressWarnings("unchecked")
public <T> Observable<T> observe(Class<T> c) {
return mBus.filter(o -> c.isAssignableFrom(o.getClass())).map(o -> (T) o);
}
}
usage:
class Message { public String result};
send a message:
Message m = new Message();
m.result = "Hello world";
RxBus.getInstance().send(m);
subscribe on a particular class type:
RxBus.getInstance().observe(Message.class).subscribe(msg -> Log.e(TAG, "Message was caught : " + msg));
I'm looking at retrofit for my networking layer. Is there any way to tell if a particular async request is running at any given moment?
For example, I'd like to know if a request is running so that I can update the user interface at various times. I could do this myself by keeping variables around to track state, but wondering if there's something already in the library for this.
Here is what I would normally do when needing a way to keep track of running requests:
First, using retrofit, as soon as you make the request, you can do the following:
Use EventBus library to post an event to your activity or fragment. Now, this can be done inside onSuccess() method of your Callback or onError() method of the same.
In your activity or fragment's onEvent(EventClassName event) method, you can simply check a variable like [isRunning] from your event to make sure that if the event is still running, you update the UI accordingly and if not, do what you need to do respectively.
When the request is completed, obviously isRunning will be false and you can then update the UI as expected by the user.
I am recommending EventBus here simply because it is much easier to decouple your application code with it; you can send different events that notify the activity of the different statuses of your requests and then update your UI that way.
You can find EventBus here
I hope this helps!
What I personally ended up doing in this case was that I was running the example with Retrofit, Android Priority Jobqueue (from yigit's fork) and Otto eventbus.
public enum SingletonBus {
INSTANCE;
private Bus bus;
private Handler handler = new Handler(Looper.getMainLooper());
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public <T> void postToSameThread(final T event) {
bus.post(event);
}
public <T> void postToMainThread(final T event) {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
public <T> void register(T subscriber) {
bus.register(subscriber);
}
public <T> void unregister(T subscriber) {
bus.unregister(subscriber);
}
}
public interface Interactor {
void injectWith(PresenterComponent presenterComponent);
}
public interface SendCertificateRequestInteractor
extends Interactor {
interface Listener {
void onSuccessfulEvent(SuccessfulEvent e);
void onFailureEvent(FailureEvent e);
}
class SuccessfulEvent
extends EventResult<CertificateBO> {
public SuccessfulEvent(CertificateBO certificateBO) {
super(certificateBO);
}
}
class FailureEvent
extends EventResult<Throwable> {
public FailureEvent(Throwable throwable) {
super(throwable);
}
}
void sendCertificateRequest(String username, String password);
}
Pay attention to the Job here:
public class SendCertificateRequestInteractorImpl
implements SendCertificateRequestInteractor {
private Presenter presenter;
private boolean isInjected = false;
#Inject
public JobManager jobManager;
public SendCertificateRequestInteractorImpl(Presenter presenter) {
this.presenter = presenter;
}
#Override
public void sendCertificateRequest(String username, String password) {
if(!isInjected) {
injectWith(presenter.getPresenterComponent());
isInjected = true;
}
InteractorJob interactorJob = new InteractorJob(presenter, username, password);
long jobId = jobManager.addJob(interactorJob); //this is where you can get your jobId for querying the status of the task if you want
}
#Override
public void injectWith(PresenterComponent presenterComponent) {
presenterComponent.inject(this);
}
public static class InteractorJob
extends Job {
private final static int PRIORITY = 1;
private final static String TAG = InteractorJob.class.getSimpleName();
private String username;
private String password;
#Inject
public MyService myService;
public InteractorJob(Presenter presenter, String username, String password) {
super(new Params(PRIORITY).requireNetwork());
presenter.getPresenterComponent().inject(this);
this.username = username;
this.password = password;
}
#Override
public void onAdded() {
// Job has been saved to disk.
// This is a good place to dispatch a UI event to indicate the job will eventually run.
// In this example, it would be good to update the UI with the newly posted tweet.
}
#Override
public void onRun()
throws Throwable {
String certificate = myService.getCertificate(username, password);
SingletonBus.INSTANCE.postToMainThread(new SuccessfulEvent(certificate));
}
#Override
protected void onCancel() {
// Job has exceeded retry attempts or shouldReRunOnThrowable() has returned false.
Log.e(TAG, "Cancelled job.");
}
#Override
protected boolean shouldReRunOnThrowable(Throwable throwable) {
// An error occurred in onRun.
// Return value determines whether this job should retry running (true) or abort (false).
Log.e(TAG, "Failed to execute job.", throwable);
SingletonBus.INSTANCE.postToMainThread(new FailureEvent(throwable));
return false;
}
}
}
And then
#Subscribe
#Override
public void onSuccessfulEvent(SendCertificateRequestInteractor.SuccessfulEvent e) {
String certificate = e.getResult();
//do things
}
#Subscribe
#Override
public void onFailureEvent(SendCertificateRequestInteractor.FailureEvent e) {
Throwable throwable = e.getResult();
//handle error
}
More about android priority jobqueue here.
This way, technically the async handling is referred to the job queue, while Retrofit itself is using the synchronous interface. It works well as long as you don't need to access the headers of the response. Although to be fair, I was also keeping track of whether the job was running with a boolean instead of the job manager and the id as well..
Also, I haven't figured out how to use dependency injection properly with persisted jobs; nor do I really know how they intended to make that work. Of course, it'd work if it was using the application scoped component rather than a supplied presenter scoped one, but that is irrelevant.
You'll probably need to customize this solution to your own scenario, and use only what you actually need.
i've seen this thread : How to implement a listener about implement listeners.
its actually pretty simple but i don't get how exactly its done and how to implement in my own code.
i have this static variable variable: AppLoader.isInternetOn.
i want to build a listener which will listen to this variable changes and update a TextView.
should i do this: ?
build an interface:
public interface InternetStateListener {
public void onStateChange();
}
run it in my activity:
public class MyActivity extends Activity {
private InternetStateListener mListener;
setTheListener(this);
public void setTheListener(InternetStateListener listen) {
mListener = listen;
}
private void onStateChange() {
if (mListener != null) {
if (AppLoader.isInternetOn)
text.setText("on")
else
text.setText("off")
}
}
}
Your Activity does nothing special, just register itself (since the interface is implemented directly in the class) with the Other class that provides the listener.
public class MyActivity extends Activity implements InternetManager.Listener {
private TextView mText;
private InternetManager mInetMgr;
/* called just like onCreate at some point in time */
public void onStateChange(boolean state) {
if (state) {
mText.setText("on");
} else {
mText.setText("off");
}
}
public void onCreate() {
mInetMgr = new InternetManager();
mInetMgr.registerListener(this);
mInetMgr.doYourWork();
}
}
The other class has to do pretty much all the work. Besides that it has to handle the registration of listeners it has to call the onStateChange method once something happend.
public class InternetManager {
// all the listener stuff below
public interface Listener {
public void onStateChange(boolean state);
}
private Listener mListener = null;
public void registerListener (Listener listener) {
mListener = listener;
}
// -----------------------------
// the part that this class does
private boolean isInternetOn = false;
public void doYourWork() {
// do things here
// at some point
isInternetOn = true;
// now notify if someone is interested.
if (mListener != null)
mListener.onStateChange(isInternetOn);
}
}
The part that you're missing it the class that actually notifies the listener. So you would need a class (most likely a service) that runs and pings the state of the network. Then when it detects a change it should call onStateChange() in any registered listeners. Then you would call setTheListener on that service, not on your activity.
Here's a link that thoroughly describes this design pattern: http://en.wikipedia.org/wiki/Observer_pattern