ARouter浅析

ARouter

ARouter是:阿里巴巴自研路由框架,主要解决组件间、模块间的 界面跳转 问题。

原理

  • 使用apt在编译阶段生成代码
  • 分组+按需加载 路由列表
  • 内部使用Intent进行跳转

原理解析

//创建的第一个使用Router注解的Activity
@Route(path = "/module_main/two")
public class TwoActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_two);
        Toast.makeText(this, getLocalClassName(), Toast.LENGTH_SHORT).show();
    }
}


//第二个
@Route(path = "/module_comm/comm")
public class CommActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_comm);
        Toast.makeText(this, getLocalClassName(), Toast.LENGTH_SHORT).show();
    }
}


//第三个
@Route(path = "/module_second/second")
public class SecondActivity extends AppCompatActivity {
    @Autowired
    String data;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this);
        TextView textView = new TextView(this);
        textView.setText(data);
        setContentView(textView);
    }
}

这三个类分别在不同的module里面

接下来我们编译一下这个项目,看都生成了哪些代码

//第一个module
public class ARouter$$Group$$module_main implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/module_main/two", RouteMeta.build(RouteType.ACTIVITY, TwoActivity.class, "/module_main/two", "module_main", null, -1, -2147483648));
  }
}

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

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

//第二个module
public class ARouter$$Group$$module_comm implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/module_comm/comm", RouteMeta.build(RouteType.ACTIVITY, CommActivity.class, "/module_comm/comm", "module_comm", null, -1, -2147483648));
  }
}

public class ARouter$$Providers$$module_comm implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
  }
}

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

//第三个module
public class ARouter$$Group$$module_second implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/module_second/second", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/module_second/second", "module_second", new java.util.HashMap<String, Integer>(){{put("data", 8); }}, -1, -2147483648));
  }
}

public class ARouter$$Providers$$module_second implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
  }
}

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

//这里是给用Autowired注解的变量赋值
public class SecondActivity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    SecondActivity substitute = (SecondActivity)target;
    substitute.data = substitute.getIntent().getStringExtra("data");
  }
}

我们可以看到基本每个module至少会生成三个类ARouter$$Providers$$***是空实现暂时不管它,我们看下ARouter$$Group$$Comm

public class ARouter$$Group$$module_comm implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/module_comm/comm", RouteMeta.build(RouteType.ACTIVITY, CommActivity.class, "/module_comm/comm", "module_comm", null, -1, -2147483648));
  }
}

这里可以看到在loadInto中是将用Router注解的类以path的值为key,类的class为value存在了Map中,ps所有@Router注解path值斜杠后第一个值相同的类都会在同一个类中被管理起来

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

这里将Router注解的path的斜杠后的第一个值为key,将ARouter$$Group$$module_comm.class为value存在了map中,这样的话我们是不是可以认为ARouter会将Router注解path斜杠后的第一个值相同的类分为了一个组,使用apt生成这个组的管理类,也就是我们现在的ARouter$$Group$$module_comm

在这里我们了解到ARouter是怎么进行分组的,接下来我们看下ARouter是怎么获取到我们的这些类的

ARouter.openLog();     // 打印日志
ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
ARouter.init(getApplication()); // 尽可能早,推荐在Application中初始化
    public static void init(Application application) {
        if (!hasInit) {
            logger = _ARouter.logger;
            _ARouter.logger.info(Consts.TAG, "ARouter init start.");
            hasInit = _ARouter.init(application);

            if (hasInit) {
                _ARouter.afterInit();
            }

            _ARouter.logger.info(Consts.TAG, "ARouter init over.");
        }
    }

	  protected static synchronized boolean init(Application application) {
	        mContext = application;
	        LogisticsCenter.init(mContext, executor);
	        logger.info(Consts.TAG, "ARouter init success!");
	        hasInit = true;
	        mHandler = new Handler(Looper.getMainLooper());
	
	        return true;
	    }
	
	  //代码有删减
	  public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
	        tr {
	                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
	                    logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
	                    // These class was generated by arouter-compiler.
	                    routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
	                    if (!routerMap.isEmpty()) {
	                        context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
	                    }
	
	                    PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
	                } else {
	                    logger.info(TAG, "Load router map from cache.");
	                    routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
	                }
	
	                logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
	                startInit = System.currentTimeMillis();
	
	                for (String className : routerMap) {
	                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
	                        // This one of root elements, load root.
	                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
	                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
	                        // Load interceptorMeta
	                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
	                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
	                        // Load providerIndex
	                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
	                    }
	                }
	            }
	        } catch (Exception e) {
	            throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
	        }
	    }

routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);我们直接看到这一行

    public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
        final Set<String> classNames = new HashSet<>();
		//拿到了apk安装之后的路径
        List<String> paths = getSourcePaths(context);
        for (final String path : paths) {
            DefaultPoolExecutor.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                    DexFile dexfile = null;
                    try {
                        if (path.endsWith(EXTRACTED_SUFFIX)) {
                            //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
                            dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                        } else {
                            dexfile = new DexFile(path);
                        }
                        Enumeration<String> dexEntries = dexfile.entries();
                        //便利dex下的类
                        while (dexEntries.hasMoreElements()) {
                            String className = dexEntries.nextElement();
                            //找到包名是com.alibaba.android.arouter.routes开头的类
                            if (className.startsWith(packageName)) {
                                classNames.add(className);
                            }
                        }
                    } catch (Throwable ignore) {
                        Log.e("ARouter", "Scan map file in dex files made error.", ignore);
                    } finally {
                        if (null != dexfile) {
                            try {
                                dexfile.close();
                            } catch (Throwable ignore) {
                            }
                        }
                    }
                }
            });
        }
        return classNames;
    }

这里传进来的packgeName = com.alibaba.android.arouter.routes 这个函数执行完就能拿到之前我们看到的几个类.因为我们编译之后生成的类的包名恰好是com.alibaba.android.arouter.routes

接下来我们继续看到LogisticsCenter类的init函数

 public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
      ...
    for (String className : routerMap) {
        if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
         } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
          ((IInterceptorGroup(Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
        } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
      }
      ...
    }

这里就是拿到apt生成的类并且调用他们的loadInto()当然这里还没有调用具体某个组loadInto所以实际path与类的映射还没加载到内存,这样做达到了节省内存的效果,这里把每个组的管理类的class添加到了Warehouse.groupsIndex中,后续我们要取出具体组的映射关系也是相当简单的事情

到这里ARouter的init()事情已经做完了我们在看一下在哪里才会取出具体组的映射呢

       ARouter.getInstance()
                .build("/module_second/second")
                .navigation();

我们通常是这样进行页面的跳转那我们接下来就看下navigation()都做了些什么事

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
      		...
            LogisticsCenter.completion(postcard);
       		...
            return _navigation(context, postcard, requestCode, callback);
        	....
    }


    public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG + "No postcard!");
        }

        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    	//当前的map中没有对应这个path的class
        if (null == routeMeta) { 
        	//取出这个path所在组的管理类
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
            if (null == groupMeta) {
                throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
            } else {
                // Load route and cache it into memory, then delete from metas.
                try {
                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
					//使用反射创建对象
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    //将这个组的所有数据添加到map
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());

                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
                } catch (Exception e) {
                    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                }
				//重新调这个函数,下次走else分子
                completion(postcard);   // Reload
            }
        } else {
        	//填充postcard后续界面跳转需要用到
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());

            Uri rawUri = postcard.getUri();
            if (null != rawUri) {   // Try to set params into bundle.
                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                Map<String, Integer> paramsType = routeMeta.getParamsType();

                if (MapUtils.isNotEmpty(paramsType)) {
                    // Set value by its type, just for params which annotation by @Param
                    for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                        setValue(postcard,
                                params.getValue(),
                                params.getKey(),
                                resultMap.get(params.getKey()));
                    }

                    // Save params name which need auto inject.
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }

                // Save raw uri
                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }

            switch (routeMeta.getType()) {
                case PROVIDER:  // if the route is provider, should find its instance
                    // Its provider, so it must implement IProvider
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) { // There's no instance of this provider
                        IProvider provider;
                        try {
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            throw new HandlerException("Init provider failed! " + e.getMessage());
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

从上面代码我们看到了ARouter的按需加载,我们在继续看下ARouter是怎么进行跳转的

    private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = null == context ? mContext : context;

        switch (postcard.getType()) {
            case ACTIVITY:
                // Build intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());

                // Set flags.
                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                // Set Actions
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }

                // 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:
                Class fragmentMeta = postcard.getDestination();
                try {
                    Object instance = fragmentMeta.getConstructor().newInstance();
                    if (instance instanceof Fragment) {
                        ((Fragment) instance).setArguments(postcard.getExtras());
                    } else if (instance instanceof android.support.v4.app.Fragment) {
                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                    }

                    return instance;
                } catch (Exception ex) {
                    logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }

从这里我们可以看到其实ARouter内部跳转还是使用的Intent,那为什么我们使用组件化开发引用不到别的module中的类但是在ARouter中却可以,那是因为在进行apk的过程中会把所有类在一起进行打包,所以在组件化项目中我们使用ARouter既方便又能达到解耦合的效果岂不美哉

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值