Android上.flatMap()内的奇怪调度程序行为

时间:2018-01-17 19:54:28

标签: java android multithreading rx-java rx-java2

我一直试图确定为什么.flatMap()运算符中的以下代码显示在主线程上运行:

public Observable<Void> getObservable() {                                                                        
    return jobServiceObservable                                                    
            .flatMap(jobService -> {                                                                             
                if (Looper.myLooper() == Looper.getMainLooper()) {                                               
                    Log.d("LooperTest", "On main thread.");                                                      
                } else {                                                                                         
                    Log.d("LooperTest", "Not on main thread.");                                                  
                }                                                                                                

                return jobService.syncReservations(accountUtil.getCurrentAccount());
            })                                                                                                   
            .subscribeOn(Schedulers.io()).observeOn(foregroundScheduler);                                                                                                                                                      
}

如您所见,使用.subscribeOn()调用Schedulers.io(),但是日志语句显示.flatMap()内的代码是在主线程上运行的:

  

LooperTest:在主线程上。

作为一项完整性检查,我在此代码的各个部分添加了对.subscribeOn(Schedulers.io())的额外调用:

public Observable<Void> getObservable() {                                                                        
    return jobServiceObservable.subscribeOn(Schedulers.io())                                                     
            .flatMap(jobService -> {                                                                             
                if (Looper.myLooper() == Looper.getMainLooper()) {                                               
                    Log.d("LooperTest", "On main thread.");                                                      
                } else {                                                                                         
                    Log.d("LooperTest", "Not on main thread.");                                                  
                }                                                                                                

                return jobService.syncReservations(accountUtil.getCurrentAccount()).subscribeOn(Schedulers.io());
            })                                                                                                   
            .subscribeOn(Schedulers.io()).observeOn(foregroundScheduler);                                                                                                                                                         
}

但是,日志语句似乎显示相同的结果。接下来,在没有任何代码更改的情况下,我清理了构建并重新启动了我的模拟器。下次运行时,打印出以下内容:

  

LooperTest:不在主线程上。

这很奇怪,因为没有进行任何代码更改。同样,在没有代码更改的情况下,我清理了构建并重新启动了模拟器。在下一次运行中,打印了以下内容:

  

LooperTest:在主线程上。

再一次,我清理了构建,然后关闭并打开了一个不同类型的新模拟器。运行后,打印出以下内容:

  

LooperTest:不在主线程上。

为什么会这样?我怀疑有一些奇怪的缓存机制在起作用。

此外,请注意jobService.syncReservations()会返回BehaviorSubject。通过各种搜索,Subjects可能会或可能不会尊重对.subscribeOn()的调用。

最后,请注意jobServiceObservable被注入到定义上述代码的文件中。 jobServiceObservable是通过以下代码创建的:

public Observable<JobService> getObservable() {                                                                                              
    return Observable.create(e -> {                                                                                                          
        if (jobServiceBound && jobService != null) {                                                                                         
            e.onNext(jobService);                                                                                                            
            e.onComplete();                                                                                                                  
        } else {                                                                                                                             
            jobServiceConnection = new ServiceConnection() {                                                                                 
                @Override                                                                                                                    
                public void onServiceConnected(ComponentName name, IBinder service) {                                                        
                    JobService.JobServiceBinder binder = (JobService.JobServiceBinder) service;                                              
                    jobService = binder.getService();                                                                                        
                    jobServiceBound = true;                                                                                                  

                    e.onNext(jobService);                                                                                                    
                    e.onComplete();                                                                                                          
                }                                                                                                                            

                @Override                                                                                                                    
                public void onServiceDisconnected(ComponentName name) {                                                                      
                    reset();                                                                                                                 
                }                                                                                                                            
            };                                                                                                                               
            try {                                                                                                                            
                boolean success = context.bindService(new Intent(context, JobService.class), jobServiceConnection, Context.BIND_AUTO_CREATE);
                if (!success) {                                                                                                                                                                                    
                    e.onError(new Throwable("The service failed to be bound."));                                                             
                }                                                                                                                            
            } catch (SecurityException exception) {                                                                                                                                                                     
                e.onError(exception);                                                                                                        
            }                                                                                                                                
        }                                                                                                                                    
    });                                                                                                                                      
}

需要有关为何发生上述行为的权威答案。

1 个答案:

答案 0 :(得分:2)

因为在订阅onServiceConnected调度程序上的包Observable之后,系统方式在主线程上调用了io()subscribeOn告诉我们进一步订阅的位置,或者在这种情况下,应该执行Observable.create()的正文。您应该在observeOn之前使用flatMap,以便在所需的线程上执行mapper函数:

public Observable<Void> getObservable() {
    return jobServiceObservable
        .observeOn(Schedulers.io())
        .flatMap(jobService -> {
            if (Looper.myLooper() == Looper.getMainLooper()) {
                Log.d("LooperTest", "On main thread.");
            } else {
                Log.d("LooperTest", "Not on main thread.");
            }

            return jobService.syncReservations(
                accountUtil.getCurrentAccount()).subscribeOn(Schedulers.io());
        })
        .observeOn(foregroundScheduler);
}

(相反,对于典型的Retrofit网络调用,subscribeOn有效,因为网络库在给定的调度程序上执行其阻塞调用,并保留在那里以发出网络响应。)