如需转载请注明地址:http://blog.csdn.net/rzleilei/article/details/52334878
LayoutCast是一个私人开源的,实现了android工程增量编译功能的工具。在此向原作者mmin18表示感谢。
一、源码实现流程以及原理
LayoutCast实现了类似于热修复的原理,主要分为电脑端和手机端部分:
手机端部分,需要在Application的onCreate方法中调用LayoutCast.init(this)注册。主要做了四个功能,按触发顺序介绍:
1.利用反射获取BaseDexClassLoader的pathList对象,然后把插入dex的Array赋值给这个对象。
2.注册activity的生命周期监听
3.在Application的onCreate方法中调用LayoutCast.init(this)注册。主要做了四个功能,插入dex, 注册activity的生命周期监听,重载Application的类加载器。
4.注册一个本地服务监听某个手机端口。
电脑端部分,当用户点击AS中的热部署按钮时,其实主要是执行了本地cash.py脚本,主要完成了下面的这些功能:
1.获取本地配置的变量,比如Android SDK目录,Java目录,工程目录,adb Path等等。(这一步有可能会因为本地配置路径不同发生异常,后面改进点之一)
2.cash.sh由于文件是在工程目录下,所以可以通过相对路径获取到所有的Module的路径
3.扫描所有的module下的build文件夹,找到最新打包完成的一个apk为默认主工程,通过aapt工具解析apk获取到该package的报名
4.利用获取到的package包名和之前约定好的端口号,通过adb命令扫描手机端口,实现电脑端口和手机端口之间的映射,建立连接,同时获得对应的手机端口。
5.通过包名获取主工程目录,解析build.gradle,通过正则关键词dependence截取所有依赖的工程,然后扫描这些工程里面是否有修改文件,并记录(PS:这一步完全根据正则去提取是会有问题的,也是后面我们的修改点)
6.判断打包时间和最晚修改时间,如果打包时间晚于最晚修改时间,则进程关闭,否则继续。
7.详细判断是src修改了还是res修改了,还是都改了。做对应的操作。
8.资源修改了,通过aapt工具打包。生成文件的路径在主工程/lcast下
9.java文件修改了,获取javac命令,定义list对象classpath,首先加入android_jar,然后找到工程依赖的所有jar包,都加入列表,包括maven依赖的。通过javac命令打包,生成class文件。通过dx工具打包生成dex文件。并通过网络传输到apk上。(这一步扫描依赖包的时候会缺少一个supprt-annotation-23.0.1.jar包)
10.结束端口映射,任务结束。
二、改动点
我做的改动点主要就是上面介绍的红字描述的部分,改动的方式也很简单,那就是本地定义写死。
在loca.properties添加三个参数,如下
otherJarPath=E:\android\sdk\extras\android\m2repository\com\android\support\support-annotations\23.0.1\support-annotations-23.0.1.jar layoutPath=CTMain,CTModule\CTSub1,CTModule\CTSub2 aaptPath=E:\android\sdk\build-tools\21.1.2
分别代表本地的
otherJarPath:未扫描到的jar包的路径,用,区分。携程工程的话应该只缺少了supprt-annotation-23.0.1.jar包
layoutPath:需要扫描的的工程路径,写成相对于主工程的路径。同样用,区分。扫描越少的工程,则运行的速度就越快。
aaptPath:本地的aapt工具的路径。
cash.py文件中获取这几个路径的方法里面,我都改了成了从local文件中读取。
另外时间有限,暂时只修改了cash.py对java文件进行了支持,对res的支持的操作还未做
三、应用于具体工程
1、给Anroid Studio工具装一个插件IDEAPlugin.jar,下图所示。如果习惯用python命令执行的话,可以跳过这个步骤。
安装成功后重启Android Studio后,会多了一个这样的图标:
2、添加支持的jar包或者依赖的module
如果添加jar包的话,很简单,直接把library.jar包放到CTBusiness下的libs下。
如果依赖module的话,需要把library的工程导入到当前工程中。
修改android_2下的build.gradle文件,添加对library工程的支持,include ':library',如下:
} } //Main entrance include 'CTMain' include ':library' }
修改CTBusiness下的build.gradle文件。添加下面中的最后一句。
dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') FileTree jarfiles = fileTree(include: ['*.jar'], dir: '../../libs') // if (rootProject.hasProperty('buildID')) { //mcd打包不包含stetho jarfiles.exclude('stetho-*.jar') // } compile jarfiles compile 'com.android.support:multidex:1.0.0' compile 'com.android.support:support-v4:23.0.1' compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:recyclerview-v7:23.0.1' apt 'org.greenrobot:eventbus-annotation-processor:3.0.1' compile ':Alipay_Lib:+@aar' compile ':BaiduWallet_Lib:+@aar' compile ':ReactAndroid:+@aar' compile project(':library')
3、对工程进行一些小改造。
首先、在CTApplication(自定义Application)的onCreate的方法中,添加一句LayoutCast.init(this)
其次、在CTMain中的Mainfest.xml中注册activity
<activity android:name="com.github.mmin18.layoutcast.ResetActivity" />
4.拷贝cash.py文件到android_2的主工程目录下
修改local.properties文件,添加对应的参数。下面是我的:
## This file is automatically generated by Android Studio. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must *NOT* be checked into Version Control Systems, # as it contains information specific to your local configuration. # # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. #Mon Aug 22 10:46:30 CST 2016 sdk.dir=E\:\\android\\sdk solidMode=false otherJarPath=E:\android\sdk\extras\android\m2repository\com\android\support\support-annotations\23.0.1\support-annotations-23.0.1.jar layoutPath=CTMain,CTModule\CTSub1,CTModule\CTSub2 aaptPath=E:\android\sdk\build-tools\21.1.2
四、忽略本地修改和提交
我们做了这么多的修改,如果不希望这些修改或新建文件提交到版本库当中,可以做如下的配置:
对于这些修改,分别两类。
对于Untracked files这些从未提交到版本库中的文件,可以忽略这些文件。修改android_2路径下.git/info/exclude文件。
添加下面的的内容,并保存,则会忽略掉这些新添加的文件,该操作只针对当前本地的git仓库。
library/
cast.py
对于modified这些已经被纳入到版本库的文件,可以本地不在对这些这些文件进行跟踪。
git update-index --assume-unchanged build.gradle//本地git仓库忽略对build.gradle文件的跟踪
git update-index –no-assume-unchanged build.gradle//恢复对build.gradle文件的关注
这些修改是对本地当前仓库生效的。
五、运行流程
运行前需要首先运行一遍工程。(由于只对java文件做了修改,res文件的修改还未做,所以暂时仅支持对java文件的动态编译)
然后修改java文件,保存。要确保修改的java文件在layoutPath标注的module下。点击一下蓝色的插件按钮就可以了。
如果未安装插件,则进入到android_2目录下,运行python cast.py命令。
六、缺陷以及同类对比
目前主流的动态编译框架有LayoutCast、facebook的Buck、google官方的install run、腾讯空间(未开源)、阿里聚宝刚刚开源的FreeLine。
原理上主要分为两类:
LayoutCast和Install Run都是基于类似于热修复原理的动态替换,把增量dex包插入到队列的最前端,这样确保修改后的.class文件会被编译。由于5.0以下版本具有dex检查,所以这两个方案只对5.0以上手机有效。
Buck基于的原理是多线程打包。apk打包是单线程,分为七步。而其实某些步骤是可以并行的,尤其javac编译这一步也是可以多线程并行的。Buck整理了相互依赖的拓扑图,采取了16线程同时打包,从而加快打包速度。
腾讯空间的话,同样是使用了热修复的原理,只不过在打包的时候,动态的添加进去一个dex包,其它所有包中的类都引用到这个dex包。从而绕过dex检查。
FreeLine刚刚开源,原理还没看,但是看介绍应该是融合了上面几家的优点的一个集合体。有空看看。
附件集合:
我修改后的cast.py文件以及local.properties、 IDEAPlugin.jar、lcast.jar
http://download.csdn.net/detail/aa5279aa/9614500
github:
https://github.com/aa5279aa/xt_LayoutCast
链接:
https://github.com/mmin18/LayoutCast
https://github.com/alibaba/freeline
PS:
如果问题,欢迎咨询。
文章为原创,原理是借助代码个人理解的,有可能有错误,也欢迎指出