每个Android应用启动时,都会先创建一个Application。通常在Application里我们会做一些应用初始化的操作,常见的有第三方SDK初始化。在应用组件化之后,组件与壳工程是隔离开来的,但是组件有时候也需要获取应用的Application,也需要在应用启动时进行初始化。这就涉及到组件的生命周期管理问题。
最简单的实现方式如下:
public interface IAppLike {
void onCreate();
void onTerminate();
void onLowMemory();
void onTrimMemory(int level);
}
public class NewsDelegate implements IApplicationDelegate {
@Override
public void onCreate() {
Log.i("hx", "NewsDelegate-onCreate");
}
@Override
public void onTerminate() {
Log.i("hx", "NewsDelegate-onTerminate");
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
}
@Override
public void onCreate() {
super.onCreate();
MainDelegate mainDelegate = new MainDelegate();
NewsDelegate newsDelegate = new NewsDelegate();
MusicDelegate musicDelegate = new MusicDelegate();
MimeDelegate mimeDelegate = new MimeDelegate();
mainDelegate.onCreate();
newsDelegate.onCreate();
musicDelegate.onCreate();
mimeDelegate.onCreate();
}
看起来貌似很简单,根本没什么技术含量,但是实际运用时,你会发现很多问题:
(1)组件初始化先后顺序问题:
前面介绍过,上层业务组件是依赖下层业务组件的,如果下层组件在应用启动时也需要初始化,那么我们在加载组件时,必然要先加载下层组件,否则加载上层组件时可能会出现问题。但是组件这么多,我们怎么确定要先加载谁后加载谁呢,当然你可以手动维护,代码里写死,但是当业务越来越多、时间越来越久,肯定不灵活,你新加一个业务组件进来,你都需要确定组件初始化先后顺序。所以,我们必须有个机制来确定组件初始化先后顺序。
类似线程优先级一样, 为每个组件定义了一个优先级,通过重写getPriority()方法可以设置组件的优先级。优先级范围从[1-10],默认优先级都为5,下层组件或需要先初始化的组件,优先级设置高一点。这样我们在加载组件的时候,先对所有组件的优先级进行排序,优先级高的排前面,然后再按顺序进行加载组件,就可解决这个问题了。
(2)自动加载问题
这里需要在壳工程代码里,手动构建各个组件的Delegate类。如果很多个组件都有实现该类,那在集成时得一个一个找出这些类,并且新增加一个组件,你都有可能要去修改壳工程代码,这样显然是不灵活且不利于代码维护的。如果能自动读取并加载这些Delegate类,那显然是极好的,这里有2种方式来实现:
应用启动时通过包名全局扫描源代码文件并找到代理类,通过反射去加载并初始化组件。这种方式只需要在组件中实现代理类即可,实现起来比较简单,但是全局扫描有性能的损耗。
通过自定义gradle插件和字节码插入技术来实现AOP,在编译期间在BaseApplication生命周期中动态注入字节码,实现调用代理类的对应方法。
在BaseApplication初始化时扫描出所有组件的代理类,再通过反射加载并初始化。这里也有个问题,所有组件的代理类散落在各个不同的组件中,包名各不相同,当扫描到class文件后判断条件就必须是【class文件全类命.equal(组件1代理类全类命) || class文件全类命.equal(组件2代理类全类命) || …】,是不是很麻烦,这里我们可以采取apt技术,通过编译时注解动态生成这些代理类的辅助文件,在辅助文件中调用代理类的方法,而这些辅助文件的包名一样,这样我们只需要扫描辅助文件就可以了,判断条件就变成【class文件包名.startWith(辅助文件包名)】,是不是简单多了。
初步思路:
初步技术难点:
需要了解APT技术,怎么在编译时动态生成java代码;关于编译时注解可以参考详解Android注解 Annotation这篇文章。
应用在运行时,怎么能扫描到某个包名下有多少个class,以及他们的名称呢;
在Android Studio中,新建一个Java Library module,命名为lifecycle-annotation,在该module中创建一个注解类,同时创建一个后面要生成代理类的相关配置文件。
注解类:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface AppLifeCycle { }
配置文件:
public class LifeCycleConfig {
/*** 生成代理类的包名 */
public static final String PROXY_CLASS_PACKAGE_NAME = "com.example.lifecycle.apt.proxy";
/*** 生成代理类统一的后缀 */
public static final String PROXY_CLASS_SUFFIX = "$$Proxy";
/*** 生成代理类统一的前缀 */
public static final String PROXY_CLASS_PREFIX = "Watson$$"; }
新建一个Java Library module,命名为lifecycle-apt,在该module里实现我们自己的注解处理器。
在build.gradle里修改配置为:
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.auto.service:auto-service:1.0-rc2'
implementation project(':lifecycle-annotation')
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
接下来就是实现我们自己的注解处理器了:
@AutoService(Processor.class)
public class AppLikeProcessor extends AbstractProcessor {
private Elements mElementUtils;
private Map<String, AppLikeProxyClassCreator> mMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mElementUtils = processingEnvironment.getElementUtils();
}
/*** 支持解析的注解 */
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = new LinkedHashSet<>();
set.add(AppLifeCycle.class.getCanonicalName());
return set;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(AppLifeCycle.class);
mMap.clear();
for (Element element : elements) {
if (!element.getKind().isClass()) {
throw new RuntimeException("Annotation AppLifeCycle can only be used in class.");
}
TypeElement typeElement = (TypeElement) element;
//这里检查一下,使用了该注解的类,同时必须要实现com.example.lifecycle.api.IAppLike接口,否则会报错,因为我们要实现一个代理类
List<? extends TypeMirror> mirrorList = typeElement.getInterfaces();
if (mirrorList.isEmpty()) {
throw new RuntimeException(typeElement.getQualifiedName() + " must implements interface com.example.lifecycle.api.IAppLike");
}
boolean checkInterfaceFlag = false;
for (TypeMirror mirror : mirrorList) {
if ("com.example.lifecycle.api.IAppLike".equals(mirror.toString())) {
checkInterfaceFlag = true;
}
}
if (!checkInterfaceFlag) {
throw new RuntimeException(typeElement.getQualifiedName() + " must implements interface com.example.lifecycle.api.IAppLike");
}
String fullClassName = typeElement.getQualifiedName().toString();
if (!mMap.containsKey(fullClassName)) {
System.out.println("process class name : " + fullClassName);
AppLikeProxyClassCreator creator = new AppLikeProxyClassCreator(mElementUtils, typeElement);
mMap.put(fullClassName, creator);
}
}
System.out.println("start to generate proxy class code");
for (Map.Entry<String, AppLikeProxyClassCreator> entry : mMap.entrySet()) {
String className = entry.getKey();
AppLikeProxyClassCreator creator = entry.getValue();
System.out.println("generate proxy class for " + className);
try {
JavaFileObject jfo = processingEnv.getFiler().createSourceFile(creator.getProxyClassFullName());
Writer writer = jfo.openWriter();
writer.write(creator.generateJavaCode());
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return true;
}
}
public class AppLikeProxyClassCreator {
private Elements mElementUtils;
private TypeElement mTypeElement;
private String mProxyClassSimpleName;
public AppLikeProxyClassCreator(Elements elements, TypeElement typeElement) {
mElementUtils = elements;
mTypeElement = typeElement;
mProxyClassSimpleName = LifeCycleConfig.PROXY_CLASS_PREFIX + mTypeElement.getSimpleName().toString() + LifeCycleConfig.PROXY_CLASS_SUFFIX;
}
/**
* 获取要生成的代理类的完整类名 * * @return
*/
public String getProxyClassFullName() {
String name = LifeCycleConfig.PROXY_CLASS_PACKAGE_NAME + "." + mProxyClassSimpleName;
return name;
}
/**
* 生成java代码
*/
public String generateJavaCode() {
StringBuilder sb = new StringBuilder();
//设置包名
sb.append("package ").append(LifeCycleConfig.PROXY_CLASS_PACKAGE_NAME).append(";\n\n");
//设置import部分
sb.append("import android.content.Context;\n");
sb.append("import com.example.lifecycle.api.IAppLike;\n");
sb.append("import ").append(mTypeElement.getQualifiedName()).append(";\n\n");
sb.append("public class ").append(mProxyClassSimpleName).append(" implements ").append("IAppLike ").append(" {\n\n");
//设置变量
sb.append(" private ").append(mTypeElement.getSimpleName().toString()).append(" mAppLike;\n\n");
//构造函数
sb.append(" public ").append(mProxyClassSimpleName).append("() {\n");
sb.append(" mAppLike = new ").append(mTypeElement.getSimpleName().toString()).append("();\n");
sb.append(" }\n\n");
//getPriority()方法
sb.append(" public int getPriority() {\n");
sb.append(" return mAppLike.getPriority();\n");
sb.append(" }\n\n");
//onCreate()方法
sb.append(" public void onCreate() {\n");
sb.append(" mAppLike.onCreate();\n");
sb.append(" }\n\n");
//onTerminate方法
sb.append(" public void onTerminate() {\n");
sb.append(" mAppLike.onTerminate();\n");
sb.append(" }\n\n");
//onTerminate方法
sb.append(" public void onLowMemory() {\n");
sb.append(" mAppLike.onLowMemory();\n");
sb.append(" }\n\n");
//onTerminate方法
sb.append(" public void onTrimMemory(int level) {\n");
sb.append(" mAppLike.onTrimMemory(level);\n");
sb.append(" }\n\n");
sb.append("\n}");
return sb.toString();
}
}
关于APT的调试可以参考https://blog.csdn.net/zhangteng22/article/details/54946270这篇文章。
在前面定义的几个组件代理类上添加注解@AppLifeCycle
//实现了IAppLike接口,并且采用了AppLifeCycle注解,二者缺一不可,否则APT处理时会报错
@AppLifeCycle
public class MainDelegate implements IAppLike {
@Override
public int getPriority() {
return 10;
}
@Override
public void onCreate() {
Log.i("hx", "MainDelegate-onCreate");
}
@Override
public void onTerminate() {
Log.i("hx", "MainDelegate-onTerminate");
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
}
同时修改组件的build.gradle,添加依赖:
//---------其他依赖------------
api project(':lifecycle-annotation')
api project(':lifecycle-api')
//需要注意这里是使用 annotationProcessor,即我们刚定义的注解处理器
annotationProcessor project(':lifecycle-apt')
到这里注解处理器就可以工作啦,将整个工程编译一下,可以看到在build目录下已经生成了我们定义的注解类,具体路径如下所示:
打开看一下呢:
public class Watson$$MainDelegate$$Proxy implements IAppLike {
private MainDelegate mAppLike;
public Watson$$MainDelegate$$Proxy() {
mAppLike = new MainDelegate();
}
public int getPriority() {
return mAppLike.getPriority();
}
public void onCreate() {
mAppLike.onCreate();
}
public void onTerminate() {
mAppLike.onTerminate();
}
public void onLowMemory() {
mAppLike.onLowMemory();
}
public void onTrimMemory(int level) {
mAppLike.onTrimMemory(level);
}
}
新建一个Android Library module,命名为lifecycle-api,在这个module里定义IAppLike接口,以及一个生命周期管理类。现在工程目录差不多是这样的:
public interface IAppLike {
int MAX_PRIORITY = 10;
int MIN_PRIORITY = 1;
int NORM_PRIORITY = 5;
default int getPriority() {
return NORM_PRIORITY;
}
void onCreate();
void onTerminate();
void onLowMemory();
void onTrimMemory(int level);
}
生命周期管理类,初始化的时候扫描所有的代理辅助类,并在相应方法中调用组件生命周期方法。
public class AppLifeCycleManager {
/**
* 初始化
*/
public static void init(Context context) {
scanClassFile(context);
Log.d("hx", "代理子模块数目:" + APP_LIKE_LIST.size());
Collections.sort(APP_LIKE_LIST, new AppDelegateComparator()); //优先级排序
}
public static void onCreate() {
for (IAppLike delegate : APP_LIKE_LIST) {
delegate.onCreate();
}
}
public static void onTerminate() {
for (IAppLike delegate : APP_LIKE_LIST) {
delegate.onTerminate();
}
}
public static void onLowMemory() {
for (IAppLike delegate : APP_LIKE_LIST) {
delegate.onLowMemory();
}
}
public static void onTrimMemory(int level) {
for (IAppLike delegate : APP_LIKE_LIST) {
delegate.onTrimMemory(level);
}
}
/**
* 扫描出固定包名下,实现了IAppLike接口的代理类 * * @param context
*/
private static void scanClassFile(Context context) {
try {
Set<String> set = ClassUtils.getFileNameByPackageName(context, LifeCycleConfig.PROXY_CLASS_PACKAGE_NAME);
if (set != null) {
for (String className : set) {
Log.d("hx", className);
try {
Object obj = Class.forName(className).newInstance();
if (obj instanceof IAppLike) {
APP_LIKE_LIST.add((IAppLike) obj);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 优先级比较器,优先级大的排在前面
*/
static class AppDelegateComparator implements Comparator<IAppLike> {
@Override
public int compare(IAppLike o1, IAppLike o2) {
int p1 = o1.getPriority();
int p2 = o2.getPriority();
return p2 - p1;
}
}
}
public class BaseApplication extends Application {
private static BaseApplication sInstance;
public static BaseApplication getIns() {
return sInstance;
}
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
if (BuildConfig.DEBUG) {
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(this);// 尽可能早,推荐在Application中初始化
// 代理方法
AppLifeCycleManager.init(this);
AppLifeCycleManager.onCreate();
}
@Override
public void onTerminate() {
super.onTerminate();
ARouter.getInstance().destroy();
AppLifeCycleManager.onTerminate();
}
@Override
public void onLowMemory() {
super.onLowMemory();
AppLifeCycleManager.onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
AppLifeCycleManager.onTrimMemory(level);
}
}
在BaseApplication的生命周期中调用Manager中相应的方法即可。
跑一下,看日志输入,可以看到扫描到了四个代理辅助类,在BaseApplication的onCreate方法中分别调用了四个代理类的onCreate方法,且按照优先级顺序调用。
更进一步的思考:
前面的思路里,应用在启动时,需要扫描dex文件里的所有class,然后通过class文件的包名来判断是不是我们的目标类。通常一个安装包里,加上第三方库,class文件可能数以千计、数以万计,但是我们的要用到的可能只有几个,这让人有点杀鸡用牛刀的感觉。能不能在运行时,不扫描所有class文件,就已经知道了所有的AppLikeProxy类名呢?首先想到的就是采用gradle插件技术,在应用打包编译时,动态插入字节码来实现。这里又会碰到几个技术难点:
新建一个Java Library module,命名为lifecycle-plugin,删除 src->main 下面的java目录,新建一个groovy目录,在groovy目录下创建类似java的package,在 src->main 下面创建一个 resources 目录,在resources目录下依次创建 META-INF/gradle-plugins 目录,最后在该目录下创建一个名为 com.example.plugin.lifecycle.properties的文本文件,文件名是你要定义的插件名,按需自定义即可。最后的工程结构如图所示:
修改module的build.gradle文件,引入groovy插件等:
apply plugin:'java-library'
apply plugin:'groovy'
apply plugin:'maven'
dependencies{
implementation fileTree(dir:'libs',include:['*.jar'])
compile gradleApi()
compile localGroovy()
compile'com.android.tools.build:transform-api:1.5.0'
compile'com.android.tools.build:gradle:3.0.1'
}
sourceCompatibility="1.8"
targetCompatibility="1.8"
tasks.withType(JavaCompile){
options.encoding="UTF-8"
}
//通过maven将插件发布到本地的脚本配置,根据自己的要求来修改
uploadArchives {
repositories.mavenDeployer {
pom.version = '1.0.0'
pom.artifactId = 'lifecycleplugin'
pom.groupId = 'com.example.component'
repository(url: "file:///D:/repository/")
}
}
这里有几点需要说明的是:
我们通过maven将该插件发布到本地的maven仓库里,发布成功后,我们在app module里引入该插件,修改app module目录下的build.gradle文件,增加如下配置:
//自定义插件
apply plugin: 'com.example.plugin.lifecycle'
buildscript {
repositories {
google()
jcenter()
//自定义插件maven地址
maven {
url uri('D:/repository') //add
}
}
dependencies {
//加载自定义插件
classpath 'com.example.component:lifecycleplugin:1.0.0'
}
}
要编写一个插件是很简单的,只需实现Plugin接口即可,在里面注册LifeCycleTransform:
class LifeCyclePlugin implements Plugin<Project> {
@Override
void apply(Project project) {
println "------LifeCycle plugin entrance-------"
def android = project.extensions.getByType(AppExtension)
android.registerTransform(new LifeCycleTransform(project))
}
}
接着在com.example.plugin.lifecycle.properties文件里增加配置:
implementation-class=com.example.lifecycle.plugin.LifeCyclePlugin
其中implementation-class的值为Plugin接口的实现类的全限定类名。
那么怎么通过插件在打包前去扫描所有的class文件呢,幸运的是官方给我们提供了 Gradle Transform 技术,简单来说就是能够让开发者在项目构建阶段即由class到dex转换期间修改class文件,Transform阶段会扫描所有的class文件和资源文件,下面通过代码部分说下我的思路:
class LifeCycleTransform extends Transform {
Project project
LifeCycleTransform(Project project) {
this.project = project
}
//该Transform的名称,自定义即可,只是一个标识
@Override
String getName() {
return "LifeCycleTransform"
}
//该Transform支持扫描的文件类型,分为class文件和资源文件,我们这里只处理class文件的扫描
@Override
Set<QualifiedContent.ContentType> getInputTypes() {
return TransformManager.CONTENT_CLASS
}
//Transform的扫描范围,我这里扫描整个工程,包括当前module以及其他jar包、aar文件等所有的class
@Override
Set<? super QualifiedContent.Scope> getScopes() {
return TransformManager.SCOPE_FULL_PROJECT
}
//是否增量扫描
@Override
boolean isIncremental() {
return true
}
@Override
void transform(Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
println "\nstart to transform-------------->>>>>>>"
def appLikeProxyClassList = []
//inputs就是所有扫描到的class文件或者是jar包,一共2种类型
inputs.each {
TransformInput input ->
//1.遍历所有的class文件目录
input.directoryInputs.each {
DirectoryInput directoryInput ->
//递归扫描该目录下所有的class文件
if (directoryInput.file.isDirectory()) {
directoryInput.file.eachFileRecurse {
File file ->
//形如 Watson****$$Proxy.class 的类,是我们要找的目标class
if (ScanUtil.isTargetProxyClass(file)) {
//如果是我们自己生产的代理类,保存该类的类名
appLikeProxyClassList.add(file.name)
}
}
}
//Transform扫描的class文件是输入文件(input),有输入必然会有输出(output),处理完成后需要将输入文件拷贝到一个输出目录下去,
// 后面打包将class文件转换成dex文件时,直接采用的就是输出目录下的class文件了。
// 必须这样获取输出路径的目录名称
def dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)
FileUtils.copyDirectory(directoryInput.file, dest)
}
//2.遍历查找所有的jar包
input.jarInputs.each {
JarInput jarInput ->println
"\njarInput = ${jarInput}"
//与处理class文件一样,处理jar包也是一样,最后要将inputs转换为outputs
def jarName = jarInput.name
def md5 = DigestUtils.md5Hex(jarInput.file.getAbsolutePath())
if (jarName.endsWith(".jar")) {
jarName = jarName.substring(0, jarName.length() - 4)
}
//获取输出路径下的jar包名称,必须这样获取,得到的输出路径名不能重复,否则会被覆盖
def dest = outputProvider.getContentLocation(jarName + md5, jarInput.contentTypes, jarInput.scopes, Format.JAR)
if (jarInput.file.getAbsolutePath().endsWith(".jar")) {
//处理jar包里的代码
File src = jarInput.file
//先简单过滤掉 support-v4 之类的jar包,只处理有我们业务逻辑的jar包
if (ScanUtil.shouldProcessPreDexJar(src.absolutePath)) {
//扫描jar包的核心代码在这里,主要做2件事情:
// 1.扫描该jar包里有没有实现IAppLike接口的代理类;
// 2.扫描AppLifeCycleManager这个类在哪个jar包里,并记录下来,后面需要在该类里动态注入字节码;
List<String> list = ScanUtil.scanJar(src, dest) if (list != null) {
appLikeProxyClassList.addAll(list)
}
}
}
//将输入文件拷贝到输出目录下
FileUtils.copyFile(jarInput.file, dest)
}
}
println "" appLikeProxyClassList.forEach({fileName -> println"file name = " + fileName})
println "\n包含AppLifeCycleManager类的jar文件" println ScanUtil.
FILE_CONTAINS_INIT_CLASS.getAbsolutePath() println
"开始自动注册"
//1.通过前面的步骤,我们已经扫描到所有实现了 IAppLike接口的代理类;
// 2.后面需要在 AppLifeCycleManager 这个类的初始化方法里,动态注入字节码;
// 3.将所有 IAppLike 接口的代理类,通过类名进行反射调用实例化
// 这样最终生成的apk包里,AppLifeCycleManager调用init()方法时,已经可以加载所有组件的生命周期类了
new AppLikeCodeInjector(appLikeProxyClassList).execute()
println "transform finish----------------<<<<<<<\n"
}
}
我们来看看ScanUtil类里的代码逻辑:
class ScanUtil {
static final PROXY_CLASS_PREFIX ="Watson\$\$"static final PROXY_CLASS_SUFFIX ="\$\$Proxy.class"static final PROXY_CLASS_PACKAGE_NAME ="com/example/lifecycle/apt/proxy"static final REGISTER_CLASS_FILE_NAME ="com/example/lifecycle/api/AppLifeCycleManager.class"static final REGISTER_CLASS_METHOD_NAME ="loadApplicationDelegate"
//包含生命周期管理初始化类的文件,及包含 AppLifeCycleManager 类的class文件或者jar文件
static File FILE_CONTAINS_INIT_CLASS
/**
* 判断该class是否是我们的目标类
*/
static boolean isTargetProxyClass(File file) {
if (file.name.endsWith(PROXY_CLASS_SUFFIX) && file.name.startsWith(PROXY_CLASS_PREFIX)) {
return true
} return false
}
/**
* 扫描jar包里的所有class文件:
* * 1.通过包名识别所有需要注入的类名
* * 2.找到注入管理类所在的jar包,后面我们会在该jar包里进行代码注入
*/
static List<String> scanJar(File jarFile, File destFile) {
def file = new JarFile(jarFile) Enumeration<JarEntry> enumeration = file.entries()
List<String> list = null while (enumeration.hasMoreElements()) {
JarEntry jarEntry = enumeration.nextElement() String entryName = jarEntry.getName()
if (entryName == REGISTER_CLASS_FILE_NAME) {
//标记这个jar包包含 AppLifeCycleManager.class
// 扫描结束后,我们会生成注册代码到这个文件里
FILE_CONTAINS_INIT_CLASS = destFile
} else {
if (entryName.startsWith(PROXY_CLASS_PACKAGE_NAME)) {
if (list == null) {
list = new ArrayList<>()
} list.addAll(entryName.substring(entryName.lastIndexOf("/") + 1))
}
}
}
return list
}
static boolean shouldProcessPreDexJar(String path) {
return !path.contains("com.android.support") && !path.contains("/android/m2repository")
}
}
前面的代码里,先注释掉LifeCycleTransform类里的AppLikeCodeInjector相关代码,这块我们后面再讲。采用最新的插件重新build一下工程,看看Gradle Console里的输出信息。
可以看到,在Transform过程中,我们找到了NewsDelegate、MusicDelegate、MimeDelegate、MainDelegate这4个类的代理类,以及AppLifeCycleManager这个class文件所在的jar包。
在app->build->intermediates->transforms中,可以看到所有的Transform,包括我们刚才自定义的LifeCycleTransform。从上图中可以看到,这里的0.jar、1.jar、2.jar等等,都是通过outputProvider.getContentLocation()方法来生成的,这个Transform目录下的class文件、jar包等,会当做下一个Transform的inputs传递过去。
到现在,我们只剩下最后一步了,那就是如何注入代码了。ASM 是一个 Java 字节码操控框架,它能被用来动态生成类或者增强既有类的功能。我这里对ASM不做详细介绍了,主要是介绍使用ASM动态注入代码的思路。
首先,我们修改一下AppLifeCycleManager类,增加动态注入字节码的入口方法:
public class AppLifeCycleManager {
private static List<IAppLike> APP_LIKE_LIST = new ArrayList<>();
private static boolean REGISTER_BY_PLUGIN = false;
/**
* 通过插件加载 IApplicationDelegate 类
*/
private static void loadApplicationDelegate() {
}
private static void registerApplicationDelegate(String className) {
if (TextUtils.isEmpty(className)) return;
try {
Object obj = Class.forName(className).getConstructor().newInstance();
if (obj instanceof IAppLike) {
registerApplicationDelegate((IAppLike) obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 注册IApplicationDelegate
*/
private static void registerApplicationDelegate(IAppLike appLike) {
//标志我们已经通过插件注入代码了
REGISTER_BY_PLUGIN = true;
APP_LIKE_LIST.add(appLike);
}
/**
* 初始化
*/
public static void init(Context context) {
loadApplicationDelegate();
if (!REGISTER_BY_PLUGIN) {
Log.d("hx", "需要扫描所有类...");
scanClassFile(context);
} else {
Log.d("hx", "插件里已自动注册...");
}
Log.d("hx", "代理子模块数目:" + APP_LIKE_LIST.size());
Collections.sort(APP_LIKE_LIST, new AppDelegateComparator()); //优先级排序
}
...
}
相比之前,这里增加了一个loadApplicationDelegate()方法,在init()方法调用时会先执行。通过前面Transform步骤之后,我们现在的目标是把代码动态插入到loadApplicationDelegate()方法里,下面这段代码是我们期望插入后的结果:
private static void loadAppLike() {
registerAppLike("com.example.lifecycle.apt.proxy.Watson$$MusicDelegate$$Proxy");
registerAppLike("com.example.lifecycle.apt.proxy.Watson$$MainDelegate$$Proxy");
registerAppLike("com.example.lifecycle.apt.proxy.Watson$$MimeDelegate$$Proxy");
registerAppLike("com.example.lifecycle.apt.proxy.Watson$$NewsDelegate$$Proxy");
}
这样在初始化时,就已经知道要加载哪些生命周期类,来看看具体实现方法,关于ASM不了解的地方,需要先搞清楚其使用方法再来阅读:
class AppLikeCodeInjector {
List<String> proxyAppLikeClassList
AppLikeCodeInjector(List<String> list) {
proxyAppLikeClassList = list
}
void execute() {
println("开始执行ASM方法======>>>>>>>>")
File srcFile = ScanUtil.FILE_CONTAINS_INIT_CLASS
//创建一个临时jar文件,要修改注入的字节码会先写入该文件里
def optJar = new File(srcFile.getParent(), srcFile.name + ".opt")
if (optJar.exists()) optJar.delete() def file = new JarFile(srcFile)
Enumeration<JarEntry> enumeration = file.entries()
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(optJar))
while (enumeration.hasMoreElements()) {
JarEntry jarEntry = enumeration.nextElement() String entryName = jarEntry.getName()
ZipEntry zipEntry = new ZipEntry(entryName)
InputStream inputStream = file.getInputStream(jarEntry)
jarOutputStream.putNextEntry(zipEntry)
//找到需要插入代码的class,通过ASM动态注入字节码
if (ScanUtil.REGISTER_CLASS_FILE_NAME == entryName) {
println "insert register code to class >> " + entryName
ClassReader classReader = new ClassReader(inputStream)
// 构建一个ClassWriter对象,并设置让系统自动计算栈和本地变量大小
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS)
ClassVisitor classVisitor = new AppLikeClassVisitor(classWriter)
//开始扫描class文件
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
byte[] bytes = classWriter.toByteArray()
//将注入过字节码的class,写入临时jar文件里
jarOutputStream.write(bytes)
} else {
//不需要修改的class,原样写入临时jar文件里
jarOutputStream.write(IOUtils.toByteArray(inputStream))
} inputStream.close() jarOutputStream.closeEntry()
} jarOutputStream.close()
file.close()
//删除原来的jar文件
if (srcFile.exists()) {
srcFile.delete()
}
//重新命名临时jar文件,新的jar包里已经包含了我们注入的字节码了
optJar.renameTo(srcFile)
}
class AppLikeClassVisitor extends ClassVisitor {
AppLikeClassVisitor(ClassVisitor classVisitor) {
super(Opcodes.ASM5, classVisitor)
}
@Override
MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exception) {
println "visit method: " + name
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exception)
//找到 AppLifeCycleManager里的loadApplicationDelegate()方法
if (ScanUtil.REGISTER_CLASS_METHOD_NAME == name) {
mv = new LoadAppLikeMethodAdapter(mv, access, name, desc)
} return mv
}
}
class LoadAppLikeMethodAdapter extends AdviceAdapter {
LoadAppLikeMethodAdapter(MethodVisitor mv, int access, String name, String desc) {
super(Opcodes.ASM5, mv, access, name, desc)
}
@Override
protected void onMethodEnter() {
super.onMethodEnter() println "-------onMethodEnter------"
proxyAppLikeClassList.forEach({proxyClassName -> println"开始注入代码:${proxyClassName}"def fullName = ScanUtil.PROXY_CLASS_PACKAGE_NAME.replace("/", ".") + "." + proxyClassName.substring(0, proxyClassName.length() - 6)println"full classname = ${fullName}"mv.visitLdcInsn(fullName)mv.visitMethodInsn(INVOKESTATIC, "com/example/lifecycle/api/AppLifeCycleManager", "registerApplicationDelegate", "(Ljava/lang/String;)V", false); })}
@Override
protected void onMethodExit(int opcode) {
super.onMethodExit(opcode) println "-------onMethodEnter------"
}
}
}
clean工程(不然有时候看不到transform中输出的console log),使用插件重新编译代码: