本系列文章主要分析 ARouter 框架的架构和原理。
这是阿里 ARouter 开源库的地址,大家可以直接访问https://github.com/alibaba/ARouter
本篇博文主要分析 arouter-api 模块的路由跳转的过程,以及变量/数据的传递,这篇文章将是本系列的最后一篇(后续会抽时间写其他的)!
在阅读过程中,涉及到方法跳转的时候,注释上有 -->
的标志,这样的好处是,以类为单位,一次性分析其所有的方法:
1 路由跳转 我们来看看
1.1 跳转方式 ARouter 支持两种方式来跳转:
1 2 3 ARouter.getInstance().build("/home/main" ) .navigation(); ARouter.getInstance().build("/home/main" , "ap" ).navigation();
1 2 Uri uri; ARouter.getInstance().build(uri).navigation();
这种调用方式相当于原生的 startActivityForResult:
1 ARouter.getInstance().build("/home/main" , "ap" ).navigation(this , 5 );
同时我们也可以指定跳转回调:NavigationCallback
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ARouter.getInstance().build("/test/activity" ).navigation(this , new NavigationCallback() { @Override public void onFound (Postcard postcard) { } @Override public void onLost (Postcard postcard) { } @Override public void onArrival (Postcard postcard) { } @Override public void onInterrupt (Postcard postcard) { } });
处理跳转的结果;
我们知道 actiivty 的跳转是收到拦截器的限制的,但是 PostCard 提供了接口,能够跳过所有的拦截器:
1 2 ARouter.getInstance().build("/home/main" ).greenChannel().navigation();
这里的 greenChannel 方法我们前面有分析过,不多说了!
1.1.1 Uri 跳转的特殊性 这里要单独讲下 uri 跳转的特殊性,ARouter 通过新建一个没有 UI 的界面作为跳板来统一处理,scheme 是 arouter 的跳转请求!
需要新建一个 activity 来接收 uri,没有 ui 界面,这是关键点!
1 2 3 4 5 6 7 8 9 10 public class SchameFilterActivity extends Activity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); Uri uri = getIntent().getData(); ARouter.getInstance().build(uri).navigation(); finish(); } }
AndroidManifest.xml 中要指定该 activity 监听的 schame 事件是:arouter://m.aliyun.com
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <activity android:name =".activity.SchameFilterActivity" > <intent-filter > <data android:host ="m.aliyun.com" android:scheme ="arouter" /> <action android:name ="android.intent.action.VIEW" /> <category android:name ="android.intent.category.DEFAULT" /> <category android:name ="android.intent.category.BROWSABLE" /> </intent-filter > <intent-filter android:autoVerify ="true" > <action android:name ="android.intent.action.VIEW" /> <category android:name ="android.intent.category.DEFAULT" /> <category android:name ="android.intent.category.BROWSABLE" /> <data android:host ="m.aliyun.com" android:scheme ="http" /> <data android:host ="m.aliyun.com" android:scheme ="https" /> </intent-filter > </activity >
相当于这个 activity 作为外界的统一入口,H5 或者 native 通过 intent 匹配,将数据传递给这个 activity,然后这个 activity 解析数据,将 uri 叫给 ARouter 最终实现跳转!
详细分析:Uri 的组成为:scheme://host:port/path?query ,我们通过中转 activity 匹配 scheme://host:port 部分,截获 Uri,然后通过 getPath 就可以回去到 Path,这个 Path 需要和 @Route 的 path 匹配,这样就可以实现跳转了!
可以看出这个过程就是 intent 的匹配!
1.2 数据传递 以上的两种跳转方式,都可以传递数据,我们来看下数据传递的方式:
1 2 3 4 5 Bundle params = new Bundle(); ARouter.getInstance() .build("/home/main" ) .with(params) .navigation();
注意:这个方法会覆盖掉 PostCast 内部默认创建的 Bundle;
1 2 3 4 ARouter.getInstance() .build("/home/main" ) .withFlags(); .navigation();
支持传递各种基本类型数据、对象、数组、List、可序列化数据:
1 2 3 4 5 6 7 8 9 10 11 12 ARouter.getInstance() .build("/home/main" ).withAction(..) .withBoolean(String key, boolean value) .withBundle(String key, Bundle value) .withByte(String key, byte value) .withCharSequenceArrayList(String key, ArrayList<CharSequence> value) .withParcelable(String key, Parcelable value) .withSerializable(String key, Serializable value) .withStringArrayList(String key, ArrayList<String> value) .withObject(@Nullable String key, @Nullable Object value) ... ... ... .navigation();
这些数据都会被加入到 PostCard 内部的默认创建的 Bundle 中,其实这些方法对应的就是 Bundle 中的方法!
1 2 3 4 5 6 7 8 9 10 11 12 13 ARouter.getInstance() .build("/test/activity2" ) .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom) .navigation(this ); ActivityOptionsCompat compat = ActivityOptionsCompat. makeScaleUpAnimation(v, v.getWidth() / 2 , v.getHeight() / 2 , 0 , 0 ); ARouter.getInstance() .build("/test/activity2" ) .withOptionsCompat(compat) .navigation();
1.2.1 对象传递的特殊 对于自定义的对象,不能确保它可序列化,所以这里通过 SerializationService 将其转为了 jsonstring:
1 2 3 4 5 public Postcard withObject (@Nullable String key, @Nullable Object value) { serializationService = ARouter.getInstance().navigation(SerializationService.class); mBundle.putString(key, serializationService.object2Json(value)); return this ; }
2 跳转流程 下面,我们重点分析路由跳转的流程,和数据传递的流程,忽略掉一些之前已经见过的流程!
1 ARouter.getInstance().build(...); --> _ARouter.getInstance().build(...);
无论是 path 跳转,还是 uri 跳转,ARouter 都会调用 _ARouter 的方法!
2.1 _ARouter.build 无论是 build(path),还是 build(uri),最终创建的 PostCard 都是一样的!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 protected Postcard build (String path) { if (TextUtils.isEmpty(path)) { throw new HandlerException(Consts.TAG + "Parameter is invalid!" ); } else { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { path = pService.forString(path); } return build(path, extractGroup(path)); } } protected Postcard build (Uri uri) { if (null == uri || TextUtils.isEmpty(uri.toString())) { throw new HandlerException(Consts.TAG + "Parameter invalid!" ); } else { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { uri = pService.forUri(uri); } return new Postcard(uri.getPath(), extractGroup(uri.getPath()), uri, null ); } }
2.2 Postcard 2.2.1 new Postcard 创建一个新的 Postcard:
1 2 3 4 5 6 7 8 9 10 public Postcard (String path, String group) { this (path, group, null , null ); } public Postcard (String path, String group, Uri uri, Bundle bundle) { setPath(path); setGroup(group); setUri(uri); this .mBundle = (null == bundle ? new Bundle() : bundle); }
可以看到,通过 Uri 创建的话,会多设置一个 Uri 的属性;
2.2.2 navigation 最核心的就后面的两个方法,支持传入 requestCode 和 NavigationCallback 实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public Object navigation () { return navigation(null ); } public Object navigation (Context context) { return navigation(context, null ); } public void navigation (Activity mContext, int requestCode) { navigation(mContext, requestCode, null ); } public Object navigation (Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this , -1 , callback); } public void navigation (Activity mContext, int requestCode, NavigationCallback callback) { ARouter.getInstance().navigation(mContext, this , requestCode, callback); }
对于 ARouter.getInstance().navigation,我们知道最后会调用 _ARouter.getInstance().navigation
1 ARouter.getInstance().navigation(...) --> _ARouter.getInstance().navigation(...);
2.3 _ARouter.navigation 这里我们可以看到回调的处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 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)) { return null ; } try { LogisticsCenter.completion(postcard); } catch (NoRouteFoundException ex) { logger.warning(Consts.TAG, ex.getMessage()); 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(); } }); } if (null != callback) { callback.onLost(postcard); } else { DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class); if (null != degradeService) { degradeService.onLost(context, postcard); } } return null ; } if (null != callback) { callback.onFound(postcard); } if (!postcard.isGreenChannel()) { interceptorService.doInterceptions(postcard, new InterceptorCallback() { @Override public void onContinue (Postcard postcard) { _navigation(context, postcard, requestCode, callback); } @Override public void onInterrupt (Throwable exception) { if (null != callback) { callback.onInterrupt(postcard); } logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage()); } }); } else { return _navigation(context, postcard, requestCode, callback); } return null ; }
2.3.1 LogisticsCenter.completion 完善登陆信息,这里前面有说过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 public synchronized static void completion (Postcard postcard) { if (null == postcard) { throw new NoRouteFoundException(TAG + "No postcard!" ); } RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath()); if (null == routeMeta) { Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); if (null == groupMeta) { throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]" ); } else { 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(); 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() + "]" ); } completion(postcard); } } else { postcard.setDestination(routeMeta.getDestination()); postcard.setType(routeMeta.getType()); postcard.setPriority(routeMeta.getPriority()); postcard.setExtra(routeMeta.getExtra()); Uri rawUri = postcard.getUri(); if (null != rawUri) { Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri); Map<String, Integer> paramsType = routeMeta.getParamsType(); if (MapUtils.isNotEmpty(paramsType)) { for (Map.Entry<String, Integer> params : paramsType.entrySet()) { setValue(postcard, params.getValue(), params.getKey(), resultMap.get(params.getKey())); } postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{})); } postcard.withString(ARouter.RAW_URI, rawUri.toString()); } switch (routeMeta.getType()) { case PROVIDER: Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination(); IProvider instance = Warehouse.providers.get(providerMeta); if (null == instance) { 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(); break ; case FRAGMENT: postcard.greenChannel(); default : break ; } } }
这里是
2.3.1.1 数据传递 - important 这里我们要重点看下 PostCard 中的数据是如何处理的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 postcard.setDestination(routeMeta.getDestination()); postcard.setType(routeMeta.getType()); postcard.setPriority(routeMeta.getPriority()); postcard.setExtra(routeMeta.getExtra()); Uri rawUri = postcard.getUri(); if (null != rawUri) { Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri); Map<String, Integer> paramsType = routeMeta.getParamsType(); if (MapUtils.isNotEmpty(paramsType)) { for (Map.Entry<String, Integer> params : paramsType.entrySet()) { setValue(postcard, params.getValue(), params.getKey(), resultMap.get(params.getKey())); } postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{})); } postcard.withString(ARouter.RAW_URI, rawUri.toString()); }
上面的两个常量的如下:
1 2 public static final String RAW_URI = "NTeRQWvye18AkPd6G" ;public static final String AUTO_INJECT = "wmHzgD4lOj5o4241" ;
2.3.1.2 TextUtils.splitQueryParameters 将 uri 后缀保存的 key-value 的键值对转为 Map<String, String>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static Map<String, String> splitQueryParameters (Uri rawUri) { String query = rawUri.getEncodedQuery(); if (query == null ) { return Collections.emptyMap(); } Map<String, String> paramMap = new LinkedHashMap<>(); int start = 0 ; do { int next = query.indexOf('&' , start); int end = (next == -1 ) ? query.length() : next; int separator = query.indexOf('=' , start); if (separator > end || separator == -1 ) { separator = end; } String name = query.substring(start, separator); if (!android.text.TextUtils.isEmpty(name)) { String value = (separator == end ? "" : query.substring(separator + 1 , end)); paramMap.put(Uri.decode(name), Uri.decode(value)); } start = end + 1 ; } while (start < query.length()); return Collections.unmodifiableMap(paramMap); }
2.3.1.3 LogisticsCenter.setValue 将传递的数据设置进入 Postcard 中!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 private static void setValue (Postcard postcard, Integer typeDef, String key, String value) { if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) { return ; } try { if (null != typeDef) { if (typeDef == TypeKind.BOOLEAN.ordinal()) { postcard.withBoolean(key, Boolean.parseBoolean(value)); } else if (typeDef == TypeKind.BYTE.ordinal()) { postcard.withByte(key, Byte.valueOf(value)); } else if (typeDef == TypeKind.SHORT.ordinal()) { postcard.withShort(key, Short.valueOf(value)); } else if (typeDef == TypeKind.INT.ordinal()) { postcard.withInt(key, Integer.valueOf(value)); } else if (typeDef == TypeKind.LONG.ordinal()) { postcard.withLong(key, Long.valueOf(value)); } else if (typeDef == TypeKind.FLOAT.ordinal()) { postcard.withFloat(key, Float.valueOf(value)); } else if (typeDef == TypeKind.DOUBLE.ordinal()) { postcard.withDouble(key, Double.valueOf(value)); } else if (typeDef == TypeKind.STRING.ordinal()) { postcard.withString(key, value); } else if (typeDef == TypeKind.PARCELABLE.ordinal()) { } else if (typeDef == TypeKind.OBJECT.ordinal()) { postcard.withString(key, value); } else { postcard.withString(key, value); } } else { postcard.withString(key, value); } } catch (Throwable ex) { logger.warning(Consts.TAG, "LogisticsCenter setValue failed! " + ex.getMessage()); } }
方法很简单,不多说了!
2.3.2 _ARouter._navigation 可以看到,启动过的过程就是将 Postcard 中的数据设置到 intent 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 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: final Intent intent = new Intent(currentContext, postcard.getDestination()); intent.putExtras(postcard.getExtras()); int flags = postcard.getFlags(); if (-1 != flags) { intent.setFlags(flags); } else if (!(currentContext instanceof Activity)) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } String action = postcard.getAction(); if (!TextUtils.isEmpty(action)) { intent.setAction(action); } 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 ; }
针对于 brocastreceiver,contenprovider,fragment,会拿到其实例,针对于 fragment,还会设置 Arguments!
2.3.2.1 _ARouter.startActivity 这就是最后启动过程了,其实很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void startActivity (int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) { if (requestCode >= 0 ) { 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) { ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim()); } if (null != callback) { callback.onArrival(postcard); } }
不多说了!
这里的 postcard.getOptionsBundle() 会返回一个 Bundle 是用来保存额外的启动参数,比如动画等等;
1 2 3 4 private Bundle optionsCompat; public Bundle getOptionsBundle () { return optionsCompat; }
2.4 跳转回调 我们看看 NavigationCallback 的相关接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 public interface NavigationCallback { void onFound (Postcard postcard) ; void onLost (Postcard postcard) ; void onArrival (Postcard postcard) ; void onInterrupt (Postcard postcard) ; }
3 总结 到这里 ARouter 分析就暂告一段落了;