Reactive flow Java 9 Flow in practice

4 interfaces of Flow

1. Publisher (data release)

    public static interface Publisher<T> {
        public void subscribe(Subscriber<? super T> subscriber);
    }
  • subscribe: receiving subscribers, data streamers

2. Subscriber (terminal operation)

  • onSubscribe: After the subscriber is received by the publisher, notify the subscriber that the subscription is successful
  • onNext: data flow Sink
  • onError: the flow direction when an error occurs
  • onComplete: the flow direction when the data is executed
    public static interface Subscriber<T> {
        public void onSubscribe(Subscription subscription);
        public void onNext(T item);
        public void onError(Throwable throwable);
        public void onComplete();
    }

3. Subscription subscription (data request, cancellation)

  • request: Request data + number to solve data congestion
  • cancel: cancel the subscription, the data is no longer needed
    public static interface Subscription {
        public void request(long n);
        public void cancel();
    }

4. Processor processor (intermediate operation)

  • Both Subscriber and Publisher, consume and deliver data
public static interface Processor<T,R> extends Subscriber<T>, Publisher<R> {
}

Publisher-Processor-Subscriber operation process

  • Green: subscription process, Subscriber-Publisher upward relationship
  • Black: SUBSCRIBE SUCCESSFUL, Publisher-Subscriber Downward Relationship
  • Blue: data request, Subscriber-Publisher up relationship
  • Red: data distribution, Publisher-Subscriber downward relationship

Example: complete a series of actions, data delivery -> filter by condition -> data conversion -> data consumption

publisher

  • Receive Spliterator and complete data delivery (T)
public class FlowPublisher<T> implements Flow.Publisher<T>, Flow.Subscription {

    private final Spliterator<T> spliterator;
    private Flow.Subscriber<? super T> subscriber;
    private boolean completed = false;
    private boolean canceled = false;

    public FlowPublisher(@NonNull Spliterator<T> spliterator) {
        this.spliterator = spliterator;
    }

    @Override
    public void subscribe(Flow.Subscriber<? super T> subscriber) {
        this.subscriber = subscriber;
        this.subscriber.onSubscribe(this);
    }

    @Override
    public void request(long n) {
        //已完成或已取消,则不再下发消息
        if(completed || canceled) {
            return;
        }
        //数据消息下发完,则下发onComplete消息
        long size = spliterator.getExactSizeIfKnown();
        if(size == 0) {
            completed = true;
            subscriber.onComplete();
            return;
        }
        //下发数据消息
        for(long i=Math.min(n, size); i>0; i--) {
            spliterator.tryAdvance(subscriber::onNext);
        }
    }

    @Override
    public void cancel() {
        canceled = true;
        //取消后下发onComplete消息
        if(!completed) {
            subscriber.onComplete();
        }
    }
}

Processor: filter operation

  • Receive Predicate and complete data filtering (T->T). If the data is filtered, continue to request upstream until the number of downstream requests is satisfied or the distribution is completed
public class FilterProcessor<T> implements Flow.Processor<T, T>, Flow.Subscription {

    private final Predicate<T> predicate;
    private Flow.Subscriber<? super T> subscriber;
    private Flow.Subscription subscription;
    private long requestCount;
    private long leftCount;

    public FilterProcessor(@NonNull Predicate<T> predicate) {
        this.predicate = predicate;
    }

    @Override
    public void subscribe(Flow.Subscriber<? super T> subscriber) {
        this.subscriber = subscriber;
        this.subscriber.onSubscribe(this);
    }

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        this.subscription = subscription;
    }

    @Override
    public void onNext(T item) {
        requestCount--;
        if(predicate.test(item)) {
            subscriber.onNext(item);
        } else {
            leftCount++;
        }
        if(requestCount == 0 && leftCount > 0) {
            request(leftCount);
        }
    }

    @Override
    public void onError(Throwable throwable) {
        subscriber.onError(throwable);
    }

    @Override
    public void onComplete() {
        subscriber.onComplete();
    }

    @Override
    public void request(long n) {
        leftCount = 0;
        requestCount = n;
        subscription.request(n);
    }

    @Override
    public void cancel() {
        subscription.cancel();
    }
}

Processor: mapping operation

  • Receive Function and complete data conversion (P->N)
public class MappingProcessor<P, N> implements Flow.Processor<P, N> {

    private final Function<P, N> function;
    private Flow.Subscriber<? super N> subscriber;
    private Flow.Subscription subscription;

    public MappingProcessor(@NonNull Function<P, N> function) {
        this.function = function;
    }

    @Override
    public void subscribe(Flow.Subscriber<? super N> subscriber) {
        this.subscriber = subscriber;
        this.subscriber.onSubscribe(subscription);
    }

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        this.subscription = subscription;
    }

    @Override
    public void onNext(P item) {
        subscriber.onNext(function.apply(item));
    }

    @Override
    public void onError(Throwable throwable) {
        subscriber.onError(throwable);
    }

    @Override
    public void onComplete() {
        subscriber.onComplete();
    }
}

Subscriber:TerminalOp

  • Receive Consumer, complete consumption (T)
public class FlowSubscriber<T> implements Flow.Subscriber<T> {

    private final Consumer<T> consumer;
    private Flow.Subscription subscription;
    private boolean completed = false;
    private long requestCount;
    private static final long REQUEST_COUNT = 3;

    public FlowSubscriber(@NonNull Consumer<T> consumer) {
        this.consumer = consumer;
    }

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        requestCount = REQUEST_COUNT;
        this.subscription = subscription;
        this.subscription.request(requestCount);
    }

    @Override
    public void onNext(T item) {
        requestCount--;
        consumer.accept(item);
        if(requestCount == 0 && !completed) {
            requestCount = REQUEST_COUNT;
            subscription.request(requestCount);
        }
    }

    @Override
    public void onError(Throwable throwable) {

    }

    @Override
    public void onComplete() {
        requestCount = 0;
        completed = true;
    }
}

FlowHelper

  • Connect FlowPublisher, FilterProcessor, MappingProcessor, and FlowSubscriber in series to complete the chain operation
public class FlowHelper<T> {

    private Flow.Publisher<T> publisher;

    public static <E> FlowHelper<E> flow(@NonNull Spliterator<E> spliterator) {
        return new FlowHelper<>(new FlowPublisher<>(spliterator));
    }

    private FlowHelper(@NonNull Flow.Publisher<T> publisher) {
        this.publisher = publisher;
    }

    public FlowHelper<T> filter(@NonNull Predicate<T> predicate) {
        FilterProcessor<T> p = new FilterProcessor<>(predicate);
        publisher.subscribe(p);
        publisher = p;
        return this;
    }

    public <R> FlowHelper<R> mapping(@NonNull Function<T, R> function) {
        MappingProcessor<T, R> p = new MappingProcessor<>(function);
        publisher.subscribe(p);
        return new FlowHelper<>(p);
    }

    public void accept(@NonNull Consumer<T> consumer) {
        FlowSubscriber<T> s = new FlowSubscriber<>(consumer);
        publisher.subscribe(s);
    }
}

FlowTest test

  1. Data Generation Spliterator
  2. Data filtering filter: filter out non-numeric strings
  3. Data conversion mapping: String ---> FlowBean
  4. Data consumption: output Log, display result: 13689
public class FlowTest {

    private final Pattern pattern;

    public FlowTest() {
        pattern = Pattern.compile("[0-9]*");
    }

    public void testFlow() {
        List<String> list = List.of("1", "2a", "3", "4b", "5c", "6", "7d", "8", "9");

        FlowHelper.flow(list.spliterator())
                .filter(v -> pattern.matcher(v).matches())
                .mapping(FlowBean::new)
                .accept(FlowBean::print);
    }

    static class FlowBean {

        final String result;

        public FlowBean(String result) {
            this.result = result;
        }

        public void print() {
            Log.d("FlowTest", result);
        }
    }
}

Guess you like

Origin blog.csdn.net/zhiyuan263287/article/details/124918753