/** Pulls bitmap from diskcache */
 private @Nullable ParcelFileDescriptor createPipe(final ArtInfo artInfo) {
   final byte[] bytes = mL2Cache.getBytes(artInfo.cacheKey());
   if (bytes == null) {
     return null;
   }
   try {
     final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
     final OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]);
     final ParcelFileDescriptor in = pipe[0];
     final Scheduler.Worker worker = mScheduler.createWorker();
     worker.schedule(
         new Action0() {
           @Override
           public void call() {
             try {
               IOUtils.write(bytes, out);
               out.flush();
             } catch (IOException e) {
               Timber.w("createPipe(e=%s) for %s", e.getMessage(), artInfo);
             } finally {
               IOUtils.closeQuietly(out);
               worker.unsubscribe();
             }
           }
         });
     return in;
   } catch (IOException e) {
     Timber.e(e, "createPipe() for %s", artInfo);
     return null;
   }
 }
  /**
   * Eagerly creates a pipe, then blocks on a background thread while we wait for the fetcher to
   * return the bitmap, simply closing the pipe if no art was found
   */
  private @Nullable ParcelFileDescriptor createPipe2(final ArtInfo artInfo) {
    try {
      final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
      final OutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]);
      final ParcelFileDescriptor in = pipe[0];
      final Scheduler.Worker worker = mScheduler.createWorker();
      worker.schedule(
          new Action0() {
            @Override
            public void call() {
              OptionalBitmap bitmap = null;
              ArtworkFetcherService.Connection binder = null;
              try {
                // make a new request and wait for it to come in.
                binder = ArtworkFetcherService.bindService(getContext());
                final BlockingQueue<OptionalBitmap> queue = new LinkedBlockingQueue<>(1);
                final CompletionListener listener =
                    new CompletionListener() {
                      @Override
                      public void onError(Throwable e) {
                        Timber.w("onError(%s) for %s", e.getMessage(), artInfo);
                        queue.offer(new OptionalBitmap(null));
                      }

                      @Override
                      public void onNext(Bitmap o) {
                        queue.offer(new OptionalBitmap(o));
                      }
                    };
                if (!binder.getService().newRequest(artInfo, listener)) {
                  throw new InterruptedException("Enqueue failed");
                }
                bitmap = queue.take();
                if (bitmap.hasBitmap()) {
                  byte[] bytes = mL2Cache.bitmapToBytes(bitmap.getBitmap());
                  IOUtils.write(bytes, out);
                  out.flush();
                }
              } catch (InterruptedException | IOException e) {
                Timber.w("createPipe2(e=%s) for %s", e.getMessage(), artInfo);
                if (binder != null) {
                  binder.getService().cancelRequest(artInfo);
                }
              } finally {
                if (bitmap != null) bitmap.recycle();
                IOUtils.closeQuietly(binder);
                IOUtils.closeQuietly(out);
                worker.unsubscribe();
              }
            }
          });
      return in;
    } catch (IOException e) {
      Timber.e(e, "createPipe2() for %s", artInfo);
      return null;
    }
  }
  @Override
  public Subscriber<? super T> call(final Subscriber<? super List<T>> child) {
    final Worker inner = scheduler.createWorker();
    child.add(inner);

    if (timespan == timeshift) {
      ExactSubscriber bsub = new ExactSubscriber(new SerializedSubscriber<List<T>>(child), inner);
      bsub.scheduleExact();
      return bsub;
    }

    InexactSubscriber bsub = new InexactSubscriber(new SerializedSubscriber<List<T>>(child), inner);
    bsub.startNewChunk();
    bsub.scheduleChunk();
    return bsub;
  }
  @Test
  public void shouldScheduleDelayedActionOnHandlerThread() {
    Handler handler = mock(Handler.class);
    Action0 action = mock(Action0.class);

    Scheduler scheduler = AndroidSchedulers.from(handler);
    Worker inner = scheduler.createWorker();
    inner.schedule(action, 1, SECONDS);

    // verify that we post to the given Handler
    ArgumentCaptor<Runnable> runnable = ArgumentCaptor.forClass(Runnable.class);
    verify(handler).postDelayed(runnable.capture(), eq(1000L));

    // verify that the given handler delegates to our action
    runnable.getValue().run();
    verify(action).call();
  }
 @Override
 public void call(SingleSubscriber<? super T> singleSubscriber) {
   Worker worker = scheduler.createWorker();
   singleSubscriber.add(worker);
   worker.schedule(new ScalarSynchronousSingleAction<T>(singleSubscriber, value));
 }