Freeline适配kotlin-1-source增量逻辑梳理

github链接

Freeline source文件的增量原理梳理

为什么要写这个呢?

因为我觉得…适配kotlin之前要弄通java的增量逻辑然后适配其实也不是很难

还有就是... source增量比资源增量好理解些(个人感觉)

先放个图

切入正题

官方已经很细致的讲述了freeline增量构建的原理,欢迎时刻回味官方介绍,贴个地址

官方原理介绍

这次我的分析作为一个第三方分析,主要来源于一个修轮子的人对于源码一层层的理解。因为要适配kotlin嘛,所以要先把java增量理顺。

所谓增量?

增量 ——> 对症下药

python freeline.py -f第一步跑项目,生成一些基本信息

在 app/build/freeline目录中我们可以发现这些文件

~/AndroidStudioProjects/One/app/build/freeline vlayout*
❯ ls
app                           freeline-backup               jar_dependencies.json         public_keeper.xml
freeline-assets               freeline_annotation_info.json project_info_cache.json       stat_cache.json
  • jar_dependencies.json : 记录项目所用第三方依赖jar包的地址,classpath会用到
  • project_info_cache.json : 记录项目相关的配置,不如说assets在哪啊,java环境变量啊什么的
  • stat_cache.json : 这就是本节重点了 记录了各个文件的状态,记录了什么状态呢???

让我们来看看啊~

{
  "app": {
    
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png": {
      "md5": "4ad08251ddecfdd6e2a2c53e79b8d8de",
      "mtime": 1491468325.0
    },
    
 "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/activity_vlayout_main.xml": {
      "md5": "27764e0bfc8b1eceb487270b5f81077f",
      "mtime": 1491671113.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/java/com/twtstudio/one/view/InfoView.java": {
      "md5": "ca64e944f5f77b3d9d963e6b5f7a5b1b",
      "mtime": 1491468325.0
    },
    
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/values/dimens.xml": {
      "md5": "77708536bdf75969da02b4a5687d1a9b",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/mipmap-xxhdpi/ic_launcher.png": {
      "md5": "40d69d20dd057b4a331d03f2f3f2667e",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/AndroidManifest.xml": {
      "md5": "58017fd1d190500d73de049906a5c5c6",
      "mtime": 1491663464.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/values/strings.xml": {
      "md5": "5a7370677c86e0d1f9172ad12d9b07c8",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/info_fragment.xml": {
      "md5": "66fa79125bcec1303742ef02a1e00b91",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/item.xml": {
      "md5": "d9ac17df542927edd7e87d686827a68e",
      "mtime": 1491661862.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/java/com/twtstudio/one/OneApp.java": {
      "md5": "2540855b975841251027d551e06e6c39",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/mipmap-mdpi/ic_launcher.png": {
      "md5": "5ff8945dee0f56d079db8ce6691aaaeb",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/build.gradle": {
      "md5": "5ca79f9228d06c1548bded1b926b62b4",
      "mtime": 1503676918.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/libs/Depth_lib_android.jar": {
      "md5": "725d9cbc54865c1fe5ccfcec13255753",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/activity_welcome.xml": {
      "md5": "7bc85b0c5224fef021a037f4b057cffb",
      "mtime": 1491663032.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/footer.xml": {
      "md5": "55dcb32dc6a806382ddfc05505e7c42c",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/activity_content.xml": {
      "md5": "70bcbcd6fe9ee665e4e83ee172936246",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/activity_main.xml": {
      "md5": "f5b8721e3a23069a6f9f945cdf8551ac",
      "mtime": 1491536877.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/mipmap-xhdpi/ic_launcher.png": {
      "md5": "93fd2eadf162fd41695cab262540184f",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/values/styles.xml": {
      "md5": "ef65245d9b65dc4e791395cc5351de59",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/mipmap-hdpi/ic_launcher.png": {
      "md5": "357539403cded61d96161198a0773ffc",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/drawable/one_pic.jpeg": {
      "md5": "d4adae21678a36795006a1d9d6ad2fb3",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/values-w820dp/dimens.xml": {
      "md5": "f06a662d076312e2271749c093cf40d3",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/java/com/twtstudio/one/presenter/InfoBeanPresenter.java": {
      "md5": "d89984c2adf024bcd39104bdec13d131",
      "mtime": 1491527284.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/values/colors.xml": {
      "md5": "5891b38465c5cfd7020d91693388aea6",
      "mtime": 1491468325.0
    },
    "/Users/retrox/AndroidStudioProjects/One/app/src/main/res/layout/item_simple_title.xml": {
      "md5": "18b47cab0db0c34cdde68598d2c41552",
      "mtime": 1491671160.0
    }
  },
  "/Users/retrox/AndroidStudioProjects/One/settings.gradle": {
    "md5": "a7fe1ac39169b5e5285a5e53662f251b"
  },
  "/Users/retrox/AndroidStudioProjects/One/build.gradle": {
    "md5": "413db5cee66d5c922059d2ab9b0f9163"
  }
}

所以看得出来他是扫描了项目代码,资源相关目录的文件,并且标注了修改日期和md5值

写这篇博客的时候我已经适配了kotlin...所以我故意把json里面的的kt文件一个个删掉...

宝宝心里苦啊

继续说,其实看到这个json,心里还是有一点小头绪的,那我们去看代码吧!

    def __check_changes(self, module_name, fpath, module_cache):
        if not fpath:
            return False

        if fpath not in module_cache:
            self.debug('find new file {}'.format(fpath))
            return True

        stat = module_cache[fpath]
        mtime = os.path.getmtime(fpath)
        if mtime > stat['mtime']:
            self.debug('find {} has modification.'.format(fpath))
            stat['mtime'] = mtime
            self._stat_cache[module_name][fpath] = stat

            md5 = get_md5(fpath)
            if md5 != stat['md5']:
                self.debug('find {} has modification.'.format(fpath))
                stat['md5'] = md5
                self._stat_cache[module_name][fpath] = stat
                return True
        return False

这就是检查文件变化的代码(json缓存层的代码我就不贴了 大家意会 也可以看代码)

  • 如果在json cache文件里找不到的话 直接返回true

  • 如果找到了,那就检查修改时间 ,如果修改时间有变化 然后就判断md5 最后综合得出结论

个人感觉:用修改时间比较来避免大量的md5

运算然后用md5校验最大限度的减小不必要增量

然后我们再回去看增量文件的扫描方法

# scan src
            src_dirs = self._config['project_source_sets'][module_name]['main_src_directory']
            for src_dir in src_dirs:
                if os.path.exists(src_dir):
                    for dirpath, dirnames, files in os.walk(src_dir):
                        if re.findall(r'[/\\+]androidTest[/\\+]', dirpath) or '/.' in dirpath:
                            continue
                        for fn in files:
                            if fn.endswith('java'):
                                if fn.endswith('package-info.java') or fn.endswith('BuildConfig.java'):
                                    continue
                                fpath = os.path.join(dirpath, fn)
                                if self.__check_changes(module_name, fpath, module_cache):
                                    self._changed_files[module_name]['src'].append(fpath)
                            # 添加kotlin的增量检查
#                            elif fn.endswith('kt'):
#                               fpath = os.path.join(dirpath, fn)
#                              if self.__check_changes(module_name, fpath, module_cache):
#                                 self._changed_files[module_name]['kotlin'].append(fpath)

就是对相应的文件夹遍历而已 然后判断修改 如果修改就把它的路基保存下来 秋后问斩~

我注释掉了自己加的kotlin判断部分 当然你也可以推断出 之前的代码对于kotlin文件是不作处理的

对! 直接跳过了......

self._changed_files[module_name]['src'].append(fpath)这里放着修改过的文件,然后对它们做什么操作呢

当然是javac编译他们了啊!

    # 删减了一些处理兼容性 apt什么的代码 为了方便阅读
    def _generate_java_compile_args(self, extra_javac_args_enabled=False):
        javacargs = [self._javac]
        arguments = ['-encoding', 'UTF-8', '-g']
        if not self._is_retrolambda_enabled:
            arguments.extend(['-target', '1.7', '-source', '1.7'])

        arguments.append('-cp')
        arguments.append(os.pathsep.join(self._classpaths))

        # 睁大眼睛
        for fpath in self._changed_files['src']:
            arguments.append(fpath)
    
        arguments.append('-d')
        arguments.append(self._finder.get_patch_classes_cache_dir())

        javacargs.extend(arguments)
        return javacargs

随意感受 java增量过程

class GradleIncJavacCommand(IncJavacCommand):
    def __init__(self, module_name, invoker):
        IncJavacCommand.__init__(self, module_name, invoker)

    def execute(self):
        self._invoker.check_r_md5()  # check if R.java has changed
        # self._invoker.check_other_modules_resources()
        should_run_javac_task = self._invoker.check_javac_task()
        if not should_run_javac_task:
            self.debug('no need to execute')
            return

        self.debug('start to execute javac command...')
        self._invoker.append_r_file()
        self._invoker.fill_classpaths()
        self._invoker.fill_extra_javac_args()
        self._invoker.clean_dex_cache()
        self._invoker.run_apt_only()
        self._invoker.run_javac_task() # 在这个方法里面调用了_generate_java_compile_args
        self._invoker.run_retrolambda()

然后呢? 打包增量Dex推送到手机

官方文档对原理有解析 就不再细说 (很多热修复框架也在说这个)

追查到freeline源码

class GradleIncDexCommand(IncDexCommand):
    def __init__(self, module_name, invoker):
        IncDexCommand.__init__(self, module_name, invoker)

    def execute(self):
        should_run_dex_task = self._invoker.check_dex_task() 
        if not should_run_dex_task:
            self.debug('no need to execute')
            return

        self.debug('start to execute dex command...')
        self._invoker.run_dex_task()

  # 根据字节码打包成dex  
  def run_dex_task(self):
        patch_classes_cache_dir = self._finder.get_patch_classes_cache_dir()
        # dex_path = self._finder.get_dst_dex_path()
        dex_path = self._finder.get_patch_dex_dir()
        add_path = None
        if is_windows_system():
            add_path = str(os.path.abspath(os.path.join(self._javac, os.pardir)))
            dex_args = [self._dx, '--dex', '--multi-dex', '--output=' + dex_path, patch_classes_cache_dir]
        else:
            dex_args = [self._dx, '--dex', '--no-optimize', '--force-jumbo', '--multi-dex', '--output=' + dex_path,
                        patch_classes_cache_dir]

        self.debug('dex exec: ' + ' '.join(dex_args))
        output, err, code = cexec(dex_args, add_path=add_path)

        if code != 0:
            raise FreelineException('incremental dex compile failed.', '{}\n{}'.format(output, err))
        else:
            mark_restart_flag(self._cache_dir)

这部分就是检查是否需要进行dex操作然后...

生成增量的部分dex然后插到DebugApp的上面 实现dex的热更新

设置一些标志位什么的(用来表示要不要重启app 要不要重启activity什么的)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容