文章目录
在使用WMRouter实现通过Path来跳转目标Fragment时,由于自己的问题给目标Fragment的构造函数传了一个Context参数,导致跳转不成功问题。在查找这个问题中熟悉了WMRouter的源码,在这里记录一下。WMRouter的详细介绍参考 WMRouter:美团外卖Android开源路由框架。
1. APT注解处理
WMRouter采用APT来对注解进行处理并生成相应模版代码,下面看一下通过Path实现Fragment跳转的注解处理过程。首先对目标Fragment添加注解:
@RouterPage(path = ["/fragment/paging/test"])
class PagingTestFragment()
生成的模版代码为:
//位置:com.sankuai.waimai.router.generated/
public class PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26 implements IPageAnnotationInit {
public void init(PageAnnotationHandler handler) {
handler.register("/fragment/paging/test", new FragmentTransactionHandler("com.yy.oms.logistics.home.fragment.PagingTestFragment"));
}
}
//位置:com.sankuai.waimai.router.generated.service/
public class ServiceInit_ac827e6cbb5a2489f812c8baa8e9ff42 {
public static void init() {
ServiceLoader.put(IPageAnnotationInit.class, "com.sankuai.waimai.router.generated.PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26", com.sankuai.waimai.router.generated.PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26.class, false);
}
}
2. 初始化
对于自动生成代码的处理往往是通过Gradle插件来完成的。
2.1. Plugin+Transform编译处理
在业务中定义一个Plugin来注册一个Transform:
class XxxPlugin : Plugin<Project> {
override fun apply(p0: Project) {
val android = p0.extensions.getByType(AppExtension::class.java)
p0.extensions.create(
XxxPluginExtension.getExtensionName(),
XxxPluginExtension::class.java
)
android.registerTransform(XxxTransform(p0, null))
}
}
这里的XxxTransform类是用来处理通过SPI定义的类的,看一下其主要方法:
class XxxTransform(val project: Project, var extension: XxxPluginExtension?) : Transform() {
//spi加载注册的类
val transforms = ServiceLoaderUtils.loadClassTransforms(project)
override fun transform(invocation: TransformInvocation) {
//编译前
val transformInvocation = XxxTransformInvocation(invocation, this)
transforms.forEach {
it.beforeTransform(transformInvocation.XxxContext, project)
}
//编译开始
if (transformInvocation.isIncremental) {
transformIncremental(transformInvocation)
} else {
invocation.outputProvider.deleteAll()
transformFull(transformInvocation)
}
//编译后
transformInvocation.onPostTransform()
transforms.forEach {
it.afterTransform(transformInvocation.XxxContext)
}
}
//增量编译
private fun transformIncremental(invocation: XxxTransformInvocation) {
invocation.inputs.parallelStream().forEach {
TransformJarUtils.transformJarInputs(it.jarInputs, invocation)
TransformClassUtils.transformClassFile(invocation, it.directoryInputs)
}
}
//全量编译
private fun transformFull(invocation: XxxTransformInvocation) {
invocation.inputs.parallelStream().forEach {
TransformJarUtils.transformJarInputsFull(it.jarInputs, invocation)
TransformClassUtils.transformClassFileFull(invocation, it.directoryInputs)
}
}
}
这里先调用loadClassTransforms()方法加载通过SPI注册的类,其定义在ServiceLoaderUtils静态类中:
//ServiceLoaderUtils.java
fun loadClassTransforms(project: Project): ArrayList<IClassTransform> {
val list = ArrayList<IClassTransform>()
ServiceLoader.load(IClassTransform::class.java).iterator().forEach {
it.getTransformExtension()?.let {
project.extensions.create(it.getExtensionName(), it.javaClass)
}
list.add(it)
}
list.sortBy {
-it.priority()
}
return list
}
这里调用ServiceLoader来将META-INFO/services/下配置文件中的类实例化。
2.2. SPI处理
这里定义的接口为IClassTransform.kt,定义一个实现类WMRouterTransform.kt,并用AutoService注解:
@AutoService(IClassTransform::class)
class WMRouterTransform : IClassTransform {}
AutoService是Google提供的用于生成SPI配置文件的,可以参考Google AutoService。编译后会在同一根目录的build/libs/module名.jar/META-INFO/services/目录下生成配置文件:com.xx.xx.IClassTransform,其文件内容为:
com.yy.oms.plugin.transforms.router.WMRouterTransform
下面看一下WMRouterTransform是如何初始化Service的。
2.3. Service初始化
在业务中,会使用ASM对生成的代码进行收集并存储在内存中,然后可以对缓存的信息进行查找,该过程在WMRouterTransform类中完成:
//WMRouterTransform.kt
private var targetClasses: MutableSet<String> = Collections.newSetFromMap(ConcurrentHashMap())
private const val GEN_PKG_SERVICE = "$GEN_PKG/service"
//查找GEN_PKG_SERVICE路径下的文件并放在targetClasses中
override fun beforeTransform(context: TransformContext, project: Project) {
val cost = measureTimeMillis {
context.getClassNodeCache()
.findClassNodeCache { it.name?.startsWith(GEN_PKG_SERVICE) ?: false }
?.forEach { targetClasses.add(it.name!!) }
}
}
override fun afterTransform(context: TransformContext) {
execute(context)
super.afterTransform(context)
}
private fun execute(context: TransformContext) {
if (targetClasses.isEmpty()) return
val className: String = SERVICE_LOADER_INIT
val cost = measureTimeMillis {
try {
// write
val writer = ClassWriter(ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS)
val cv: ClassVisitor = object : ClassVisitor(Opcodes.ASM5, writer) {}
cv.visit(50, Opcodes.ACC_PUBLIC, className, null, "java/lang/Object", null)
val mv = cv.visitMethod(Opcodes.ACC_PUBLIC or Opcodes.ACC_STATIC, INIT_METHOD,
"()V", null, null)
mv.visitCode()
//插入代码:调用service的init()方法
for (clazz in targetClasses) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC, clazz, "init", "()V", false)
}
mv.visitMaxs(0, 0)
mv.visitInsn(Opcodes.RETURN)
mv.visitEnd()
cv.visitEnd()
// output file
val dest = File(context.getOutPutDirPath(), className + SdkConstants.DOT_CLASS).touch()
writer.toByteArray().redirect(dest)
getLogger().i("$TAG execute generated file $dest, length ${dest.length()}")
} catch (e: Exception) {
getLogger().i("$TAG execute error: $e")
}
}
getLogger().i("$TAG execute cost ${cost}ms, generated class $className")
}
这里调用visitMethodInsn()方法实际调用的是ServiceInit_ac827e6cbb5a2489f812c8baa8e9ff42类的init()方法,在init()方法中会调用ServiceLoader的put()方法:
public static void put(Class interfaceClass, String key, Class implementClass, boolean singleton) {
ServiceLoader loader = SERVICES.get(interfaceClass);
if (loader == null) {
loader = new ServiceLoader(interfaceClass);
SERVICES.put(interfaceClass, loader);
}
loader.putImpl(key, implementClass, singleton);
}
这里的implementClass即是PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26类的Class,进一步将implementClass为参数调用putImpl()方法:
private void putImpl(String key, Class implementClass, boolean singleton) {
if (key != null && implementClass != null) {
mMap.put(key, new ServiceImpl(key, implementClass, singleton));
}
}
将implementClass包装成一个ServiceImpl对象并存放在mMap
中。
2.4. Annotation初始化
Annotation初始化的调用链路为:
从调用链可以看到最终会调用DefaultAnnotationLoader的load()方法:
@Override
public <T extends UriHandler> void load(T handler,
Class<? extends AnnotationInit<T>> initClass) {
List<? extends AnnotationInit<T>> services = Router.getAllServices(initClass);
for (AnnotationInit<T> service : services) {
service.init(handler);
}
}
这里就会拿到Service初始化时记录在mMap中所有的ServiceImpl对象,然后执行其init()方法,这里就会执行PageAnnotationInit_7e09d0c7c2c720bbbacddb0cedbfec26类的init()方法,看一下“APT注解处理”一节的代码可知会调用PathHandler类的register()方法:
/**
* 注册一个子节点
*
* @param path path
* @param target 支持ActivityClassName、ActivityClass、UriHandler
* @param exported 是否允许外部跳转
* @param interceptors 要添加的interceptor
*/
public void register(String path, Object target, boolean exported,
UriInterceptor... interceptors) {
if (!TextUtils.isEmpty(path)) {
path = RouterUtils.appendSlash(path);
UriHandler parse = UriTargetTools.parse(target, exported, interceptors);
UriHandler prev = mMap.put(path, parse);
if (prev != null) {
Debugger.fatal("[%s] 重复注册path='%s'的UriHandler: %s, %s", this, path, prev, parse);
}
}
}
这里的target是一个FragmentTransactionHandler对象,将该对象转化成类型为UriHandler的parse对象并保存在一个Map中。
3. 跳转逻辑
显示一个Fragment必须先实例化该Fragment对象,通过一下代码实现:
FragmentBuildUriRequest(requireContext(),
"/fragment/paging/test".pageUri)
.getFragment()
suspend fun AbsFragmentUriRequest.getFragment() = suspendCancellableCoroutine<Fragment?> {
onComplete(object : OnCompleteListener {
override fun onSuccess(request: UriRequest) {
val fragment = request.getField(Fragment::class.java, FragmentBuildUriRequest.FRAGMENT)
it.resume(fragment)
}
override fun onError(request: UriRequest, resultCode: Int) {
it.resume(null)
}
}).start()
}
onComplete()方法添加了一个监听对象,最终会调用FragmentBuildUriRequest的父类UriRequest的start()方法,其调用链如图:
在分析后面逻辑之前需要明白Handler链的结构:DefaultRootUriHandler下有三个次级Handler(如:PageAnnotationHandler、UriAnnotationHandler、RegexAnnotationHandler),各个次级Handler下又有子Handler(如:FragmentTransactionHandler)。看一下DefaultRootUriHandler注册次级Handle的代码:
public DefaultRootUriHandler(Context context,
@Nullable String defaultScheme, @Nullable String defaultHost) {
super(context);
mPageAnnotationHandler = createPageAnnotationHandler();
mUriAnnotationHandler = createUriAnnotationHandler(defaultScheme, defaultHost);
mRegexAnnotationHandler = createRegexAnnotationHandler();
// 处理RouterPage注解定义的内部页面跳转,如果注解没定义,直接结束分发
addChildHandler(mPageAnnotationHandler, 300);
// 处理RouterUri注解定义的URI跳转,如果注解没定义,继续分发到后面的Handler
addChildHandler(mUriAnnotationHandler, 200);
// 处理RouterRegex注解定义的正则匹配
addChildHandler(mRegexAnnotationHandler, 100);
// 添加其他用户自定义Handler...
// 都没有处理,则尝试使用默认的StartUriHandler直接启动Uri
addChildHandler(new StartUriHandler(), -100);
// 全局OnCompleteListener,用于输出跳转失败提示信息
setGlobalOnCompleteListener(DefaultOnCompleteListener.INSTANCE);
}
因此上图的调用链中会先执行PageAnnotationHandler类的handleInternal()方法:
//PageAnnotationHandler.java
@Override
protected void handleInternal(@NonNull final UriRequest request,
@NonNull final UriCallback callback) {
UriHandler h = getChild(request);
if (h != null) {
h.handle(request, new UriCallback() {
@Override
public void onNext() {
handleByDefault(request, callback);
}
@Override
public void onComplete(int resultCode) {
callback.onComplete(resultCode);
}
});
} else {
handleByDefault(request, callback);
}
}
//PathHandler.java
private UriHandler getChild(@NonNull UriRequest request) {
String path = request.getUri().getPath();
if (TextUtils.isEmpty(path)) {
return null;
}
path = RouterUtils.appendSlash(path);
if (TextUtils.isEmpty(mPathPrefix)) {
return mMap.get(path);
}
if (path.startsWith(mPathPrefix)) {
return mMap.get(path.substring(mPathPrefix.length()));
}
return null;
}
这里getChild()方法从mMap中取出的即是前面往其中添加的FragmentTransactionHandler对象,因此又会执行其handleInternal()方法:
@Override
protected void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback) {
StartFragmentAction action = request.getField(StartFragmentAction.class,
StartFragmentAction.START_FRAGMENT_ACTION);
if (action == null) {
Debugger.fatal("FragmentTransactionHandler.handleInternal()应返回的带有StartFragmentAction");
callback.onComplete(UriResult.CODE_BAD_REQUEST);
return;
}
if (!request.hasField(FRAGMENT_CLASS_NAME)) {
//判断一下,便于被替换
request.putField(FRAGMENT_CLASS_NAME, mClassName);
}
// Extra
Bundle extra = request.getField(Bundle.class, FIELD_INTENT_EXTRA);
//*关键代码,反射构造目标Fragment对象*
boolean success = action.startFragment(request, extra);
// 完成
callback.onComplete(success ? UriResult.CODE_SUCCESS : UriResult.CODE_BAD_REQUEST);
}
先从request中获取一个StartFragmentAction对象,然后调用其startFragment()方法初始化目标Fragment并执行Fragment事务:
@Override
public boolean startFragment(@NonNull UriRequest request, @NonNull Bundle bundle)
throws ActivityNotFoundException, SecurityException {
String fragmentClassName =
request.getStringField(FragmentTransactionHandler.FRAGMENT_CLASS_NAME);
if (TextUtils.isEmpty(fragmentClassName)) {
Debugger.fatal("FragmentTransactionHandler.handleInternal()应返回的带有ClassName");
return false;
}
if (mContainerViewId == 0) {
Debugger.fatal("FragmentTransactionHandler.handleInternal()mContainerViewId");
return false;
}
try {
Fragment fragment =
Fragment.instantiate(request.getContext(), fragmentClassName, bundle);
if (fragment == null) {
Debugger.e("FragmentTransactionUriRequest fragment init error");
return false;
}
Debugger.i("FragmentTransactionUriRequest fragment: ", fragment);
FragmentTransaction transaction = mFragmentManager.beginTransaction();
switch (mStartType) {
case TYPE_ADD:
transaction.add(mContainerViewId, fragment, mTag);
break;
case TYPE_REPLACE:
transaction.replace(mContainerViewId, fragment, mTag);
break;
default:
Debugger.e("BuildStartFragmentAction:" + mStartType);
}
if (mAllowingStateLoss) {
transaction.commitAllowingStateLoss();
} else {
transaction.commit();
}
return true;
} catch (Exception e) {
}
}
}
这里的代码很熟悉了,我们看一个关键点:反射构造目标Fragment实例。
//Fragment.java
public static Fragment instantiate(@NonNull Context context, @NonNull String fname,
@Nullable Bundle args) {
try {
Class<? extends Fragment> clazz = FragmentFactory.loadFragmentClass(
context.getClassLoader(), fname);
//通过构造器构造目标Fragment
Fragment f = clazz.getConstructor().newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.setArguments(args);
}
return f;
} catch (java.lang.InstantiationException e) {}
}
//Class.java
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getConstructor0(parameterTypes, Member.PUBLIC);
}
可以看到通过目标Fragment的无参构造器来初始化目标Fragment的,因此在文章开头提到对于含有参数的目标Fragment来说,WMRouter是不能成功构造出该Fragment实例并完成Fragment显示功能的。
4. 自动获取intent/aegument数据原理
先看一下使用姿势,先在一个Fragment中定义用@Autowired注解一个参数,该参数名也是argument中的key,如下:
@RouterPage(path = ["/fragment/test/router"])
class TestRouterFragment : Fragment() {
@JvmField
@Autowired
var value1: Boolean? = false
@JvmField
@Autowired
var value2: Byte? = 1
@JvmField
@Autowired
var value3: Short? = 1
}
然后通过APT会生成一个TestRouterFragment$$WMRouter$$Autowired类,该类中只有一个inject()方法,该方法的作用是获取intent/argument中的数据:
public class TestRouterFragment$$WMRouter$$Autowired implements ISyringe {
@Override
public void inject(Object target) {
TestRouterFragment substitute = (TestRouterFragment)target;
substitute.value1 = substitute.getArguments().getBoolean("value1", substitute.value1);
substitute.value2 = substitute.getArguments().getByte("value2", substitute.value2);
substitute.value3 = substitute.getArguments().getShort("value3", substitute.value3);
}
}
下面看一下如何通过APT来生成上面的类,源码在WMRouter库AutowiredProcessor类中:
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AutowiredProcessor extends BaseProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set != null && !set.isEmpty()) {
try {
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
generateHelper();
} catch (Exception error) { }
return true;
}
return false;
}
private void generateHelper() throws IOException, IllegalAccessException {
TypeElement typeISyringe = elements.getTypeElement(ISYRINGE);
TypeMirror activityTm = elements.getTypeElement(ACTIVITY_CLASS).asType();
TypeMirror fragmentTm = elements.getTypeElement(FRAGMENT_CLASS).asType();
TypeMirror fragmentTmV4 = elements.getTypeElement(FRAGMENT_V4_CLASS).asType();
// Build input param name.
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
if (parentAndChild != null && !parentAndChild.isEmpty()) {
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
// Build method : 'inject'
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
TypeElement parent = entry.getKey();
List<Element> childs = entry.getValue();
String qualifiedName = parent.getQualifiedName().toString();
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
//生成的类名,NAME_OF_AUTOWIRED = $$ + WMRouter + $$ + "Autowired";
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
// todo 改成static void
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addJavadoc("\n")
.addJavadoc(WARNING_TIPS2)
.addSuperinterface(ClassName.get(typeISyringe))
.addModifiers(PUBLIC);
injectMethodBuilder
.addStatement("$T substitute = ($T)target", ClassName.get(parent),
ClassName.get(parent));
// Generate method body, start inject.
//有多少个注解就有多少个element
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);
String fieldName = element.getSimpleName().toString();
String originalValue = "substitute." + fieldName;
String statement =
"substitute." + fieldName + " = " + buildCastCode(element,
fieldConfig.typeKind()) +
"substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(),
activityTm)) { // Activity, then use getIntent()
isActivity = true;
statement += "getIntent().";
} else if (types.isSubtype(parent.asType(), fragmentTm) ||
types.isSubtype(parent.asType(),
fragmentTmV4)) { // Fragment, then use getArguments()
statement += "getArguments().";
} else {
throw new IllegalAccessException("The field [" + fieldName +
"] need autowired from intent, its parent must be activity or fragment!");
}
statement = buildStatement(originalValue, statement,
typeUtils.typeExchange(element, fieldConfig.typeKind()), isActivity);
injectMethodBuilder.addStatement(statement,
fieldConfig.name().isEmpty() ? fieldName : fieldConfig.name());
// Validator
if (fieldConfig.required() && !element.asType().getKind()
.isPrimitive()) { // Primitive wont be check.
injectMethodBuilder
.beginControlFlow("if (null == substitute." + fieldName + ")");
injectMethodBuilder.addStatement(
"$T.e(\"" + TAG + "\", \"The field '" + fieldName +
"' is null, in class '\" + $T.class.getName() + \"!\")",
AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
helper.addMethod(injectMethodBuilder.build());
// Generate autowire helper
JavaFile.builder(packageName, helper.build()).build().writeTo(filer);
}
}
}
}
这里又是使用AutoService来处理SPI类的,会在MATR-INF/services/下生成名为javax.annotation.processing.Processor的配置文件,文件内容有:com.sankuai.waimai.router.compiler.AutowiredProcessor。以上APT代码即会生成TestRouterFragment W M R o u t e r WMRouter WMRouterAutowired类,下面需要在Activity/Fragment的onCreate()方法中调用该类的inject()方法。这里可以使用和WMRouterTransform相类似的处理办法:
@AutoService(IClassTransform::class)
class AutowiredTransform : IClassTransform {
override fun transform(context: TransformContext, classNode: ClassNode): ClassNode {
if (isTargetClassNode(classNode)) {
val injector = "${classNode.name}$AUTO_WIRED_CLASS_SUFFIX"
classNode.methods.find {
it.name == "onCreate" && it.desc == "(Landroid/os/Bundle;)V"
}?.let {
// 下面的 ASM 代码对应的 java代码:(插件生成)
// (new TestActivity$$WMRouter$$Autowired()).inject(this);
val mv = MethodNode()
mv.visitTypeInsn(Opcodes.NEW, injector)
mv.visitInsn(Opcodes.DUP)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, injector, "<init>", "()V", false)
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, injector, "inject", "(Ljava/lang/Object;)V", false)
it.instructions.insert(mv.instructions)
return classNode
}
getLogger().i("$TAG transform ${classNode.name} find OnCreate failed, create onCreate and inject $injector")
// 下面的 ASM 代码对应的 java代码:(插件生成)
// public void onCreate(Bundle bundle) {
// (new TestActivity$$WMRouter$$Autowired()).inject(this);
// super.onCreate(bundle);
// }
val mv: MethodVisitor = classNode.visitMethod(Opcodes.ACC_PUBLIC, "onCreate",
"(Landroid/os/Bundle;)V", null, null)
mv.visitCode()
mv.visitTypeInsn(Opcodes.NEW, injector)
mv.visitInsn(Opcodes.DUP)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, injector, "<init>", "()V", false)
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, injector, "inject", "(Ljava/lang/Object;)V", false)
mv.visitVarInsn(Opcodes.ALOAD, 0)
mv.visitVarInsn(Opcodes.ALOAD, 1)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classNode.superName, "onCreate", "(Landroid/os/Bundle;)V",
false)
mv.visitInsn(Opcodes.RETURN)
mv.visitMaxs(2, 2)
mv.visitEnd()
classNode.visitEnd()
return classNode
}
return classNode
}
}