/**
* 标准使用
* @path 路由路径
* @params 所传参数
*/
fun routerNavigation(path: String, params: Map<String, Any?>? = null) {
buildParams(ARouter.getInstance().build(path), params).navigation()
}
/**
* 解析params放到Postcard中
*/
private fun buildParams(postcard: Postcard, params: Map<String, Any?>?): Postcard {
return postcard.apply {
if (params != null && params.isNotEmpty())
for ((k, v) in params)
when (v) {
is String -> withString(k, v)
is Int -> withInt(k, v)
is Boolean -> withBoolean(k, v)
is Long -> withLong(k, v)
is Float -> withFloat(k, v)
is Double -> withDouble(k, v)
is ByteArray -> withByteArray(k, v)
is Bundle -> with(v)
is List<*>, is Map<*, *>, is Set<*> -> withObject(k, v)
is Parcelable -> withParcelable(k, v)
is Serializable -> withSerializable(k, v)
else -> withObject(k, v)
}
}
}
/**
* 进阶使用方法
* @path 路由路径
* @params 所传参数
* @activity 当前activity,配合requestCode或callback使用
* @requestCode 当需要startActivityForResult时
* @callback 使用NavigationCallback处理跳转结果(局部降级策略)
* @group 一般不传,默认就是 path 的第一段相同
* @uri 通过uri跳转时
* @flag 当需要指定flag时Intent.FLAG_ACTIVITY_CLEAR_TOP
* @enterAnim 页面进入动画
* @exitAnim 页面退出动画
* @compat ActivityOptionsCompat动画
* @greenChannel 是否使用绿色通道(跳过所有的拦截器)
*/
fun routerNavigation(
path: String,
params: Map<String, Any?>? = null,
activity: Activity? = null,
requestCode: Int = -1,
callback: NavigationCallback? = null,
group: String? = null,
uri: Uri? = null,
flag: Int? = null,
enterAnim: Int? = null,
exitAnim: Int? = null,
compat: ActivityOptionsCompat? = null,
greenChannel: Boolean = false,
) {
if (path.isNullOrEmpty()) {
buildParams(ARouter.getInstance().build(uri), params)
} else {
val postcard =
if (group.isNullOrEmpty()) ARouter.getInstance().build(path)
else ARouter.getInstance().build(path, group)
buildParams(
postcard.apply {
if (flag != null)
postcard.withFlags(flag)
if (enterAnim != null && exitAnim != null)//转场动画(常规方式)
postcard.withTransition(enterAnim, exitAnim)
if (compat != null)// 转场动画(API16+)
postcard.withOptionsCompat(compat)
if (greenChannel)//使用绿色通道(跳过所有的拦截器)
postcard.greenChannel()
}, params
)
}.navigation(activity, requestCode, callback)
}
1. 更多类型的参数
传递参数
data class TestObj(val name:String?,val age:Int?,val email:String?)
val testObj = TestObj("ljy", 18, "xxx@qq.com")
val testObj2 = TestObj("yang", 20, "www@qq.com")
val list = listOf(testObj, testObj2)
val testSerializable = TestSerializable("serial", 12)
val testParcelable = TestParcelable("parcel", 11)
routerNavigation(
PATH_ACTIVITY_LOGIN,
mapOf(
"key1" to 123,
"key2" to "aaa",
"key3" to "bbb",
"testObj" to testObj,
"list" to list,
"testSerializable" to testSerializable,
"testParcelable" to testParcelable
)
)
接收参数
@Autowired
lateinit var testObj: TestObj
@Autowired
lateinit var list: List<TestObj?>
@Autowired
lateinit var testSerializable: TestSerializable
@Autowired
lateinit var testParcelable: TestParcelable
class SchemeFilterActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val uri = intent.data
ARouter.getInstance().build(uri).navigation()
}
}
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
Postcard保存了路由跳转需要的所有信息,并且有一系列withXXX方法供我们设置
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!
private Bundle mBundle; // Data to transform
private int flags = 0; // Flags of route
private int timeout = 300; // Navigation timeout, TimeUnit.Second
private IProvider provider; // It will be set value, if this postcard was provider.
private boolean greenChannel;
private SerializationService serializationService;
private Context context; // May application or activity, check instance type before use it.
private String action;
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim = -1;
private int exitAnim = -1;
...
}
public Object navigation() {
return navigation(null);
}
public Object navigation(Context context) {
return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
_ARouter.getInstance().navigation代码如下
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//获取PretreatmentService实例
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
//如果PretreatmentService实例存在,即用户实现了预处理服务,并且onPretreatment返回了false,则拦截本次跳转
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
//最主要是调用了这一行
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
//openDebug则toast一些提示
if (debuggable()) {
// Show friendly tips for user.
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();
}
});
}
//如果NavigationCallback存在则调用其onLost,就是我们之前讲的局部降级
if (null != callback) {
callback.onLost(postcard);
} else {
// NavigationCallback不存在则看看有没有实现DegradeService,就是之前讲的全局降级
// 所以局部降级存在,则全局降级就在本次路由就不会生效了
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()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
//如果没有开启绿色通道,则调用拦截器服务,
//拦截器服务是在上面的_ARouter.afterInit中初始化的
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@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(postcard, requestCode, callback);
}
return null;
}
LogisticsCenter.completion
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//根据path获取RouteMeta信息
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
//如果group没找到
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()));
}
//在groupIndex中找对应的groupMeta
addRouteGroupDynamic(postcard.getGroup(), null);
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); // Reload
}
} else {
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());
}
//如果type是Fragment或者IProvider则开启greenChannel,也就是不用拦截器
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) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
checkInterceptorsInitStatus();
//如果拦截器list没有初始化
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);
//拦截器如果超时会回调callback.onInterrupt
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);
}
}
_execute中遍历了拦截器集合
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());
// }
}
});
}
}