The polling function is very common. The most common way in Android is to implement it through Handler, sending a Deley message, and then sending the message according to the condition in The handlerMessage. This method needs to be careful of memory leakage, and you need to deal with this problem yourself. That’s not our focus today, but let’s look at another way to implement polling, using RxJava. It has the following two characteristics:
1 Automatically unpolling and subscription relationships, without the risk of memory leakage; 2 Can be bound to the Activity or Fragment life cycle to automatically stop polling
RxJava
1.Subject
2.takeUntil
3 filter
4 compose
Refer to my previous blog RxJava partial operator parsing
1. Use
There are two ways to use it
BindIntervalEvent is a binding event that polling will be stopped when the event occurs. BindLifeCycle is a binding lifecycle that polling will be stopped when the specified life cycle occurs
FragmentEvent corresponds to the Fragment life cycle, ActivityEvent corresponds to the Activity life cycle, and BizEvent corresponds to our user-defined events:
INTERVAL is the periodic event generated by the INTERVAL operator in RxJava. You can set the polling INTERVAL. TIMER is the corresponding TIMER event. You can specify how long an event will be generated after the event. STOP is the STOP event, this is custom; ALL Matches ALL events.
public interface Event {
enum FragmentEvent implements EventInterface{
ATTACH,
CREATE,
CREATE_VIEW,
START,
RESUME,
PAUSE,
STOP,
DESTROY_VIEW,
DESTROY,
DETACH
}
enum ActivityEvent implements EventInterface{
CREATE,
START,
RESUME,
PAUSE,
STOP,
DESTROY
}
enum BizEvent implements EventInterface{
INTERVAL,
TIMER,
STOP,
ALL
}
}
Copy the code
Add two buttons to your Activity, one to start polling and one to stop polling.
private static final String TAG = MainActivity.class.getSimpleName() + "_POLLING"; / / open the polling PollingManager getInstance () bindIntervalEvent (1, TAG, Event. BizEvent. INTERVAL, null); / / stop polling PollingManager getInstance () stopPolling (TAG, Event. BizEvent. The INTERVAL);Copy the code
Take a look at the log print and stop polling after receiving the INTERVAL event.
bindIntervalEvent.PNG
Moving on to the second use, there are two steps:
1. Inherit BaseActivity, in which LifeInterface needs to be implemented by itself
public abstract class BaseActivity extends Activity implements LifeInterface
public interface LifeInterface {
void bindLife();
String getTag();
}
Copy the code
2. Implement two LifeInterface methods in the Activity that requires polling, as shown in the following example:
@Override
public String getTag() {
return TAG;
}
@Override
public void bindLife() {
PollingManager.getInstance().bindLifeCycle(getTag(), Event.ActivityEvent.PAUSE);
}
Copy the code
This example listens for PAUSE events and stops polling when the Activity enters onPause.
bindLifeCycle.PNG
That’s exactly what we expected. Let’s look at the code implementation.
2.PollingManager
The main logic is in PollingManager, which is the facade of the tool, somewhat similar to the facade mode.
The first is the singleton pattern, where the activeSubjectMap is the repository of Subjects, where all registered polling Models are stored.
private HashMap<String, SubjectModel<EventInterface>> activeSubjectMap;
private static PollingManager manager;
private PollingManager() {
activeSubjectMap = new HashMap<>();
}
public static PollingManager getInstance() {
if (null == manager) {
synchronized (PollingManager.class) {
if (null == manager) {
manager = new PollingManager();
}
}
}
return manager;
}
Copy the code
To illustrate the polling Model above, each Model encapsulates the poller, RxJava subscription relationships Disposable and Subject. Disposable is used to unsubscribe when polling stops to prevent memory leaks.
//Subject private BehaviorSubject<T> behaviorSubject; // Subscription relationship private Disposable Disposable; Private PollingRequest PollingRequest; public void clearSubject(){ if (null == disposable || disposable.isDisposed()) return; disposable.dispose(); }Copy the code
Each PollingRequest interface is called execute, where doAction is invoked on each poll.
Public abstract class PollingRequest {// The unique identifier of each Subject is protected String tag; // EventInterface protected EventInterface; // Protected PollingAction PollingAction; public PollingRequest(String tag, EventInterface eventInterface, PollingAction pollingAction) { this.tag = tag; this.eventInterface = eventInterface; this.pollingAction = pollingAction; } public abstract Disposable execute(PollingManager pollingManager); public String getTag() { return tag; } public EventInterface getEventInterface() { return eventInterface; } } public interface PollingAction { void doAction(Object accept); }Copy the code
IntervalPolling is implemented with a simple logic, that is, polling at every inteVALS interval will call doAction to complete the action.
public class IntervalPolling extends PollingRequest { private int inteval; public IntervalPolling(int interval, String tag, EventInterface eventInterface, PollingAction action) { super(tag, eventInterface, action); this.inteval = interval; } @Override public Disposable execute(PollingManager manager) { return Observable.interval(inteval, TimeUnit.SECONDS). compose(manager.composeEvent(tag, eventInterface)). observeOn(AndroidSchedulers.mainThread()). doOnNext(new Consumer<Long>() { @Override public void accept(Long aLong) throws Exception { Timber.tag(Constants.TAG).d("emit interval polling, Tag = " + tag + ", num = " + aLong); } }). subscribe(new Consumer<Long>() { @Override public void accept(Long num) throws Exception { if (null ! = pollingAction) { pollingAction.doAction(num); } Timber.tag(Constants.TAG).d("running interval polling, Tag = " + tag + ", num = " + num); }}); }}Copy the code
The perhaps convoluted logic above is this line:
compose(manager.composeEvent(tag, eventInterface))
Copy the code
Call the composeEvent method in PollingManager and follow along:
public ObservableTransformer<Long, Long> composeEvent(final String tag, final EventInterface outEvent) { BehaviorSubject<EventInterface> subject = getSubject(tag); if (null == subject) { Timber.tag(Constants.TAG).e("subject = null"); return new EmptyObservableTransformer(); } final Observable observable = subject.filter(new Predicate<EventInterface>() { @Override public boolean test(EventInterface event) throws Exception { Timber.tag(Constants.TAG).i("receive event: %s", event); boolean filter = outEvent == event || event == ALL; if (filter) clearSubject(tag); return filter; }}); return new ObservableTransformer<Long, Long>() { @Override public ObservableSource<Long> apply(Observable<Long> upstream) { return upstream.subscribeOn(Schedulers.io()).takeUntil(observable); }}; }Copy the code
The first is the takeUntil operator, which stops polling when the Subject sends data. When does the Subject send data? When the subject.filter returns true. The Subject will return true based on whether the events received are equal to those received at the time of subscription, or if the events received are ALL.
In fact, the above logic needs to have a certain understanding of RxJava, this is not the scope of this article, friends to consult on the Internet.
With the poller, Model, and trigger conditions in place, it’s just a matter of creating start and destroy. Let’s look at creating.
3. Create and start
Let’s look at the first binding event creation method:
public BehaviorSubject<EventInterface> bindIntervalEvent(int interval, @NonNull String tag, @NonNull EventInterface eventInterface, PollingAction action){ //1. IntervalPolling = new IntervalPolling(interval, tag, eventInterface, action); //2. CreateSubject createSubject(intervalPolling); //3. Start the polling startPolling(tag); //4. Return Subject Return ActivesubjectMap.get (tag).getBehaviorSubject(); }Copy the code
The second step will cache the Subject and poller into HashMap
> activeSubjectMap. Key is the unique identification tag of Subject.
The life cycle is created in the same four steps, except that the poller is the life cycle poller.
public BehaviorSubject<EventInterface> bindLifeCycle(@NonNull String tag,@NonNull EventInterface eventInterface){ //1. PollingRequest request = new LifePolling(tag, eventInterface, null); //2. CreateSubject createSubject(request); //3. Start the polling startPolling(tag); //4. Return Subject Return ActivesubjectMap.get (tag).getBehaviorSubject(); }Copy the code
Now that the analysis is done, let’s see how to stop polling.
4. Stop
In fact, the stopping logic is to send the event to the Subject, so that the Subject can receive it by itself, and then enter the logic of Filter for judgment. If the event is consistent with the registered event or ALL event when it is created, the polling will be stopped.
public boolean stopPolling(String tag, EventInterface event) {
BehaviorSubject<EventInterface> subject = getSubject(tag);
if (null == subject) {
Timber.tag(Constants.TAG).e("can not find subject according to the %s", tag);
return false;
}
subject.onNext(event);
Timber.tag(Constants.TAG).i("Stop Polling SubjectTag = " + tag + ", Event = " + event.toString());
return true;
}
Copy the code
One final addition is the launch event logic, which scans all the Subjects in the activeSubjectMap and fires the event:
public void emitEvent( @NonNull EventInterface event){ if (null == activeSubjectMap) return; for (Map.Entry<String, SubjectModel<EventInterface>> next : activeSubjectMap.entrySet()) { BehaviorSubject<EventInterface> behaviorSubject = next.getValue().getBehaviorSubject(); if (null == behaviorSubject) return; behaviorSubject.onNext(event); }}Copy the code
5. To summarize
Here the basic logic involved are finished analysis, I hope to provide another way to achieve polling home, if there is any question, welcome to leave a message, thank you!
My blog home juexingzhe welcome to follow you.
I wish you all a happy code.