源码分析-ARouter(下)

97 阅读6分钟

依赖注入部分

依赖注入的注解@Autowired,有三个参数,其中第二个时候为必需参数,如果注解的时候加入了此参数传值的时候就一定得传入此参数,否则会直接crash

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {

    // Mark param's name or service name.
    String name() default "";

    // If required, app will be crash when value is null.
    // Primitive type wont be check!
    boolean required() default false;

    // Description of the field
    String desc() default "";
}

被注解的示例类:

@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {
    @Autowired
    int age = 10;
    @Autowired
    int height = 175;
    @Autowired(name = "boy", required = true)
    boolean girl;
    @Autowired
    char ch = 'A';
    @Autowired
    float fl = 12.00f;
    @Autowired
    double dou = 12.01d;
    @Autowired
    TestSerializable ser;
    @Autowired
    TestParcelable pac;
    @Autowired
    TestObj obj;
    @Autowired
    List<TestObj> objList;
    @Autowired
    Map<String, List<TestObj>> map;
    private long high;
    @Autowired
    String url;
    @Autowired
    HelloService helloService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test1);

        ARouter.getInstance().inject(this);

        String params = String.format("name=%s,\n age=%s, \n height=%s,\n girl=%s,\n high=%s,\n url=%s,\n ser=%s,\n pac=%s,\n obj=%s \n ch=%s \n fl = %s, \n dou = %s, \n objList=%s, \n map=%s", name, age, height, girl, high, url, ser, pac, obj, ch, fl, dou, objList, map);
        helloService.sayHello("Hello moto.");

        ((TextView) findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName());
        ((TextView) findViewById(R.id.test2)).setText(params);
    }
}

@Autowired的注解处理器

@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends BaseProcessor {
    private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>();   // Contain field need autowired and his super class.
    private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
    private static final ClassName AndroidLog = ClassName.get("android.util", "Log");

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        if (CollectionUtils.isNotEmpty(set)) {
            try {
                categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
                generateHelper();

            } catch (Exception e) {
                logger.error(e);
            }
            return true;
        }

        return false;
    }

    private void generateHelper() throws IOException, IllegalAccessException {
        TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);
        TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);
        TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
        TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
        TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
        TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();

        // Build input param name.
        ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();

        if (MapUtils.isNotEmpty(parentAndChild)) {
            //遍历parentAndChild里面所有的key-value集合
            for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
                //构造inject方法
                MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
                        .addAnnotation(Override.class)
                        .addModifiers(PUBLIC)
                        .addParameter(objectParamSpec);
                //取出entry里面的TypeElement,也就是完整的类名
                //com.alibaba.android.arouter.demo.module1.testactivity.Test1Activity
                TypeElement parent = entry.getKey();
                //取出entry里面的childs,也就是这个类下被@Autowired注解的所有的变量
                List<Element> childs = entry.getValue();
                
                // com.alibaba.android.arouter.demo.module1.testactivity.Test1Activity
                String qualifiedName = parent.getQualifiedName().toString();
                
                //com.alibaba.android.arouter.demo.module1.testactivity
                String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
                
                //Test1Activity$$ARouter$$Autowired
                String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;

                logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");

                //构建类名
                //public class Test1Activity$$ARouter$$Autowired implements ISyringe
                TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
                        .addJavadoc(WARNING_TIPS)
                        .addSuperinterface(ClassName.get(type_ISyringe))
                        .addModifiers(PUBLIC);

                //私有的成员变量
                //  private SerializationService serializationService;
                FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
                helper.addField(jsonServiceField);
                
                //inject方法里添加代码
                //serializationService = ARouter.getInstance().navigation(SerializationService.class);
                injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
                
                //inject方法里添加代码
                //Test1Activity substitute = (Test1Activity)target;
                injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));

                // 遍历这个类下所有被@Autowired注解的变量
                for (Element element : childs) {
                    Autowired fieldConfig = element.getAnnotation(Autowired.class);
                    String fieldName = element.getSimpleName().toString();
                    if (types.isSubtype(element.asType(), iProvider)) {  // It's provider
                        if ("".equals(fieldConfig.name())) {    // User has not set service path, then use byType.

                            // Getter
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
                                    ARouterClass,
                                    ClassName.get(element.asType())
                            );
                        } else {    // use byName
                            // Getter
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
                                    ClassName.get(element.asType()),
                                    ARouterClass,
                                    fieldConfig.name()
                            );
                        }

                        // Validator
                        if (fieldConfig.required()) {
                            injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
                            injectMethodBuilder.addStatement(
                                    "throw new RuntimeException("The field '" + fieldName + "' is null, in class '" + $T.class.getName() + "!")", ClassName.get(parent));
                            injectMethodBuilder.endControlFlow();
                        }
                    } else {    // It's normal intent value
                        String originalValue = "substitute." + fieldName;
                        String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
                        boolean isActivity = false;
                        if (types.isSubtype(parent.asType(), activityTm)) {  // Activity, then use getIntent()
                            isActivity = true;
                            statement += "getIntent().";
                        } else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) {   // Fragment, then use getArguments()
                            statement += "getArguments().";
                        } else {
                            throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
                        }

                        statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity, isKtClass(parent));
                        if (statement.startsWith("serializationService.")) {   // Not mortals
                            injectMethodBuilder.beginControlFlow("if (null != serializationService)");
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = " + statement,
                                    (StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
                                    ClassName.get(element.asType())
                            );
                            injectMethodBuilder.nextControlFlow("else");
                            injectMethodBuilder.addStatement(
                                    "$T.e("" + Consts.TAG + "", "You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!")", AndroidLog, ClassName.get(parent));
                            injectMethodBuilder.endControlFlow();
                        } else {
                            injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
                        }

                        // Validator
                        if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) {  // Primitive wont be check.
                            injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
                            injectMethodBuilder.addStatement(
                                    "$T.e("" + Consts.TAG + "", "The field '" + fieldName + "' is null, in class '" + $T.class.getName() + "!")", AndroidLog, ClassName.get(parent));
                            injectMethodBuilder.endControlFlow();
                        }
                    }
                }

                helper.addMethod(injectMethodBuilder.build());

                // Generate autowire helper
                JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);

                logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
            }

            logger.info(">>> Autowired processor stop. <<<");
        }
    }

    private boolean isKtClass(Element element) {
        for (AnnotationMirror annotationMirror : elementUtils.getAllAnnotationMirrors(element)) {
            if (annotationMirror.getAnnotationType().toString().contains("kotlin")) {
                return true;
            }
        }

        return false;
    }

    private String buildCastCode(Element element) {
        if (typeUtils.typeExchange(element) == TypeKind.SERIALIZABLE.ordinal()) {
            return CodeBlock.builder().add("($T) ", ClassName.get(element.asType())).build().toString();
        }
        return "";
    }

    /**
     * Build param inject statement
     */
    private String buildStatement(String originalValue, String statement, int type, boolean isActivity, boolean isKt) {
        switch (TypeKind.values()[type]) {
            case BOOLEAN:
                statement += "getBoolean" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case BYTE:
                statement += "getByte" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case SHORT:
                statement += "getShort" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case INT:
                statement += "getInt" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case LONG:
                statement += "getLong" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case CHAR:
                statement += "getChar" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case FLOAT:
                statement += "getFloat" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case DOUBLE:
                statement += "getDouble" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
                break;
            case STRING:
                statement += (isActivity ? ("getExtras() == null ? " + originalValue + " : substitute.getIntent().getExtras().getString($S") : ("getString($S")) + ", " + originalValue + ")";
                break;
            case SERIALIZABLE:
                statement += (isActivity ? ("getSerializableExtra($S)") : ("getSerializable($S)"));
                break;
            case PARCELABLE:
                statement += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
                break;
            case OBJECT:
                statement = "serializationService.parseObject(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", new " + TYPE_WRAPPER + "<$T>(){}.getType())";
                break;
        }

        return statement;
    }

    //扫描所有被@Autowired注解的变量element,如果是被private修饰则直接抛出异常
    //找到这些变量所在的类 enclosingElement
    //把这些变量按照其所在的类作为key的情况下,分类放在private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>(); 
    private void categories(Set<? extends Element> elements) throws IllegalAccessException {
        if (CollectionUtils.isNotEmpty(elements)) {
            for (Element element : elements) {
                TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();//获取到这个element所在的类名

                if (element.getModifiers().contains(Modifier.PRIVATE)) {
                    throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
                            + element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
                }

                if (parentAndChild.containsKey(enclosingElement)) { // Has categries
                    parentAndChild.get(enclosingElement).add(element);
                } else {
                    List<Element> childs = new ArrayList<>();
                    childs.add(element);
                    parentAndChild.put(enclosingElement, childs);
                }
            }

            logger.info("categories finished.");
        }
    }
}

被注解的类通过注解处理器生成的代码示例如下

public interface ISyringe {
    void inject(Object target);
}

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class Test1Activity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    Test1Activity substitute = (Test1Activity)target;
    substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
    substitute.height = substitute.getIntent().getIntExtra("height", substitute.height);
    substitute.girl = substitute.getIntent().getBooleanExtra("boy", substitute.girl);
    substitute.ch = substitute.getIntent().getCharExtra("ch", substitute.ch);
    substitute.fl = substitute.getIntent().getFloatExtra("fl", substitute.fl);
    substitute.dou = substitute.getIntent().getDoubleExtra("dou", substitute.dou);
    substitute.ser = (com.alibaba.android.arouter.demo.service.model.TestSerializable) substitute.getIntent().getSerializableExtra("ser");
    substitute.pac = substitute.getIntent().getParcelableExtra("pac");
    if (null != serializationService) {
      substitute.obj = serializationService.parseObject(substitute.getIntent().getStringExtra("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    if (null != serializationService) {
      substitute.objList = serializationService.parseObject(substitute.getIntent().getStringExtra("objList"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestObj>>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'objList' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    if (null != serializationService) {
      substitute.map = serializationService.parseObject(substitute.getIntent().getStringExtra("map"), new com.alibaba.android.arouter.facade.model.TypeWrapper<Map<String, List<TestObj>>>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'map' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    substitute.url = substitute.getIntent().getExtras() == null ? substitute.url : substitute.getIntent().getExtras().getString("url", substitute.url);
    substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
  }
}

自动赋值操作(可以参考ButterKnife)

在示例的类的代码里onCreate()里面,调用了Arouter.getInstance().inject(this)来实现自动赋值操作。

@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test1);

        ARouter.getInstance().inject(this);

        ((TextView) findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName());
        ((TextView) findViewById(R.id.test2)).setText(params);
    }
}
ARouter.java

public void inject(Object thiz) {
    _ARouter.inject(thiz);
}


_ARouter.java

//通过注解的形式启动了AutewiredService的实现类
static void inject(Object thiz) {
    AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());//在navigation里面会调用其init()函数,并返回一个实例赋值给autowiredService
    if (null != autowiredService) {
        autowiredService.autowire(thiz); //自动装载
    }
}

被ARouter启动的AutowiredServiceImpl,在navigation里面会调用到其init函数,并返回一个实例对象赋值给autowiredService,可以参考上文的InterceptorServiceImpl一样的流程。

public interface AutowiredService extends IProvider {
    void autowire(Object instance);
}
public interface IProvider {
    void init(Context context);
}
@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    private LruCache<String, ISyringe> classCache;
    private List<String> blackList;

    //初始化一个cache和一个blackList
    @Override
    public void init(Context context) {
        classCache = new LruCache<>(50);
        blackList = new ArrayList<>();
    }

    //自动注入,instance为调用ARouter.getInstance().inject(this);的类的对象

    @Override
    public void autowire(Object instance) {
        doInject(instance, null);
    }
    
    private void doInject(Object instance, Class<?> parent) {
       //class com.alibaba.android.arouter.demo.module1.testactivity.Test1Activity
       Class<?> clazz = null == parent ? instance.getClass() : parent;
       
        ISyringe syringe = getSyringe(clazz); //拿到clazz对应的辅助类
        if (null != syringe) {
            syringe.inject(instance); //调用辅助类的inject方法
        }

        //获取调用类的父类,如果父类里也有被@Autowired注解的变量,也会赋值
        //class com.alibaba.android.arouter.demo.module1.testactivity.BaseActivity
        //class android.support.v7.app.AppCompatActivity
        Class<?> superClazz = clazz.getSuperclass();
        // has parent and its not the class of framework.
        if (null != superClazz && !superClazz.getName().startsWith("android")) {
            doInject(instance, superClazz);
        }
    }

    private ISyringe getSyringe(Class<?> clazz) {
        String className = clazz.getName();
        //com.alibaba.android.arouter.demo.module1.testactivity.Test1Activity
        try {
            //判断黑名单里是否有这个类
            if (!blackList.contains(className)) {
                //从init的cache中获取这个类,首次进入不存在
                ISyringe syringeHelper = classCache.get(className);
                if (null == syringeHelper) {  // No cache.
                   
                   //通过反射调用到这个类对应的生成的辅助类,实例化一个对话赋值给syringeHelper,并通过key-value的形式缓存到classCache里面
                  //com.alibaba.android.arouter.demo.module1.testactivity.Test1Activity$$ARouter$$Autowired
                    syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                classCache.put(className, syringeHelper);
                return syringeHelper;
            }
        } catch (Exception e) {
            blackList.add(className);    // This instance need not autowired.
        }

        return null;
    }
}

降级策略

单独降级策略和全局的降级策略都是在_Arouter的这个navigation里面实现的,主要

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        // Pretreatment failed, navigation canceled.
        return null;
    }
    System.out.println("navigation" );
    // Set context to postcard.
    postcard.setContext(null == context ? mContext : context);

    try {
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {、
        //在补全postcard的时候,如果出现了没找到RouteMeta的情况,会catch这个异常
        logger.warning(Consts.TAG, ex.getMessage());
        //如果是debug版本,会有个Toast提示
        if (debuggable()) {
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mContext, "There's no route matched!\n" +
                            " Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
            });
        }
        //如果在navigation的时候,有传入callback,及为这个跳转做单独的降级策略,单独的降级策略的优先级是高于全局降级策略的
        if (null != callback) {
            System.out.println("null != callback");
            callback.onLost(postcard);
        } else {
            //如果在navigation的时候,没有传入callback,则会这个跳转做全局的降级策略
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }

        return null;
    }

    //这个单独的callback可以继续传递,找了对应的RuteMeta之后,找到了的回调
    if (null != callback) {
        callback.onFound(postcard);
    }

    if (!postcard.isGreenChannel()) { 
           interceptorService.doInterceptions(postcard, new InterceptorCallback() {
               @Override
               public void onContinue(Postcard postcard) {
               _navigation(postcard, requestCode, callback);
          }  
              //这个单独的callback可以继续传递,,如果有拦截器对这个navigation进行了拦截,还有拦截了的回调
              @Override
              public void onInterrupt(Throwable exception) {
                 if (null != callback) {
                     callback.onInterrupt(postcard);
                  }

          }

    } else {
        System.out.println("postcard.isGreenChannel()");
        return _navigation(postcard, requestCode, callback);
    }

    return null;
}


//这个callback继续传递
@SuppressLint("WrongConstant")
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = postcard.getContext();
    System.out.println("_navigation getType : " + postcard.getType());
    switch (postcard.getType()) {
        case ACTIVITY:
            ...

            // Navigation in main looper.
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;
        case PROVIDER:
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
        ...
        case METHOD:
        case SERVICE:
        default:
            return null;
    }

    return null;
}

private void startActivity(int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) {
    if (requestCode >= 0) {  // Need start for result
        if (currentContext instanceof Activity) {
            ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
        } else {
            logger.warning(Consts.TAG, "Must use [navigation(activity, ...)] to support [startActivityForResult]");
        }
    } else {
        ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
    }

    if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
        ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
    }
    //最后的完成了startActivity之后,还有个完成的回调
    if (null != callback) { // Navigation over.
        callback.onArrival(postcard);
    }
}



单独降级

单独降级策略实现在navigation的时候实现其对应的接口即可。

ARouter.getInstance().build("/xxx/xxx").navigation(this, new NavCallback() {
    @Override
    public void onFound(Postcard postcard) {
        Log.d("ARouter", "找到了");
    }

    @Override
    public void onLost(Postcard postcard) {
        Log.d("ARouter", "找不到了");
    }

    @Override
    public void onArrival(Postcard postcard) {
        Log.d("ARouter", "跳转完了");
    }

    @Override
    public void onInterrupt(Postcard postcard) {
        Log.d("ARouter", "被拦截了");
    }
});

全局降级

全局的降级策略只提供了一个接口,并没有具体的实现类,业务需要需要自己实现对应的业务代码,并通过@Route注册

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {

    try {
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
    
         ...
        if (null != callback) {
            System.out.println("null != callback");
            callback.onLost(postcard);
        } else {
            //全局的降级策略
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }

        return null;
    }

    ...
    return null;
}

public interface DegradeService extends IProvider {

    void onLost(Context context, Postcard postcard);
}
public interface IProvider {

    void init(Context context);
}

业务对全局降级策略的实现

@Route(path = "/degrade/test")
public class DegradeServiceImpl implements DegradeService {
    @Override
    public void onLost(Context context, Postcard postcard) {
        Log.e("DegradeServiceImpl", "DegradeServiceImpl onLost");
    }

    @Override
    public void init(Context context) {
        Log.e("DegradeServiceImpl", "DegradeServiceImpl init");
    }
}

参考文章

developer.aliyun.com/article/716…

juejin.cn/post/684490…

juejin.cn/post/694561…

juejin.cn/post/684490…

juejin.cn/post/684490…

juejin.cn/post/684490…

juejin.cn/post/698126…

www.jianshu.com/p/f524abda3…

blog.csdn.net/Lebron_xia/…

www.jianshu.com/p/b8353fa94…

www.jianshu.com/p/746b9bed4…