zoukankan      html  css  js  c++  java
  • 自动化打包资源混淆集成python实践----打包

    1、自动化打包方案

      1)友盟多渠道多渠道打包

        2)gradle productFlavors系统的条件编译

        3)美团打包

        4)APK文件注释写入渠道号

    2、各打包方案简介

           1)友盟多渠道多渠道打包(window 绿色版,gradle版本 现在过时)

            

            原理:拆包分解apk,修改AndroidManifest.xml二进制文件后,再重新打包

            耗时:较短(多渠道打包时,避免了多次dex过程,aapt过程)

            渠道号保存方式:保存在AndroidManifest.xml 文 件 meta 数据中

            不足:较难保证AndroidManifest.xml二进制修改正确,可能会出现兼容性问题;多渠道apk 命名不能预定,需要额外处理重命名apk文件。

         

          2).gradle productFlavors系统的条件编译

      

          原理: 每个渠道包,都走完整个打包(appt、混淆、dex,签名)流程

          耗时:长(跟渠道包量成正比)

          渠道号保存方式:保存在AndroidManifest.xml 文 件 meta 数据中

         3)美团方案

         

         原理:在APK文件的META-INF目里增加渠道文件

         耗时:快(多个渠道包,只有一次完整打包流程)

         渠道号保存方式:mtchannel_yybcpd文件名中

         

        4)APK文件注释写入渠道号

         

        

     原理:Android 使用的apk包的压缩方式是zip,与zip有相同的文件结构,正确写入 File Comment 部分,不破坏压缩包、不用重新打包前提下,写入自己想要的数据。

     耗时:快

     渠道号保存方式:保存在zip 注释中。

    3、android 打包流程和签名机制:

      1)android 打包流程

     

       ps:代码混淆在dx 生成dex 之前进行。

        

      2)签名机制

        (1)生成MANIFEST.MF文件 遍历apk包中的所有文件(entry),对非文件夹非签名 文件的文件,逐个生成SHA1的数字签名信息,再用 Base64进行编码

        (2)生成CERT.SF文件 对生成的Manifest,使用SHA1-RSA算法,用私钥进 行签名

        (3)生成CERT.RSA文件 对CERT.SF文件做签名,内容存档(公钥、所采用的加 密算法)到中CERT.RSA

         ps:从第一步规则源码,/build/tools/signapk/SignApk.java ,关键源码代码如下:

         

        从源码中,可以看出空文件夹免签名,并看不出来美团方案怎样规避签名机制。

        从apk安装过程签名校验关键类:PackageParser.java   collectCertificates()方法:

       

      从上图可以看空文件夹和META-INFO文件夹中的文件、AndroidManifest.xml免签名检验的,美团就是通过在META-INFO文件夹

          新增渠道号文件,实现多渠道打包,当然不需要进行zipalign操作(通过zipalign -c -v 4 application.apk检验包是否已经对齐)。 

    4、python实现方案3和方案4(python2.7 windos环境):

      1)思路:1.执行gradle 打包命令

         2.监听gradle 打包进程,打包结束后

          方案3:拷贝apk,对apk进行zipfile 文件操作,在META-INFO文件夹中新增多渠道文件。

          方案4:拷贝apk,对apk添加zip comment.

        

      2)python代码实现:

             (1)执行gradle 打包命令     

      cmd = 'gradle assembleInnerRelease ' #假设 builde.gradle productFlavors 中配置inner
      pro = subprocess.Popen(cmd,shell = True)

      (2)监听进程(做了简单的封装)

       

    #监听进程 用来监听gradle 打包进程,后文微信资源混淆进程等
    def func_listen_process(Process,listener):
        if listener == None or not isinstance(listener,ProcessListener):
            print 'func_listen_process: ' + process + ' listener is null or not processListener interface'
            return
        
        listener.start()
        
        while (True):   
            time.sleep(1)  
            retCode = subprocess.Popen.poll(pro)   
            if retCode is not None:
                listener.end()
                break
            else:
                listener.doing()
    

    #抽象类加抽象方法就等于面向对象编程中的接口 from abc import ABCMeta,abstractmethod #定义接口 class ProcessListener: __metaclass__ = ABCMeta #指定这是一个抽象类 @abstractmethod #抽象方法 def start(self): pass @abstractmethod #抽象方法 def doing(self): pass @abstractmethod #抽象方法 def end(self): pass

       (3)监听gradlle打包进程

    #监听gradle 打包进程
    func_listen_process(pro,GradlePackProcessListener())
    #gradle打包进程监听器 class GradlePackProcessListener(ProcessListener): curTime = 0 apkCount = 0 def __init__(self): return def start(self): self.curTime = time.time() def doing(self): return def end(self):     apkCount = func_channelsReleasePack(workSpace) self.curTime = time.time() - self.curTime; print " android channels pack cost total time : " + str(int(self.curTime)) +'s , ' + str(apkCount) + ' apks' #多渠道打正式包 channelsFile.txt 每行一个渠道号 def func_channelsReleasePack(workSpace): apkPath = workSpace + '/build/outputs/apk' channelsFile = open(workSpace + '/channelsFile.txt', "r") apks = [] countApk = 0 global version print u" start channle release pack " while True: channelName = channelsFile.readline() channelName = channelName.strip(' ').strip(' ') if channelName: channelApkName = apkPath + '/' + apkPrefix + '_' + channelName + '_' + version +'.apk' shutil.copyfile(apkPath+'/' + apkPrefix + '_inner_' + ''+ version +'.apk',channelApkName) apks.append(apkPrefix + '_' + channelName + '_' + version +'.apk') zipped = zipfile.ZipFile(channelApkName, 'a', zipfile.ZIP_DEFLATED) empty_channel_file = "META-INF/channel_{channel}".format(channel = channelName) newChannelFile = open(apkPath+'/channel_'+ channelName, 'w') newChannelFile.close() zipped.write(apkPath+'/channel_'+ channelName,empty_channel_file) zipped.close(); os.remove(apkPath+'/channel_'+ channelName) print '---> /' + apkPrefix +'_' + channelName + '_' + version +'.apk' countApk = countApk + 1; else: channelsFile.close() #删除出多余的文件 func_delFiles(apkPath,apks) break print u" end channle release pack" #func_zipalignApks(apks) return countApk;

     (4)获取应用获取渠道号(这里只给出方案3渠道号获取方法,方案四渠道号获取方法见一种为 Apk 动态写入信息的方案):

    /**
         * 获取友盟渠道
         * #app安装后,拷贝保存在/data/app/目录下
         * @return
         */
        public static String getChannel(Context mContext) {
            ApplicationInfo appinfo = mContext.getApplicationInfo();
            String sourceDir = appinfo.sourceDir;
            String ret = "";
            ZipFile zipfile = null;
            try {
                Log.d("sihaixuan", "app source dir : " + sourceDir);
                zipfile = new ZipFile(sourceDir);
                Enumeration<?> entries = zipfile.entries();
                while (entries.hasMoreElements()) {
                    ZipEntry entry = ((ZipEntry) entries.nextElement());
                    String entryName = entry.getName();
                    Log.d("sihaixuan",entryName + "");
                    if (entryName.contains("channel")) {
                        ret = entryName;
    
                        break;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (zipfile != null) {
                    try {
                        zipfile.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            String[] split = ret.split("_");
            if (split != null && split.length >= 2) {
                return ret.substring(split[0].length() + 1);
    
            } else {
                return "";
            }
        }


    (5)打包测试

     

     5、总结

      

     ps:gradle 定义 task 也可以实现以上方案,新一代Android渠道打包工具:1000个渠道包只需要5秒实现了apk 注释添加渠道号

    github:AndroidPackCi

    参考资料:

       团Android自动化之旅—生成渠道包

       新一代Android渠道打包工具:1000个渠道包只需要5秒

             友盟渠道打包工具

            Android签名机制之---签名过程详解

           Android源码查看网址http://androidxref.com/

          一种为 Apk 动态写入信息的方案

  • 相关阅读:
    LeetCode 227. Basic Calculator II
    LeetCode 224. Basic Calculator
    LeetCode 103. Binary Tree Zigzag Level Order Traversal
    LeetCode 102. Binary Tree Level Order Traversal
    LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode 105. Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode 169. Majority Element
    LeetCode 145. Binary Tree Postorder Traversal
    LeetCode 94. Binary Tree Inorder Traversal
    LeetCode 144. Binary Tree Preorder Traversal
  • 原文地址:https://www.cnblogs.com/sihaixuan/p/5320840.html
Copyright © 2011-2022 走看看