面试官一套RxJava组合拳下来,我被问得怀疑人生

安卓开发实战讲解!一份非常适合收藏的Android进阶面试题!面试建议 前言 从去年底到前几天,不断被各路跳槽的朋友刺激着,他们都跟我说着同一件事:跳槽了,薪资翻倍了,去一家叫做字节跳动的公司了。 让我更加讶异的是一个在微博做移动开发的朋友跟我说他舍弃了年终奖,在春节之前就投入到这家公司的怀抱,这让我一度以为这个朋友是被传销了。 说实话,我内心早就被这些个朋友成功跳槽的消息撩拨的春心荡漾了,在这春色融融的夜晚我的代码也开始字节跳动了,今晚就跟大家分享下这些朋友们跳槽的故事。 架构师成长第一站—UML建模 1.正向工程与逆向工程在UML图中的应用 2.关系(依赖、泛化、关联、实 阅读详情

大家都知道RxJava上手是非常难的一个框架,为什么说是难呢,因为它的功能非常强大,各种操作符让人很难上手,搭配使用带生命周期的框架有RxLife等。以至于后面出了很多类似Rxjava的框架,有RxAndroid,我们用的RxJava切换主线程就是出自该框架,后面ACC架构中有LiveData、Lifecycle、包括后面协成中出来的flow异步流,都是向Rxjava靠拢,不过ACC出来的这些框架他们最大的特点就是操作简单,上手简单。所以RxJava强大的框架背后读懂源码是非常难的,因此本着扒开源码的想法,探索一些RxJava平时的疑惑。

在Rxjava里面有几个角色我们需要弄明白:

Observable:俗称被订阅者,被订阅者是事件的来源,接收订阅者(Observer)的订阅,然后通过发射器(Emitter)发射数据给订阅者。 Observer:俗称订阅者,注册过程传给被订阅者,订阅者监听开始订阅,监听订阅过程中会把Disposable传给订阅者,然后在被订阅者中的发射器(Emitter)发射数据给订阅者(Observer)。 Emitter:俗称发射器,在发射器中会接收下游的订阅者(Observer),然后在发射器相应的方法把数据传给订阅者(Observer)。 Consumer:俗称消费器,这是RxJava2.0才出来的,在RxJava1.0中用Action来表示,消费器其实是Observer的一种变体,Observer的每一个方法都会对应一个Consumer,比如Observer的onNext、onError、onComplete、onSubscribe都会对应一个Consumer。 Disposable:是释放器,通常有两种方式会返回Disposable,一个是在Observer的onSubscribe方法回调回来,第二个是在subscribe订阅方法传consumer的时候会返回。

这里以一种最基本的订阅来介绍它们之间的关系:

首先在①、②、③这几个过程中会形成一个从Observer到Observable订阅的过程,首先是下游的Observer发起Observable的subscribe方法,而该方法会调用到Observable的subscribeActual,并且会把下游的Observer传给该方法中,这就是上面的过程①。 过程②中会创建发射器,也就是各种Emitter对象,该发射器需要接受过程①中的Observer,也就是下游的观察者,接着给下游的Observer的添加订阅的监听,也就是onSubscribe方法,并且把Disposable传给onSubscribe方法,这里的Disposable其实是刚才创建的发射器,因为本身发射器也是实现了Disposable类型,所以下游的Observer在订阅监听中会收到上游创建的Disposable。 在过程②中做完了下游的Observer订阅监听后,继续向上一层的Observable添加订阅,也就是把②中创建的发射器或②中创建的Observer传给上游的Observable的subscribe方法。 完成了上面的从下游的observer到上游的Observable订阅的过程,接着就是④发射数据了,由于最上游的Observable会在subscribe方法中收到发射器,因此我们可以利用发射器把数据发送到下游的Observer,也就是onNext、onError、onComplete等方法。

注:该篇源码分析会基于3.0.7版本分析,如果看其他版本可以去官网看看

该篇文章会通过源码的形式介绍一下几点,以及总结面试过程中如何应答RxJava的问题

目录

  • RxJava怎么通过被订阅者传给订阅者的过程是什么样的?
  • Observer处理完onComplete后会还能onNext吗?
  • RxJava中map、flatMap的区别,你还用过其他哪些操作符?
  • Maybe、Single、Flowable、Completable几种观察者的区别,以及他们在什么场景用?
  • RxJava切换线程是怎么回事?
  • RxJava的subscribeOn只有第一次生效?
  • RxJava的observeOn多次调用哪个有效?
  • RxJava1.0、RxJava2.0、RxJava3.0有什么区别?
  • RxJava中背压是怎么回事?

RxJava怎么通过被订阅者传给订阅者的过程是什么样的?

通常我们写一个从订阅到发送数据的示例如下:

Observable.create(new ObservableOnSubscribe<Integer>() {

    @Override
    public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
        emitter.onNext(1);
        emitter.onNext(2);
        emitter.onNext(3);
        emitter.onComplete();
    }
}).subscribe(new Observer<Integer>() {
    @Override
    public void onSubscribe(@NonNull Disposable d) {
        Log.d(TAG, "onSubscribe:" + d.getClass().getName());
    }

    @Override
    public void onNext(@NonNull Integer integer) {
        Log.d(TAG, "onNext: " + integer);
    }

    @Override
    public void onError(@NonNull Throwable e) {
        Log.d(TAG, "onError: " + e.getMessage());
    }

    @Override
    public void onComplete() {
        Log.d(TAG, "onComplete");
    }
});

相信这是最简单的事件发送的示例,这没什么好说的,那它们是怎么发送数据,接收数据的呢,下面我会把代码拆分来看,因为现在是链式调用,我把代码拆分如下:

订阅过程

这里把创建observable和observer、以及发起订阅分别拆开来写,后面方便我们分析代码,首先是第一步发起订阅observable.subscribe(observer):

@Override
public final void subscribe(@NonNull Observer<? super T> observer) {
    try {
        subscribeActual(observer);
    } catch (NullPointerException e) { // NOPMD
        throw e;
    } catch (Throwable e) {
        NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
        throw npe;
    }
}

observable的订阅方法关键一句subscribeActual(observer),这里提一句,所有的被观察者的订阅入口都是subscribeActual方法,而subscribeActual在被观察者中是抽象方法,因此看对应的observable子类实现的逻辑,在上面通过Observable.create创建的被观察者是ObservableCreate,它是Observable的子类,我么需要明确,RxJava中的操作符都会对应一个Observable的子类,比如just操作符对应的是ObservableJust的被观察者,好了,我们看ObservableCreate的subscribeActual实现:

@Override
protected void subscribeActual(Observer<? super T> observer) {
    //创建发射器,并且把下游的observer给发射器
    CreateEmitter<T> parent = new CreateEmitter<>(observer);
    //给下游的observer添加被订阅的监听
    observer.onSubscribe(parent);
    try {
        //给上游的ObservableOnSubscribe添加订阅,并且把下游的observer给上游的ObservableOnSubscribe
        source.subscribe(parent);
    } catch (Throwable ex) {
        Exceptions.throwIfFatal(ex);
        parent.onError(ex);
    }
}

先是创建CreateEmitter类型的发射器,把下游的observer传给发射器,注意此处的发射器是实现了Disposable接口,所以紧接着会把发射器通过下游的观察者的onSubscribe方法传给下游观察者,注意此处传的是Disposable对象。接着会给上游的ObservableOnSubscribe添加订阅,并且把下游的observer给上游的ObservableOnSubscribe。 为了描述订阅的过程,我们画一张时序图:

  • 小总结

订阅是从下游的Observer向上游的Observable发送订阅,然后在订阅的过程中,给下游的Observer发送订阅监听,并且给上游的被观察者添加订阅。

发送数据

上面我们知道在ObservableCreate的subscribeActual方法中给上游的ObservableOnSubscribe添加了onSubscribe订阅过程,并且把当前的发射器传给了ObservableOnSubscribe,而在我们上面的示例中定义的ObservableOnSubscribe内部类的subscribe方法通过传过来的发射器添加了如下代码:

emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onComplete();

所以到这里可以看到是通过发射器的onNext和onComplete发送数据,而emitter是上面订阅过程传过来的CreateEmitter,所以直接看它的onNext和onComplete:

@Override
public void onNext(T t) {
    if (t == null) {
        onError(ExceptionHelper.createNullPointerException("onNext called with a null value."));
        return;
    }
    //如果isDisposed为false,则可以继续发送数据
    if (!isDisposed()) {
        observer.onNext(t);
    }
}

很简单,给observer发送数据,而当前的observer是订阅过程中传进来的下游observer,所以大家明白了吧,最终是下游的observer接收到数据。

  • 小总结

发送主要通过上游的被观察者通知发射器,然后发射器会发送给下游的observer。

Observer处理完onComplete后会还能onNext吗?

上面我们看到emitter.onNext三次完了后,会发送onComplete事件,那onComplete处理啥呢:

@Override
public void onComplete() {
    if (!isDisposed()) {
        try {
            observer.onComplete();
        } finally {
            dispose();
        }
    }
}

这是发射器中onComplete的定义,dispose方法是控制是否还能发送数据,其实这里的 CreateEmitter它是一个AtomicReference原子类包装Disposable的实现类,而我们dispose方法正是将该原子类添加了常量的DISPOSED,而在onNext方法中通过判断isDisposed是否为false才能继续发送数据。而isDisposed什么时候为false呢?当AtomicReference中的包装对象不是DISPOSED。所以我们的onComplete是用来控制不能发送数据的。

您可以通过如下代码测试:

emitter.onNext(1);
emitter.onNext(2);
emitter.onComplete();
emitter.onNext(3);

看看下游的observer是否还能收到3的数据。

小总结

onComplete是用来控制不能发送数据的,也就是不能onNext了,包括onError也是不能再发送onNext数据了,该方法中也是调用了dispose方法。

RxJava中map、flatMap的区别,你还用过其他哪些操作符?

map和flatMap是我们经常用的转换操作,我们先看看map如何使用:

Observable<Integer> createObservable = Observable.create(new ObservableOnSubscribe<Integer>() {

        @Override
        public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
            emitter.onNext(1);
            emitter.onNext(2);
            emitter.onNext(3);
            emitter.onComplete();
        }
    });
    Observable<String> mapObservable = createObservable.map(new Function<Integer, String>() {

        @Override
        public String apply(Integer integer) throws Throwable {
            return String.valueOf(integer + 1);
        }
    });
    Observer<String> observer = new Observer<String>() {
        @Override
        public void onSubscribe(@NonNull Disposable d) {
            Log.d(TAG, "onSubscribe:" + d.getClass().getName());
        }

        @Override
        public void onNext(@NonNull String string) {
            Log.d(TAG, "onNext: " + string);
        }

        @Override
        public void onError(@NonNull Throwable e) {
            Log.d(TAG, "onError: " + e.getMessage());
        }

        @Override
        public void onComplete() {
            Log.d(TAG, "onComplete");
        }
    };
    mapObservable.subscribe(observer);
}

通过createObservable的map操作生成了一个mapObservable的被观察者,最终通过mapObservable与observer形成订阅关系,而map操作需要一个Function的接口,第一个泛型是入参类型,第二个泛型是出参的类型,也就是apply的返回值,这里定义map的出参类型是String类型。 我们再来看下flatMap如何使用:

Observable<Integer> flatMapObservable = mapObservable.flatMap(new Function<String, ObservableSource<Integer>>() {
    @Override
    public ObservableSource<Integer> apply(String s) throws Throwable {
        return Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
                emitter.onNext(Integer.valueOf(s)+1);
                emitter.onComplete();
            }
        });
    }
});
flatMapObservable.subscribe(observer);

在上面的mapObservable基础上通过flatMap返回flatMapObservable,最后通过flatMapObservable订阅observer。flatMap的Function第二个泛型是ObservableSource类型的,Observable的父类是ObservableSource类型,因此第二个参数返回Observable也可以。

从上面可以看出map是通过原始数据类型返回另外一种数据类型,而flatMap是通过原始数据类型返回另外一种被观察者。

关于面试也有问flatMap和concatMap的区别,下面我通过一个例子来演示他们的区别:

Observable<String> createObservable = Observable.just("1", "2", "3", "4", "5", "6", "7", "8", "9");
Observable<Integer> flatMapObservable = createObservable.flatMap(new Function<String, ObservableSource<Integer>>() {
    @Override
    public ObservableSource<Integer> apply(String s) throws Throwable {
        if (s.equals("2")) {
            return Observable.create(new ObservableOnSubscribe<Integer>() {
                @Override
                public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
                    emitter.onNext(Integer.valueOf(s) + 1);
                    emitter.onComplete();
                }
            }).delay(500, TimeUnit.MILLISECONDS);
        } else {
            return Observable.create(new ObservableOnSubscribe<Integer>() {
                @Override
                public void subscribe(@NonNull ObservableEmitter<Integer> emitter) throws Throwable {
                    emitter.onNext(Integer.valueOf(s) + 1);
                    emitter.onComplete();
                }
            });
        }

    }
});
Observable<Integer> observeOnObservable = flatMapObservable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
Observer<Integer> observer = new Observer<Integer>() {
    @Override
    public void onSubscribe(@NonNull Disposable d) {
        Log.d(TAG, "onSubscribe:" + d.getClass().getName());
    }

    @Override
    public void onNext(@NonNull Integer string) {
        Log.d(TAG, "onNext: " + string);
    }

    @Override
    public void onError(@NonNull Throwable e) {
        Log.d(TAG, "onError: " + e.getMessage());
    }

    @Override
    public void onComplete() {
        Log.d(TAG, "onComplete");
    }
};
observeOnObservable.subscribe(observer);

在上面flatMap操作过程中为了演示flatMap和concatMap的区别,在数据为2的时候让返回的observable延迟500毫秒,我们看到的结果如下:

上面例子中3是由2的发射数据发射过来的,而正好数据为2的时候让延迟了500毫秒,那如果换成concatMap结果是按照发射数据的顺序来返回的。

concatMap和flatMap的功能是一样的, 将一个发射数据的Observable变换为多个Observables,然后将它们发射的数据放进一个单独的Observable。只不过最后合并ObservablesflatMap采用的merge,而concatMap采用的是连接(concat)。总之一句一话,他们的区别在于:concatMap是有序的,flatMap是无序的,concatMap最终输出的顺序与原序列保持一致,而flatMap则不一定,有可能出现交错。

关于其他的操作符比如merge、concat、zip都是合并,interval是周期执行,timer是延迟发送数据。如果要学习更多的操作符请自行前去官网

Maybe、Observer、Single、Flowable、Completable几种观察者的区别,以及他们在什么场景用?

其实想知道它们的区别,我们直接看对应的Observer的方法有哪些:

  • Maybe

Maybe从字面意思是可能的意思,看下MaybeObserver接口:

public interface MaybeObserver<@NonNull T> {  
    void onSubscribe(@NonNull Disposable d);
    void onSuccess(@NonNull T t);  
    void onError(@NonNull Throwable e);
    void onComplete();
}

它没有onNext方法,也就是说不能发多条数据,如果回调到onSuccess再不能发消息了,如果直接回调onComplete相当于没发数据,也就是说Maybe可能不发送数据,如果发送数据只会发送单条数据。

  • Observer

这个不用多说了,它是能发送多条数据的,直到发送onError或onComplete才不会再发送数据了,当然它也是可以不发送数据的,直接发送onError或onComplete。

  • Single
public interface SingleObserver<@NonNull T> {
    void onSubscribe(@NonNull Disposable d);
    void onSuccess(@NonNull T t);
    void onError(@NonNull Throwable e);
}

single也是发送单条数据,但是它要么成功要么失败。

  • Flowable

Flowable没有FlowableObserver接口,它是由FlowableSubscriber代表观察者,Flowable在后面被压的时候讲,我们只要知道它是被压策略的一个被观察者。

  • Completable
public interface CompletableObserver {
    void onSubscribe(@NonNull Disposable d);
    void onComplete();
    void onError(@NonNull Throwable e);
}

Completable不发送数据,只会发送成功或失败的事件,当然这个用得很少。

小总结

从上面各个对应的observer接口来看,如果只想发一条数据,或者不发数据就用Maybe,如果想发多条数据或者不发数据就用Observable,如果只发一条数据或者失败就用Single,如果想用背压策略使用Flowable,如果不发数据就用Completable。

RxJava切换线程是怎么回事?

大家都知道RxJava切换线程使用subscribeOn指定被观察者的在哪个线程执行,使用observeOn指定观察者在哪个线程执行,通常我们写法如下:

subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

subscribeOn(Schedulers.io())

image

subscribeOn会返回一个ObservableSubscribeOn,它是一个Observable,根据前面介绍的订阅流程,我们直接看ObservableSubscribeOn的subscribeActual操作:

@Override
public void subscribeActual(final Observer<? super T> observer) {
    //创建了内部的Observer,其实这里类似上面介绍的Observable.create创建的发射器,只不过发射器是Emitter
    final SubscribeOnObserver<T> parent = new SubscribeOnObserver<>(observer);
    //给下游的observer添加订阅的监听
    observer.onSubscribe(parent);
    //给SubscribeOnObserver设置disposable对象
    parent.setDisposable(scheduler.scheduleDirect(new SubscribeTask(parent)));
}

创建了SubscribeOnObserver对象,它是Observer类型的,其实类似上面介绍的Observable.create创建的发射器,只不过发射器是Emitter类型。接着给下游的observer添加订阅的监听,最后是给SubscribeOnObserver设置disposable对象,还记得在observable.create最后一步是给上游的ObservableOnSubscribe添加订阅吗,那我们看看此处是如果给上游的observable添加订阅的,首先scheduler是Schedulers.io(),最终它是一个IoScheduler对象,关于如何创建的线程池就不细说了,大家可以跟着IoScheduler创建过程可以看下,里面最终是通过SchedulerPoolFactory.create创建了线程池,创建线程池如下:

ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);

而scheduler.scheduleDirect(new SubscribeTask(parent))中的SubscribeTask是一个Runnable,所以最终通过线程池执行SubscribeTask的run方法:

到了最后还是通过线程池执行Runnable来添加上游Observable的订阅,并且把当前创建的SubscribeOnObserver传给了上游的observable,这个跟我们上面介绍Observable.create中给上游的ObservableOnSubscribe添加订阅是一样的。

  • 小总结

subscribeOn实际是创建了ObservableSubscribeOn的Observable,它的订阅方法里面创建了SubscribeOnObserver,通过线程池执行Runnable来达到上游Observable的订阅在子线程中执行,这就是为什么subscribeOn能控制observable在哪个线程中执行的原因。

observeOn(AndroidSchedulers.mainThread())

同样如此observeOn也会有对应的observable,它是ObservableObserveOn,我们直接看它订阅的方法:

同样如此,可以看到先是拿到AndroidSchedulers中的worker,它是HandlerWorker类型,按道理说应该给下游的observer添加订阅监听啊,怎么没有呢,看官别急,我们继续看ObserveOnObserver的订阅方法:

我们的重点不在下游的observer订阅监听这,在ObserveOnObserver的onNext方法中,会调用schedule方法,最终是通过HandlerWorker的schedule执行ObserveOnObserver,因为ObserveOnObserver也是一个runnable实现类,HandlerWorker中的schedule方法是通过主线程的Handler给主线程发送了一个Message,所以我们回到ObserveOnObserver的run方法,在run方法中会执行下游的onNext、onError等方法,所以这就是为什么observeOn能让observer能在主线程中执行。

  • 小总结

observeOn实际是创建了ObservableObserveOn的Observable,它的订阅方法里面创建了ObserveOnObserver,而ObserveOnObserver是实现了Runnable接口,把它包装成message给主线程的Handler发送一条消息,而ObserveOnObserver的run方法中会给下游的Observer发送数据。所以这就是observeOn能让observer在哪个线程中执行。

RxJava的subscribeOn只有第一次生效?

如果你理解了订阅的过程,其实该问题很好理解,subscribeOn是规定上游的observable在哪个线程中执行,如果我们执行多次的subscribeOn的话,从下游的observer到上游的observable的订阅过程,最开始调用的subscribeOn返回的observable会把后面执行的subscribeOn返回的observable给覆盖了,因此我们感官的是只有第一次的subscribeOn能生效。

那如何才能知道它实际在里面生效了呢,我们可以通过doOnSubscribe来监听切实发生线程切换了。

RxJava的observeOn多次调用哪个有效?

上面分析了observeOn是指定下游的observer在哪个线程中执行,所以这个更好理解,看observeOn下一个observer是哪一个,所以多次调用observeOn肯定是最后一个observeOn控制有效。

RxJava1.0、RxJava2.0、RxJava3.0有什么区别?

RxJava2.0相比于RxJava1.0

  • 添加背压的策略Flowable
  • 添加Observer的变体consumer
  • ActionN 和 FuncN 改名(Action0 改名成Action,Action1改名成Consumer,而Action2改名成了BiConsumer,而Action3 - Action9都不再使用了,ActionN变成了Consumer<Object[]> 。Func改名成Function,Func2改名成BiFunction,Func3 - Func9 改名成 Function3 - Function9,FuncN 由 Function<Object[], R> 取代。)
  • Observable.OnSubscribe 变成 ObservableOnSubscribe
  • ObservableOnSubscribe 中使用 ObservableEmitter 发射数据给 Observer,在RxJava中使用Subscriber发射数据。
  • Subscription 改名为 Disposable

RxJava3.0相比与RxJava2.0

  • 提供Java 8 lambda友好的API
  • 删除Maybe.toSingle(T)
  • 删除Flowable.subscribe(4 args)
  • 删除Observable.subscribe(4 args)
  • 删除Single.toCompletable()

更多3.0和2.0不同大家可以去搜一搜

你以为完了吗,还有背压没介绍呢,好吧,由于篇幅原因,把背压放到下一篇单独来讲了。

该片介绍了Observable的订阅过程,其实我们只要把订阅过程理解为当前的Observable向下游的Observer添加订阅监听,向上游的Observable添加订阅,发送数据的是从上游的Observable向下游的Observer发送数据。

介绍了发射器发送了onComplete事件后再不能发送onNext事件的原因,介绍了map、flatMap以及concatMap的区别,以及简单的介绍了其他的关键字。 介绍了Maybe、Single、Flowable、Completable几种观察者的区别,以及他们在什么场景用。 介绍了RxJava切换线程通过subscribeOn控制上游的Observable订阅发生的线程,observeOn控制下游的observer接收数据发生的线程,以及为什么RxJava的subscribeOn只有第一次生效,RxJava的observeOn多次调用离observer最近的一个才生效。 介绍了RxJava2.0相比于RxJava1.0有哪些变动,RxJava3.0相比与RxJava2.0有哪些变动。

「RxJava 3.x subscribeOn 线程切换分析 」 RxJava subscribeOn线程切换流程,以及多次调用subscribeOn为什么只有“第一次生效”? 阅读详情

相关推荐

从原理到实践:MIPI D-PHY v1.2的HS-Deskew功能全解析(附时序图详解)

本文深入解析MIPI D-PHY v1.2的HS-Deskew功能,从技术背景到实现机制,详细介绍了其在2.5Gbps高速传输中的关键作用。通过时序图详解和实测数据,帮助工程师理解并应用这一技术,解决信号完整性问题,提升系统可靠性。

weixin_30300225的博客 309

Android开发面试之RxJava

好文推荐: 作者:xiangcman 大家都知道RxJava上手是非常难的一个框架,为什么说是难呢,因为它的功能非常强大,各种操作符让人很难上手,搭配使用带生命周期的框架有RxLife等。以至于后面出了很多类似Rxjava的框架,有RxAndroid,我们用的RxJava切换主线程就是出自该框架,后面ACC架构中有LiveData、Lifecycle、包括后面协成中出来的flow异步流,都是向Rxjava靠拢,不过ACC出来的这些框架他们最大的特点就是操作简单,上手简单。所以RxJava强大的框架背后.

鸿蒙开发知识记录 4032

mysql8.0安装及配置超详细教程_mysql-8.0.15-winx64

MySQL8.0配置教程, 全网最详,同时有本地mysql和小皮系统见另一篇

小诺言的博客 2万+

RxJava2.0(三)强大的操作符

上一节讲解了线程调度, 并且举了两个实际中的例子, 其中有一个登录的例子, 不知大家有没有想过这么一个问题, 如果是一个新用户, 必须先注册, 等注册成功之后再自动登录该怎么做呢.很明显, 这是一个嵌套的网络请求, 首先需要去请求注册, 待注册成功回调了再去请求登录的接口..subscribeOn(Schedulers.io()) //在IO线程进行网络请求.observeOn(AndroidSchedulers.mainThread()) //回到主线程去处理请求结果@Override。

m0_49508485的博客 947

RK3568嵌入式Linux多屏显示开发指南

摘要:本篇RK3568嵌入式Linux多屏显示开发指南详细介绍了基于Qt应用层的实现方案。文章首先概述了技术架构,说明从Qt应用到硬件显示的完整流程。然后提供了两种实现方案:推荐使用Qt内置多屏支持方案,包含完整的C++代码示例和配置文件说明;对于需要精细控制的场景,给出了基于libdrm API的底层实现方案。最后重点讨论了屏幕旋转处理和多屏协同工作模式的关键技术要点,包括扩展桌面、镜像模式等实现方法。

froxy的专栏 1574

上位机图像处理和嵌入式模块部署(qmacvisual入门)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 虽然我们前面学习了很多的知识点,比如说在windows这边,用qt写界面,用opencv写图像处理代码;在linux这边先用树莓派测试,最后再部署到自己的平台上面。但是,对于一个商业软件是怎么运行的,很多时候我们心理还是没有底,或者说,自己写的软件距离一个商业软件还有多远,大部分学习的同学是不知道的。 就算有的同学说,这个时候可以借鉴一下vision pro,或者是

嵌入式-老费,一个分享专业嵌入式知识的blog 1231

rxJava源码解析系列四之subscribeOn和observeOn的理解

本篇文章主要解决subscribeOn和observeOn这两个方法为什么subscribeOn只有一次有效果,observeOn切换多次回调的都有效果。 不知道朋友有没有看过rxandroid的源码,如果看过的话,就会迎刃而解,没什么疑虑啦。没看过源码的朋友,可以看看我这个系列的前几篇文章,主要是系列二和系列三的文章,讲述了线程切换的问题。在这里我只说一下这两个方法线程切换的时机以及Obser...

dxyadc的博客 1878

2025年09月中国电子学会青少年软件编程(Python)等级考试试卷(一级)答案 + 解析

2025年09月中国电子学会青少年软件编程(Python)等级考试试卷(一级)答案 + 解析

伶俐角少儿编程 641

面试官一套RxJava组合拳下来,我被问得怀疑人生,腾讯安卓开发面试流程

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2020-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。还有高级架构技术进阶脑图、高级进阶架构资料帮助大家学习提升进阶,这里我也免费分享给大家也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。一起互勉~网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

2401_84023787的博客 868

C# WPF上位机实战:从零开发空压机监控系统(含实时监控+故障预警+数据追溯)

在工业生产场景中,空压机作为核心动力设备,常面临这些痛点:压力/温度异常导致停机、油耗超标未及时发现、设备故障追溯无数据支撑、人工巡检效率低且易遗漏。而基于C# WPF开发的空压机监控系统,能通过实时采集设备运行参数、智能分析运行状态、联动故障预警,实现“状态监控-异常告警-数据追溯-远程管控”全流程自动化,完美解决传统管理模式的弊端。今天就带大家落地这套空压机监控系统,基于C# + .NET 8 + WPF + Modbus TCP/RTU协议,整合压力、温度、油耗、运行时长等核心参数监控,搭配WPF的数

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1106

【图像超分】论文复现:Pytorch实现ESPCN

论文精读:由于ESPCN没有公布源码,我们选择github上一个高star的项目来复现。准备数据集,以及数据预处理搭建网络模型设置参数并训练测试训练好的模型用训练好的模型测试自己的数据本文我们使用pycharm链接服务器训练。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 1万+

面试官一套RxJava组合拳下来,我被问得怀疑人生(1),美团测试工程师面试题

订阅是从下游的Observer向上游的Observable发送订阅,然后在订阅的过程中,给下游的Observer发送订阅监听,并且给上游的被观察者添加订阅。

2301_82241647的博客 733

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

孽缘啊,同套面试题中午我把你挂了,晚上你把我挂了...互相成全不好吗

“中午帮公司刁难搞走一个阿里程序员,想要跳槽加薪晚上悄咪面试阿里,结果被中午拒绝了的阿里程序员给挂了,问的面试题还是中午我自己问的! 世界之大无奇不有,有些巧合简直就是天作之合,就像下面这位百度程序员和阿里程序员的故事。 某百度程序员发帖吐槽:中午面试了一个阿里程序员,问了他一些刁钻的问题,结果自己晚上去阿里面试,面试官竟然就是中午自己面的那个人,而且对方把自己上午问他的问题反问了一遍,关键自己还没回答好…… 走出阿里大门的楼主表示总感觉哪里不对劲,脑袋嗡嗡的,但又找不到证据??????????

weixin_44339238的博客 319

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Android开发面试之RxJava,flutterandroidx支持

Log.d(TAG, “onComplete”); } }); 相信这是最简单的事件发送的示例,这没什么好说的,那它们是怎么发送数据,接收数据的呢,下面我会把代码拆分来看,因为现在是链式调用,我把代码拆分如下: 订阅过程 这里把创建observable和observer、以及发起订阅分别拆开来写,后面方便我们分析代码,首先是第一步发起订阅observable.subscribe(observer): @Override public final void subscribe(@NonNull Observ

m0_64383184的博客 509

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

RxJava之Subscription

前言 前面写过RxJava类似观察者模式,但是一直没提到RxJava如何取消订阅,今天就来学习一下. Subscription RxJava中有个叫做Subscription的接口,可以用来取消订阅. public interface Subscription { /** * Stops the receipt of notifications on the {@...

程序亦非猿——一个能帮你进大厂的男人 560

Android 面试(Java 篇)

好记性不如烂笔头,这是近半年根据北京一些中小型企业安卓招聘需求整理的 Java 面试题,未完待续,如有不足欢迎留言指教。

X_sunmmer的博客 2326

RxJava之五—— observeOn()与subscribeOn()的详解

转载请标明出处:http://blog.youkuaiyun.com/xx326664162/article/details/51967967 文章出自:薛瑄的博客你也可以查看我的其他同类文章,也会让你有一定的收货!为什么多次调用subscribeOn()却只有第一个起作用? 为什么多次调用observeOn()却可以切换到不同线程 observeOn()后能不能再次调用subscribeOn()?如果

薛瑄的博客 3万+

Android RxJava框架基础知识(三)

Rxjava

xuyin1204的博客 748

Android 面试知识点记录——Rxjava与协程

背压(Backpressure)是指当数据产生速度大于消费速度,程序处理不过来是消息就会出现堆积。从而导致内存溢出、程序崩溃等问题。这种情况被称为背压问题逻辑上的改进办法生产者数量=消费者数量节流,丢弃一部分请求打包,把所有事件封装在一个集合中发送Rxjava1.x的时候没有对背压的支持,只提供了onBackpressureBuffer(time)、onBackpressureDrop() 等)来缓解背压问题,但这些解决方案都只是对数据流进行了缓存或者丢弃处理。

1 1056

面试:Rxjava线程切换原理

子线程切换主线程:给主线程所在的Handler发消息,然后就把逻辑切换过去了。主线程切换子线程:把任务放到线程池中执行就能把执行逻辑切换到子线程子线程切换子线程:把任务分别扔进两个线程就行了。

王温暖的博客 4302

Android RxJava 将subscribeOn与observeOn简化合并

RxJava 将subscribeOn与observeOn简化合并

3232

【Android】RxJava的使用(四)线程控制 —— Scheduler

转自:http://www.jianshu.com/p/ecfb9d68d2a2 前言 经过前几篇的介绍,对RxJava对模式有了一定的理解:由Observable发起事件,经过中间的处理后由Observer消费。(对RxJava还不了解的可以出门左拐) 之前的代码中,事件的发起和消费都是在同一个线程中执行,也就是说之前我们使用的RxJava是同步的~~~ 观察者模式本身的目的不就

linder_qzy的专栏 560

Android RxJava生命周期管理解决方案整理

一、为什么要做生命周期管理? Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe(ObservableEmitter<String> emitter) throws Exception { //TODO } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .

残风乱了温柔的博客 2885
上一篇: 中国35岁以上的程序猿哪去了?
下一篇: 滴滴DoKit For Flutter正式开源,还是原来的“味道”
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值