ARouter
1.ARouter主要用于不同组件的路由跳转,使用时,目标对象需要使用@Route注解,规则是@Route(path = "/xxx/xxx"),这里的一级路径必须唯一,一般跟moudle名相同,二级一般使用目标对象的类名,完整的路径不可重复,不然会报错。path必须"/"
开头,且至少两级,在build方法里面有如下判断:
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
}
build(path)方法会调用extractGroup(path)获取group,有如下判断:
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
2.Arouter跳转,使用ARouter.getInstance().build("/xxx/xxx").navigation(),build里面的内容就是目标对象注解的路径,需要带参数的话可使用ARouter.getInstance().build("/xxx/xxx").with(new Bundle()).navigation(),也可以用其他类型的withXXX,支持基本类型、String、实现了Parcelable或Serializable接口的对象以及这些类型的集合和数组。此外,还支持withObject,要使用withObject,需要定义一个实现SerializationService接口的类,并加上@Route(path = "/service/json")注解,这个类需要实现解析的方法,实质上是对字符串的解析,例如:
@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
private Gson mGson;
@Override
public <T> T json2Object(String input, Class<T> clazz) {
check();
return mGson.fromJson(input,clazz);
}
@Override
public String object2Json(Object instance) {
check();
return mGson.toJson(instance);
}
@Override
public <T> T parseObject(String input, Type clazz) {
check();
return mGson.fromJson(input,clazz);
}
@Override
public void init(Context context) {
check();
}
private void check(){
if (mGson==null) {
mGson = new Gson();
}
}
}
接收方可以用@Autowired注解标记字段,并调用ARouter.getInstance().inject(this)直接赋值,不用在intent里面去取值,@Autowired(name = "xxx")可以指定key。
这里还有几点需要注意:
(1).@Autowired注解标记的字段不能是private的。
(2).withObject传递的对象不能实现Parcelable接口,实现了Parcelable接口的传递用withParcelable方法。
(3).withObject有时候会出现接收方解析出的对象为空的情况,考虑以下解决思路:
实体类提供显示的构造器;
实体类的成员变量改成public类型;
接收方的@Autowired注解的字段改成和传入的key名称一样。
(4).withObject传递List或者map时,接收方@Autowired注解的字段不能是他们的实现类,如ArrayList。
3.Arouter在使用时,使用Arouter的注解,如@Route、@Autowired等,build.gradle必须加如下语句,否则注解无法解析:
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ AROUTER_MODULE_NAME : project.getName() ]
}
}
}
}
dependencies {
...
//ARouter
api 'com.alibaba:arouter-api:1.4.0'
annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
}
4.Arouter的build也可以传入Uri,Uri必须指定path,build方法返回的是Postcard对象,在PendingIntent中需要传入Intent,可以用如下方式获取class:
public static Class getRouterClass(String path){
Postcard postcard = ARouter.getInstance().build(path);
LogisticsCenter.completion(postcard);
return postcard.getDestination();
}
startActivityForResult的实现方式是navigation(Context context,int requestCode),Intent设置flag的方式用withFlags方法,withTransition方法指定转场动画。
5.Arouter可以自定义拦截器,例如:
@Interceptor(name = "init",priority = 1)//priority 指定拦截顺序,越小排越前面
public class InitInterceptorImpl implements IInterceptor {
private static final String TAG = "InitInterceptorImpl";
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
String path = postcard.getPath();
LogUtils.LogI(TAG,path);
if(ARouterManager.PATH_AIDL_AIDL.equals(path)){//根据目标判断
boolean v = KeyValueUtils.getInstance().get("hasInMeg");//判断条件
if(v){
//不拦截,可以做其他操作,比如加参数
callback.onContinue(postcard);
}else {
//表示拦截
callback.onInterrupt(new Throwable("请先打开Messenger页面"));
}
}else{
callback.onContinue(postcard);
}
}
@Override
public void init(Context context) {
LogUtils.LogI(TAG,"初始化拦截器");
}
}
在跳转的时候调用navigation(Context context, NavigationCallback callback)方法。NavigationCallback的回调方法如下:
@Override
public void onFound(Postcard postcard) {
LogUtils.LogE(TAG,"onFound");//找到路由
}
@Override
public void onLost(Postcard postcard) {
LogUtils.LogE(TAG,"onLost");//未找到路由
}
@Override
public void onArrival(Postcard postcard) {
LogUtils.LogE(TAG,"onArrival");//跳转成功
}
@Override
public void onInterrupt(Postcard postcard) {
LogUtils.LogE(TAG,"onInterrupt");//被拦截
}
注意NavigationCallback 的onInterrupt方法是在子线程,要进行UI操作需要切换线程。
6.降级策略分两种,用于找不到路由的后续处理,第一种是上面的NavigationCallback 回调的onLost方法,这个是单独降级。另外一种是实现DegradeService 接口,是全局降级,例如:
@Route(path = "/degrade/noInit")
public class NoInitDealService implements DegradeService {
private static final String TAG = "NoInitDealService";
@Override
public void onLost(Context context, Postcard postcard) {
LogUtils.LogE(TAG,"降级策略");
}
@Override
public void init(Context context) {
}
}
单独降级的优先级高于全局,也就是说写了回调,就不会走全局的onLost。
7.自定义服务可用于组件间调用接口,需要提供一个继承IProvider的接口,例如:
public interface ISayHelloService extends IProvider {
void say(String msg);
}
然后在需要实现的组件里面新增一个实现类,注意这个类不能为内部类,例如:
@Route(path = "/main/say")
public class SayImpl implements ISayHelloService {
private static final String TAG = "SayImpl";
private ICallBack mICallBack;
@Override
public void say(String msg) {
if(mICallBack!=null){
mICallBack.callBack(msg);
}
}
public void setCallBack(ICallBack iCallBack) {
mICallBack = iCallBack;
}
@Override
public void init(Context context) {
}
public interface ICallBack{
void callBack(String msg);
}
}
其他组件调用接口可以用以下几种方式:
注解:
@Autowired
ISayHelloService s1;
@Autowired(name = "/main/say")
ISayHelloService s2;
ARouter.getInstance().inject(this);
s1.say("1号");
s2.say("2号");
主动查找:
private ISayHelloService s3;
private ISayHelloService s4;
s3 = ARouter.getInstance().navigation(ISayHelloService.class);
s3.say("3号");
s4 = (ISayHelloService) ARouter.getInstance().build("/main/say").navigation();
s4.say("4号");
这里要注意,如果这个接口有多个实现,一定要根据byname的方式来查找,即s2、s4的方式。
8.原理分析:
Arouter的主要原理是通过注解和注解解释器生成java文件,在调用ARouter.init(this)方法时,调用_ARouter的init方法:
public static void init(Application application) {
if (!hasInit) {
//...
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
//...
}
}
在里面调用LogisticsCenter的init方法,这里初始化了一个主线程的handler:
protected static synchronized boolean init(Application application) {
//...
LogisticsCenter.init(mContext, executor);
//...
mHandler = new Handler(Looper.getMainLooper());
return true;
}
LogisticsCenter的init方法里面,找到前面通过注解解释器生成的java文件类名集合,然后循环调用对象的loadInto,loadInto把path和注解的目标类放到路由表里面:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
//...
Set<String> routerMap;
//...
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
//...
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
//这里先保存各个分组对应的IRouteGroup类,IRouteGroup里面保存了各个路径对应的RouteMeta对象,RouteMeta对象的RouteType是ACTIVITY,且保存了目标class对象
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if ( className.startsWith ( ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS ) ) {
//这里根据priority保存了IInterceptor对象
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
//这里根据服务类型,保存了对应的RouteMeta对象,RouteMeta对象里面有目标服务的class对象
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
//...
getFileNameByPackageName方法扫描目标包下面的所有类的类名:
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
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) {
}
}
parserCtl.countDown();
}
}
});
}
parserCtl.await();
return classNames;
}
这里路由表的初始化就完成了,在调用页面跳转的时候,ARouter.getInstance().build(uri)返回的是一个PostCard对象,navigation方法会调用Arouter对象的navigation方法,接着调用_ARouter的navigation方法,重点看下_ARouter的navigation方法:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//...
//这个方法里面会从路由表里面,根据path找到RouteMeta对象,找到了就将目标对象的class赋给PostCard,如果没找到,会先找IRouteGroup对象,并主动调用IRouteGroup的loadInto方法,把对应的class放进路由表,然后递归回来重新找。
LogisticsCenter.completion(postcard);
//..try-catch块里面,如果没找到目标对象,会回调 callback.onLost(postcard)
//没抛异常,说明找到了,会回调onFound
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);
}
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
上面的方法没拦截的情况都会走_navigation方法,这里面做了具体的跳转:
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)) { // Non activity, need less one flag.
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;
}
跳转完成了,那些参数是怎么自动赋值的呢,主要逻辑在ARouter.getInstance().inject(this)方法里面,在_ARouter的inject方法里面,会调用AutowiredService的autowire方法:
static void inject(Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
AutowiredService的实现类AutowiredServiceImpl里面,autowire做了什么操作呢:
@Override
public void autowire(Object instance) {
String className = instance.getClass().getName();
try {
if (!blackList.contains(className)) {
ISyringe autowiredHelper = classCache.get(className);
if (null == autowiredHelper) { // No cache.
//这里是重点,找到目标类,并调用inject方法,在注解自动生成的$$ARouter$$Autowired类里面,inject方法会将传入的target对象强转为本身的类型,然后对注解的对象进行赋值,所赋的值是通过getIntent().getXXXExtra()的方式获取的
autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
autowiredHelper.inject(instance);
classCache.put(className, autowiredHelper);
}
} catch (Exception ex) {
blackList.add(className); // This instance need not autowired.
}
}