Arouter

Arouter [组件化框架]

官方简介:一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦

Arouter框架地址

使用集成就不在此赘述,记录一下我所理解中这个框架。

dependencies {
    // 替换成最新版本, 需要注意的是api
    // 要与compiler匹配使用,均使用最新版可以保证兼容
    compile 'com.alibaba:arouter-api:x.x.x'
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
    ...
}

    集成里面有这么一个操作,需要引入一个注解处理器,再看了下具体的使用方式,分析猜测框架实现的大概思路是 注解+动态生成文件实现,具体是不是这样,接着分析

注解处理器

注解处理器,是javac处理注解的一种工具,它用来在编译时扫描和处理注解。简单来说就是在编译期,通过注解采集信息,生成.java文件。减少重复的代码的编写。
用的比较多的就是页面注册跳转和传值这两个功能,对应的两个注解是 @Autowired@Route

  • Aoute注解,给页面添加一个路由url,跳转的时候直接使用这个url进行跳转,代替系统自带的那种方式来找到跳转的目标界面
  • @Autowired注解,跳转的传值自动解析值,代替手动获取intent的传值(自带的获取值的方式也可以使用)
    主要看看这两个注解的实现思路;

1、Aroute

给界面添加该注解后,build 一下,就能生成对应的代码类,如下所示
Arouter

// build 类1
public class ARouter$$Group$$my implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/my/MainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/my/mainactivity", "my", null, -1, -2147483648));
    atlas.put("/my/TwoActivity", RouteMeta.build(RouteType.ACTIVITY, TwoActivity.class, "/my/twoactivity", "my", new java.util.HashMap<String, Integer>(){{put("name", 8); }}, -1, -2147483648));
  }
}

// build 类2
public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
  }
}

// build 类3
public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("my", ARouter$$Group$$my.class);
  }
}

// build 类4
public class TwoActivity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    TwoActivity substitute = (TwoActivity)target;
    substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
  }
}

分析一下注解生成的类

  1. @Route(path = "/my/MainActivity"),路由前一节/my是group名,区分不同组别的路由,第二段是targer 目标类,在build类1中可以看到把我们添加该注解的界面放到了一个 Map 里面,key是route注解的path, value里面存放的是一个RouteMate对象。

分析就是该注解帮我们把对应界面存到了一个,map里面,与模块解耦,路由跳转就直接在这个map里面寻找 key 值对应的 value 来实现跳转


根据此原理,手动实现一个类似功能的注解

a、定义注解
b、注解处理器定义解析注解,自动把注解类添加到map集合
c、提供一个工具类,方便route的初始话化,以及各种操作(比如添加,启动等)

  • 第一步,定义一个注解 Route
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Route {
    String value();
}
  • 第二步 定义一个注解处理器
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
    Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotaions = new HashSet<>();
        annotaions.add(Route.class.getCanonicalName());
        return annotaions;
    }

    //文件的内容是什么
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    }

由于想只通过一个注解达到此目的,所以添加和启动的操作就需要在解析注解的类里面去完成,生成一个build 类1 块所示的java类,此处设计就是一些反射相关的操作,

 //文件的内容是什么
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("------------22222222-------------");
        //在build目录生成文件
        //原理,就是去找所有继承了activity文件,并且是被Router注解了的文件
        //map.put(path,ativity的全类名
       Set<? extends Element> elements =  roundEnv.getElementsAnnotatedWith(Route.class);
         System.out.println(elements.size());
         //这个表示被Route注解的类的所有元素
        //只需要全类名
        Map<String,String> map = new HashMap<>();
        //map,收集被Route注解的类
        for(Element element:elements){
            TypeElement typeElement = (TypeElement) element;
            //通过这个方法得到activity的全类名
            String activityName = typeElement.getQualifiedName().toString();
            //key值
            String key = typeElement.getAnnotation(Route.class).value();
            map.put(key,activityName);
        }
        //生成文件

        return false;
    }

//生成文件方式
// 1、拼接字符串
//2、javapoet方式

相关方法实现之后,build一下看看生成的文件:

public class ActivityUtil_1632152688013 implements IRouter {
@Override
public void putActivity() { 
   Arouter.getInstance().putActivity("/my/TwoActivity",com.example.arouterdemo.MainActivity.class);
   Arouter.getInstance().putActivity("/my/TwoActivity",com.example.arouterdemo.TwoActivity.class);
 }
}
  • 第三步 工具类提供的方法
    public void startActivity(String activityName, Bundle bundle) {
        Intent intent = new Intent();
        Class<? extends Activity> aCls = activityMap.get(activityName);
        if (aCls == null) {
            return;
        }
        if (bundle != null) {
            intent.putExtras(bundle);
        }
        intent.setClass(mContext, aCls);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        // TODO 是不是很熟悉了
        mContext.startActivity(intent);
    }
public void init(Context context) {
        List<String> className = getAllActivityUtils("com.example.arouterdemo.");

//        反射调用
        for (String cls : className) 
            try {
                Class<?> aClass = Class.forName(cls);
                if (IRouter.class.isAssignableFrom(aClass)) {
                    IRouter iRouter = (IRouter) aClass.newInstance();
                    iRouter.putActivity();
                }
            } catch ( Exception e) {
                e.printStackTrace();
            }
        }
    }
    public List<String> getAllActivityUtils(String packageName) {
        List<String> list = new ArrayList<>();
        String path;
        try {
            path =   mContext.getPackageManager().getApplicationInfo(mContext.getPackageName(), 0).sourceDir;
            DexFile dexFile = new DexFile(path);
            Enumeration<String> enumeration = dexFile.entries();
            while (enumeration.hasMoreElements()) {
                String name =  enumeration.nextElement();
                if (name.startsWith(packageName)) {
                    list.add(name);
                }
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;

    }

基本功能就实现了跳转,ARouter框架的核心思想其实也就是这样,只不过是更加强大的升级版本,比如说注解类的路由保存用了一个实体包装

 */
public class RouteMeta {
    private RouteType type;         // Type of route
    private Element rawType;        // Raw type of route
    private Class<?> destination;   // Destination
    private String path;            // Path of route
    private String group;           // Group of route
    private int priority = -1;      // The smaller the number, the higher the priority
    private int extra;              // Extra data
    private Map<String, Integer> paramsType;  // Param type
    private String name;

    private Map<String, Autowired> injectConfig;  // Cache inject config.
  ...

里面就把跳转所需的各个信息囊括了进去,处理方式更优,核心实现就是这么一个逻辑,弄清楚这一层之后再去看它其他具体细节的处理就更加顺畅了。

好了,大概就先到这儿,记录一下这个过程。

上一篇:[转]Vmware(vmdk)虚拟机到hyperv(vhd)虚拟机转换


下一篇:一.Arouter框架分析