Instant Run
创新互联专注于企业营销型网站建设、网站重做改版、琼结网站定制设计、自适应品牌网站建设、H5页面制作、购物商城网站建设、集团公司官网建设、外贸网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为琼结等各大城市提供网站开发制作服务。
Instant Run,是android studio2.0新增的一个运行机制,在你编码开发、测试或debug的时候,它都能显著减少你对当前应用的构建和部署的时间。通俗的解释就是,当你在Android Studio中改了你的代码,Instant Run可以很快的让你看到你修改的效果。而在没有Instant Run之前,你的一个小小的修改,都肯能需要几十秒甚至更长的等待才能看到修改后的效果。
传统的代码修改及编译部署流程
传统的代码修改及编译流程如下:构建整个apk → 部署app → app重启 → 重启Activity
Instant Run编译和部署流程
Instant Run构建项目的流程:构建修改的部分 → 部署修改的dex或资源 → 热部署,温部署,冷部署
热拔插,温拔插,冷拔插
热拔插:代码改变被应用、投射到APP上,不需要重启应用,不需要重建当前activity。
场景:适用于多数的简单改变(包括一些方法实现的修改,或者变量值修改)
温拔插:activity需要被重启才能看到所需更改。
场景:典型的情况是代码修改涉及到了资源文件,即resources。
冷拔插:app需要被重启(但是仍然不需要重新安装)
场景:任何涉及结构性变化的,比如:修改了继承规则、修改了方法签名等。
首次运行Instant Run,Gradle执行过程
一个新的App Server类会被注入到App中,与Bytecode instrumentation协同监控代码的变化。
同时会有一个新的Application类,它注入了一个自定义类加载器(Class Loader),同时该Application类会启动我们所需的新注入的App Server。于是,Manifest会被修改来确保我们的应用能使用这个新的Application类。(这里不必担心自己继承定义了Application类,Instant Run添加的这个新Application类会代理我们自定义的Application类)
至此,Instant Run已经可以跑起来了,在我们使用的时候,它会通过决策,合理运用冷温热拔插来协助我们大量地缩短构建程序的时间。
在Instant Run运行之前,Android Studio会检查是否能连接到App Server中。并且确保这个App Server是Android Studio所需要的。这同样能确保该应用正处在前台。
热拔插
Android Studio monitors: 运行着Gradle任务来生成增量.dex文件(这个dex文件是对应着开发中的修改类) Android Studio会提取这些.dex文件发送到App Server,然后部署到App(Gradle修改class的原理,请戳链接)。
App Server会不断监听是否需要重写类文件,如果需要,任务会被立马执行。新的更改便能立即被响应。我们可以通过打断点的方式来查看。
温拔插
温拔插需要重启Activity,因为资源文件是在Activity创建时加载,所以必须重启Activity来重载资源文件。
目前来说,任何资源文件的修改都会导致重新打包再发送到APP。但是,google的开发团队正在致力于开发一个增量包,这个增量包只会包装修改过的资源文件并能部署到当前APP上。
所以温拔插实际上只能应对少数的情况,它并不能应付应用在架构、结构上的变化。
注:温拔插涉及到的资源文件修改,在manifest上是无效的(这里的无效是指不会启动Instant Run),因为,manifest的值是在APK安装的时候被读取,所以想要manifest下资源的修改生效,还需要触发一个完整的应用构建和部署。
冷拔插
应用部署的时候,会把工程拆分成十个部分,每部分都拥有自己的.dex文件,然后所有的类会根据包名被分配给相应的.dex文件。当冷拔插开启时,修改过的类所对应的.dex文件,会重组生成新的.dex文件,然后再部署到设备上。
之所以能这么做,是依赖于Android的ART模式,它能允许加载多个.dex文件。ART模式在android4.4(API-19)中加入,但是Dalvik依然是选择,到了android5.0(API-21),ART模式才成为系统默认选择,所以Instant Run只能运行在API-21及其以上版本。
使用Instant Run一些注意点
Instant Run是被Android Studio控制的。所以我们只能通过IDE来启动它,如果通过设备来启动应用,Instant Run会出现异常情况。在使用Instant Run来启动Android app的时候,应注意以下几点:
如果应用的minSdkVersion小于21,可能多数的Instant Run功能会挂掉,这里提供一个解决方法,通过product flavor建立一个minSdkVersion大于21的新分支,用来debug。
Instant Run目前只能在主进程里运行,如果应用是多进程的,类似微信,把webView抽出来单独一个进程,那热、温拔插会被降级为冷拔插。
在Windows下,Windows Defender Real-Time Protection可能会导致Instant Run挂掉,可用通过添加白名单列表解决。
暂时不支持Jack compiler,Instrumentation Tests,或者同时部署到多台设备。
结合Demo深度理解
为了方便大家的理解,我们新建一个项目,里面不写任何的逻辑功能,只对application做一个修改:
首先,我们先反编译一下APK的构成,使用的工具:d2j-dex2jar 和jd-gui。
我们要看的启动的信息就在这个instant-run.zip文件里面,解压instant-run.zip,我们会发现,我们真正的业务代码都在这里。
从instant-run文件中我们猜想是BootstrapApplication替换了我们的application,Instant-Run代码作为一个宿主程序,将app作为资源dex加载起来。
那么InstantRun是怎么把业务代码运行起来的呢?
Instant Run如何启动app
按照我们上面对instant-run运行机制的猜想,我们首先看一下appliaction的分析attachBaseContext和onCreate方法。
attachBaseContext()
- protected void attachBaseContext(Context context) {
- if (!AppInfo.usingApkSplits) {
- String apkFile = context.getApplicationInfo().sourceDir;
- long apkModified = apkFile != null ? new File(apkFile).lastModified() : 0L;
- createResources(apkModified);
- setupClassLoaders(context, context.getCacheDir().getPath(), apkModified);
- }
- createRealApplication();
- super.attachBaseContext(context);
- if (this.realApplication != null) {
- try {
- Method attachBaseContext = ContextWrapper.class.getDeclaredMethod("attachBaseContext", new Class[] { Context.class });
- attachBaseContext.setAccessible(true);
- attachBaseContext.invoke(this.realApplication, new Object[] { context });
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
- }
- }
我们依次需要关注的方法有:
createResources → setupClassLoaders → createRealApplication → 调用realApplication的attachBaseContext方法
createResources()
- private void createResources(long apkModified) {
- FileManager.checkInbox();
- File file = FileManager.getExternalResourceFile();
- this.externalResourcePath = (file != null ? file.getPath() : null);
- if (Log.isLoggable("InstantRun", 2)) {
- Log.v("InstantRun", "Resource override is " + this.externalResourcePath);
- }
- if (file != null) {
- try {
- long resourceModified = file.lastModified();
- if (Log.isLoggable("InstantRun", 2)) {
- Log.v("InstantRun", "Resource patch last modified: " + resourceModified);
- Log.v("InstantRun", "APK last modified: " + apkModified
- + " "
- + (apkModified > resourceModified ? ">" : "<")
- + " resource patch");
- }
- if ((apkModified == 0L) || (resourceModified <= apkModified)) {
- if (Log.isLoggable("InstantRun", 2)) {
- Log.v("InstantRun", "Ignoring resource file, older than APK");
- }
- this.externalResourcePath = null;
- }
- } catch (Throwable t) {
- Log.e("InstantRun", "Failed to check patch timestamps", t);
- }
- }
- }
说明:该方法主要是判断资源resource.ap_是否改变,然后保存resource.ap_的路径到externalResourcePath中。
setupClassLoaders()
- private static void setupClassLoaders(Context context, String codeCacheDir, long apkModified) {
- List dexList = FileManager.getDexList(context, apkModified);
- Class server = Server.class;
- Class patcher = MonkeyPatcher.class;
- if (!dexList.isEmpty()) {
- if (Log.isLoggable("InstantRun", 2)) {
- Log.v("InstantRun", "Bootstrapping class loader with dex list " + join('\n', dexList));
- }
- ClassLoader classLoader = BootstrapApplication.class.getClassLoader();
- String nativeLibraryPath;
- try {
- nativeLibraryPath = (String) classLoader.getClass().getMethod("getLdLibraryPath", new Class[0]).invoke(classLoader, new Object[0]);
- if (Log.isLoggable("InstantRun", 2)) {
- Log.v("InstantRun", "Native library path: " + nativeLibraryPath);
- }
- } catch (Throwable t) {
- Log.e("InstantRun", "Failed to determine native library path " + t.getMessage());
- nativeLibraryPath = FileManager.getNativeLibraryFolder().getPath();
- }
- IncrementalClassLoader.inject(classLoader, nativeLibraryPath, codeCacheDir, dexList);
- }
- }
说明,该方法是初始化一个ClassLoaders并调用IncrementalClassLoader。
IncrementalClassLoader的源码如下:
- public class IncrementalClassLoader extends ClassLoader {
- public static final boolean DEBUG_CLASS_LOADING = false;
- private final DelegateClassLoader delegateClassLoader;
- public IncrementalClassLoader(ClassLoader original, String nativeLibraryPath, String codeCacheDir, List dexes) {
- super(original.getParent());
- this.delegateClassLoader = createDelegateClassLoader(nativeLibraryPath, codeCacheDir, dexes, original);
- }
- public Class findClass(String className) throws ClassNotFoundException {
- try {
- return this.delegateClassLoader.findClass(className);
- } catch (ClassNotFoundException e) {
- throw e;
- }
- }
- private static class DelegateClassLoader extends BaseDexClassLoader {
- private DelegateClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
- super(dexPath, optimizedDirectory, libraryPath, parent);
- }
- public Class findClass(String name) throws ClassNotFoundException {
- try {
- return super.findClass(name);
- } catch (ClassNotFoundException e) {
- throw e;
- }
- }
- }
- private static DelegateClassLoader createDelegateClassLoader(String nativeLibraryPath, String codeCacheDir, List dexes,
- ClassLoader original) {
- String pathBuilder = createDexPath(dexes);
- return new DelegateClassLoader(pathBuilder, new File(codeCacheDir), nativeLibraryPath, original);
- }
- private static String createDexPath(List dexes) {
- StringBuilder pathBuilder = new StringBuilder();
- boolean first = true;
- for (String dex : dexes) {
- if (first) {
- first = false;
- } else {
- pathBuilder.append(File.pathSeparator);
- }
- pathBuilder.append(dex);
- }
- if (Log.isLoggable("InstantRun", 2)) {
- Log.v("InstantRun", "Incremental dex path is " + BootstrapApplication.join('\n', dexes));
- }
- return %
网站标题:深入理解AndroidInstantRun运行机制
地址分享:http://www.shufengxianlan.com/qtweb/news14/16714.html网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联