content: package com.alibaba.android.arouter.core;
  
  import android.content.Context;
  
  import com.alibaba.android.arouter.exception.HandlerException;
  import com.alibaba.android.arouter.facade.Postcard;
  import com.alibaba.android.arouter.facade.annotation.Route;
  import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
  import com.alibaba.android.arouter.facade.service.InterceptorService;
  import com.alibaba.android.arouter.facade.template.IInterceptor;
  import com.alibaba.android.arouter.thread.CancelableCountDownLatch;
  import com.alibaba.android.arouter.utils.MapUtils;
  
  import java.util.Map;
  import java.util.concurrent.TimeUnit;
  
  import static com.alibaba.android.arouter.launcher.ARouter.logger;
  import static com.alibaba.android.arouter.utils.Consts.TAG;
  
  /**
   * All of interceptors
   *
   * @author zhilong <a href="mailto:zhilong.lzl@alibaba-inc.com">Contact me.</a>
   * @version 1.0
   * @since 2017/2/23 下午2:09
   */
  @Route(path = "/arouter/service/interceptor")
  public class InterceptorServiceImpl implements InterceptorService {
      private static boolean interceptorHasInit;
      private static final Object interceptorInitLock = new Object();
  
      @Override
      public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
          if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
  
              checkInterceptorsInitStatus();
  
              if (!interceptorHasInit) {
                  callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
                  return;
              }
  
              LogisticsCenter.executor.execute(new Runnable() {
                  @Override
                  public void run() {
                      CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                      try {
                          _execute(0, interceptorCounter, postcard);
                          interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                          if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.
                              callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                          } else if (null != postcard.getTag()) {    // Maybe some exception in the tag.
                              callback.onInterrupt((Throwable) postcard.getTag());
                          } else {
                              callback.onContinue(postcard);
                          }
                      } catch (Exception e) {
                          callback.onInterrupt(e);
                      }
                  }
              });
          } else {
              callback.onContinue(postcard);
          }
      }
  
      /**
       * Excute interceptor
       *
       * @param index    current interceptor index
       * @param counter  interceptor counter
       * @param postcard routeMeta
       */
      private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
          if (index < Warehouse.interceptors.size()) {
              IInterceptor iInterceptor = Warehouse.interceptors.get(index);
              iInterceptor.process(postcard, new InterceptorCallback() {
                  @Override
                  public void onContinue(Postcard postcard) {
                      // Last interceptor excute over with no exception.
                      counter.countDown();
                      _execute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
                  }
  
                  @Override
                  public void onInterrupt(Throwable exception) {
                      // Last interceptor execute over with fatal exception.
  
                      postcard.setTag(null == exception ? new HandlerException("No message.") : exception);    // save the exception message for backup.
                      counter.cancel();
                      // Be attention, maybe the thread in callback has been changed,
                      // then the catch block(L207) will be invalid.
                      // The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
  //                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {    // You shouldn't throw the exception if the thread is main thread.
  //                        throw new HandlerException(exception.getMessage());
  //                    }
                  }
              });
          }
      }
  
      @Override
      public void init(final Context context) {
          LogisticsCenter.executor.execute(new Runnable() {
              @Override
              public void run() {
                  if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                      for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                          Class<? extends IInterceptor> interceptorClass = entry.getValue();
                          try {
                              IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                              iInterceptor.init(context);
                              Warehouse.interceptors.add(iInterceptor);
                          } catch (Exception ex) {
                              throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                          }
                      }
  
                      interceptorHasInit = true;
  
                      logger.info(TAG, "ARouter interceptors init over.");
  
                      synchronized (interceptorInitLock) {
                          interceptorInitLock.notifyAll();
                      }
                  }
              }
          });
      }
  
      private static void checkInterceptorsInitStatus() {
          synchronized (interceptorInitLock) {
              while (!interceptorHasInit) {
                  try {
                      interceptorInitLock.wait(10 * 1000);
                  } catch (InterruptedException e) {
                      throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
                  }
              }
          }
      }
  }
  