apk生成多渠道的安装包

一、前言->需求

最近公司的项目需要试上线,安卓包会放到多个渠道进行推广,玩家会进行下载安装登录,后台为了得到渠道包的下载使用数据,就会给每个渠道包加入了不同的渠道ID以便统计数据。那问题就来了,每出一个新版本的包,要快速生成几十上百个渠道包,那改怎么办,不可能手动去改ID再去生成吧,这样会很费时费力还容易出错,同时文件还要命名好。所以我就花了两天的时间开发这么个工具解决该问题。另外使其他人方便打包,所以需要增加http功能方便其他人来使用。

二、开始->思路构想

1、使用apktool d -s xxxx.apk 拆包可获得一个xxxx文件夹,里面可以得到明文的AndroidMenifest.xml文件。

2、找到对应的meta-data参数用正则匹配规则进行替换掉。

3、使用apktool b xxxx -o new_xxxx.apk 生成未签名的apk包,注意这个包还是不能安装在手机上的,需要签名才行。

4、使用jarsigner -keystore -keypass istormkj -storepass istormkj -signedjar new_xxxx_singed.apk new_xxxx.apk alise

5、步骤完成。

三、选择的语言与开发

因为时间比较急,所以就直接选择了Java语言进行开发,选择Java的原因是开发快,工具类库比较多,网上资料也相当的丰富,好了,开始编码。

先准备一个配置文件,文件包括:启动http端口,渠道包的版本号、应用ID、应用子ID、主渠道号、子渠道号,登录平台等,参数根据自己决定。如下图

<configuration>
    <option name="httpPort" value="8089"/>
    <option name="version" value="1.0.8"/>
    <option name="app_id" value="1039"/>
    <option name="appchi_id" value="10139"/>
    <option name="channel_id" value="1000124"/>
    <option name="channelchi_ids" value="10002102,10002106,10002108"/>
    <option name="login_env" value="test_113"/>
configuration>

上面的参数key命名建议与AndriodMenifest.xml文件中的Meta-data一致,方便写代码进行修改,我的AndriodMenifest.xml部分内容如下:

        <meta-data android:name="app_id" android:value="1039"/>
        <meta-data android:name="appchi_id" android:value="10139"/>
        <meta-data android:name="channel_id" android:value="1000124"/>
        <meta-data android:name="channelchi_id" android:value="10002106"/>
        <meta-data android:name="login_env" android:value="cg_xz_wan_001"/>

启动服务后,通过http:127.0.0.1:8089/ 可以看到渠道方面的配置参数,如下,这里是可以进行打包(渠道包)和设置渠道参数的操作

html;gutter:true; ============>打多渠道服务器</p> <pre><code> 请求 http:127.0.0.1:8089/?a=b 会进行打包 ,道具是进行解包,下面的代码是一个解包的类。 </code></pre> <p>package gateway;</p> <p>import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Timer; import java.util.TimerTask;</p> <p>import config.FileSystem;</p> <p>/*<em> * * @author hzd * </em>/ public class DecompressionApk extends TimerTask {</p> <pre><code>private String serverDir; public DecompressionApk(String serverDir) { this.serverDir = serverDir; } @Override public void run() { String[] files = FileSystem.ls(serverDir); for(int i = 0; i < files.length; ++i) { String file = files[i]; if(file.endsWith("-debug.apk") || file.endsWith("-release.apk") ) // { FileSystem.delete(serverDir + "/" + file.substring(0,file.lastIndexOf("."))); String cmd = serverDir + "/apktool.bat d -s " + serverDir + "/" + file; Process p = null; try { p = Runtime.getRuntime().exec(cmd); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } BufferedReader in = new BufferedReader(new InputStreamReader( p.getInputStream())); String s = ""; String line = null; try { while ((line = in.readLine()) != null) { s += line + "\n"; } } catch (IOException e) { e.printStackTrace(); } System.out.println("cmd:========\n" + s); new Timer().schedule(new MakeChannelsAndUnsigneApk(serverDir, file),1000); break; } } System.out.println("========解压Apk完成============="); } </code></pre> <p>}</p> <pre><code> 解压完后,进行生成渠道用的AndroidMenifest.xml文件,把每个文件放到渠道号为名的文件夹中,然后现进行打未签名的apk名。 </code></pre> <p>代码如下:</p> <pre><code> <pre><span>package</span><span> gateway; </span><span>import</span><span> java.io.BufferedReader; </span><span>import</span><span> java.io.IOException; </span><span>import</span><span> java.io.InputStreamReader; </span><span>import</span><span> java.util.Timer; </span><span>import</span><span> java.util.TimerTask; </span><span>import</span><span> java.util.regex.Matcher; </span><span>import</span><span> java.util.regex.Pattern; </span><span>import</span><span> config.Config; </span><span>import</span><span> config.DateTime; </span><span>import</span><span> config.FileSystem; </span><span>/**</span><span> * * </span><span>@author</span><span> hzd * </span><span>*/</span> <span>public</span> <span>class</span> MakeChannelsAndUnsigneApk <span>extends</span><span> TimerTask { </span><span>private</span><span> String serverDir; </span><span>private</span><span> String file; </span><span>public</span><span> MakeChannelsAndUnsigneApk(String serverDir,String file) { </span><span>this</span>.serverDir =<span> serverDir; </span><span>this</span>.file =<span> file; } @Override </span><span>public</span> <span>void</span><span> run() { FileSystem.delete(serverDir </span>+ &quot;/AllChannels&quot;<span>); FileSystem.delete(serverDir </span>+ &quot;/UnSignePackages&quot;<span>); String[] files </span>= FileSystem.ls(serverDir + &quot;/SignedPackages/&quot;<span>); </span><span>for</span>(<span>int</span> i = 0; i &lt; files.length; ++<span>i) { String file </span>=<span> files[i]; </span><span>if</span>(file.endsWith(&quot;.apk&quot;<span>)) { FileSystem.delete(serverDir </span>+ &quot;/SignedPackages/&quot; +<span> file); } } String version </span>= Config.getString(&quot;version&quot;).replaceAll(&quot;\\.&quot;,&quot;&quot;<span>); String app_id </span>= Config.getString(&quot;app_id&quot;<span>); String appchi_id </span>= Config.getString(&quot;appchi_id&quot;<span>); String channel_id </span>= Config.getString(&quot;channel_id&quot;<span>); String channelchi_ids </span>= Config.getString(&quot;channelchi_ids&quot;<span>); String[] channelChiIds </span>= channelchi_ids.split(&quot;,&quot;<span>); String xmlFile </span>= serverDir + &quot;/&quot; + file.substring(0,file.lastIndexOf(&quot;.&quot;))+&quot;/AndroidManifest.xml&quot;<span>; String xmlStr </span>=<span> FileSystem.read(xmlFile); String packname </span>= &quot;com.istorm.hllad&quot;<span>; Pattern pattern </span>= Pattern.compile(&quot;com.istorm.\\w+ad&quot;<span>); Matcher m </span>=<span> pattern.matcher(xmlStr); </span><span>while</span><span>(m.find()) { packname </span>=<span> m.group(); </span><span>break</span><span>; } xmlStr </span>= xmlStr.replaceFirst(&quot;android:name=\&quot;app_id\&quot; android:value=\&quot;\\d{4,8}\&quot;&quot;, &quot;android:name=\&quot;app_id\&quot; android:value=\&quot;&quot;+app_id+&quot;\&quot;&quot;<span>); xmlStr </span>= xmlStr.replaceFirst(&quot;android:name=\&quot;appchi_id\&quot; android:value=\&quot;\\d{4,8}\&quot;&quot;, &quot;android:name=\&quot;appchi_id\&quot; android:value=\&quot;&quot;+appchi_id+&quot;\&quot;&quot;<span>); xmlStr </span>= xmlStr.replaceFirst(&quot;android:name=\&quot;channel_id\&quot; android:value=\&quot;\\d{4,8}\&quot;&quot;, &quot;android:name=\&quot;channel_id\&quot; android:value=\&quot;&quot;+channel_id+&quot;\&quot;&quot;<span>); </span><span>for</span> (<span>int</span> i = 0; i &lt; channelChiIds.length; i++<span>) { String channelchi_id </span>=<span> channelChiIds[i]; String newXmlStr </span>= xmlStr.replaceFirst(&quot;android:name=\&quot;channelchi_id\&quot; android:value=\&quot;\\d{8}\&quot;&quot;, &quot;android:name=\&quot;channelchi_id\&quot; android:value=\&quot;&quot;+channelchi_id+&quot;\&quot;&quot;<span>); String newXmlFileName </span>= serverDir + &quot;/AllChannels/&quot;+ channelchi_id +&quot;/AndroidManifest.xml&quot;<span>; FileSystem.write(newXmlFileName, newXmlStr, </span><span>false</span><span>); } HttpRequestHandler.pack_cur_counts </span>= 0<span>; HttpRequestHandler.pack_all_counts </span>=<span> channelChiIds.length; </span><span>//</span><span> 开启定时去打第一个包</span> <span>for</span>(<span>int</span> i = 0; i &lt; channelChiIds.length; i++<span>) { String channelchi_id </span>=<span> channelChiIds[i]; String subFileName </span>= file.substring(0, file.lastIndexOf(&quot;.&quot;<span>)); String newXmlFileName </span>= serverDir + &quot;/AllChannels/&quot; +<span> channelchi_id </span>+ &quot;/AndroidManifest.xml&quot;<span>; String dstXmlFileName </span>= serverDir + &quot;/&quot; + file.substring(0, file.lastIndexOf(&quot;.&quot;<span>)) </span>+ &quot;/AndroidManifest.xml&quot;<span>; FileSystem .write(dstXmlFileName, FileSystem.read(newXmlFileName), </span><span>false</span><span>); String outFileName </span>= packname.substring(packname.lastIndexOf(&quot;.&quot;) + 1<span>, packname.length()); outFileName </span>= outFileName.substring(0, 3<span>); </span><span>if</span> (subFileName.indexOf(&quot;debug&quot;) &gt; -1<span>) { outFileName </span>+= &quot;_d_&quot;<span>; } </span><span>else</span><span> { outFileName </span>+= &quot;_r_&quot;<span>; } outFileName </span>+= channelchi_id + &quot;_&quot; + version + &quot;_&quot; + DateTime.date(&quot;MMddHHmm&quot;) + &quot;.apk&quot;<span>; String cmd </span>= serverDir + &quot;/apktool.bat b &quot; + serverDir + &quot;/&quot; + file.substring(0, file.lastIndexOf(&quot;.&quot;)) + &quot; -o &quot; +<span> serverDir </span>+ &quot;/UnSignePackages/&quot; +<span> outFileName; String s </span>= &quot;&quot;<span>; </span><span>try</span><span> { Process p </span>=<span> Runtime.getRuntime().exec(cmd); BufferedReader in </span>= <span>new</span> BufferedReader(<span>new</span><span> InputStreamReader( p.getInputStream())); String line </span>= <span>null</span><span>; </span><span>while</span> ((line = in.readLine()) != <span>null</span><span>) { s </span>+= line + &quot;\n&quot;<span>; } } </span><span>catch</span><span> (IOException e) { e.printStackTrace(); } System.out.println(</span>&quot;cmd==&quot; +<span> s); System.out.println(</span>&quot;===打未签名包:&quot; + outFileName + &quot;..............ok!&quot;<span>); </span><span>new</span> Timer().schedule(<span>new</span> MakeSingedApk(serverDir, outFileName),1000<span>); } } }</span></pre> 最后就是要进行签包了,相关的代码如下: </code></pre> <p>package gateway;</p> <p>import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Timer; import java.util.TimerTask;</p> <p>import config.FileSystem;</p> <p>/*<em> * * @author hzd * </em>/ public class DecompressionApk extends TimerTask {</p> <pre><code>private String serverDir; public DecompressionApk(String serverDir) { this.serverDir = serverDir; } @Override public void run() { String[] files = FileSystem.ls(serverDir); for(int i = 0; i < files.length; ++i) { String file = files[i]; if(file.endsWith("-debug.apk") || file.endsWith("-release.apk") ) // { FileSystem.delete(serverDir + "/" + file.substring(0,file.lastIndexOf("."))); String cmd = serverDir + "/apktool.bat d -s " + serverDir + "/" + file; Process p = null; try { p = Runtime.getRuntime().exec(cmd); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } BufferedReader in = new BufferedReader(new InputStreamReader( p.getInputStream())); String s = ""; String line = null; try { while ((line = in.readLine()) != null) { s += line + "\n"; } } catch (IOException e) { e.printStackTrace(); } System.out.println("cmd:========\n" + s); new Timer().schedule(new MakeChannelsAndUnsigneApk(serverDir, file),1000); break; } } System.out.println("========解压Apk完成============="); } </code></pre> <p>}

四、演示例子

apk生成多渠道的安装包

apk生成多渠道的安装包

apk生成多渠道的安装包

需要代码的同学们可以加我QQ:296464231(注明加我原因)

Original: https://www.cnblogs.com/hellohuang/p/9152059.html
Author: HolleHuang
Title: apk生成多渠道的安装包

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/611182/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • vue进阶(一)

    vue.js 数据绑定 定义:将数据和视图相关联,当数据发生变化时,可以自动更新视图。 语法 1) 插值 使用双大括号{{}},双大括号会将里面的值当做字符串进行处理;而如果值是H…

    数据库 2023年6月6日
    096
  • vue项目发布到docker nginx的方法总结

    我总结了以下两种方法可用 一、通过制作镜像,使用镜像生成docker 这个是比较常用的方法,制作镜像(images)后,如果还要生成docker可以直接使用镜像比较方便以下是操作步…

    数据库 2023年6月9日
    082
  • 【黄啊码】MySQL入门—2、使用数据定义语言(DDL)操作数据库

    大家好!我是黄啊码,上一节的知识点你了解多少了,掌握了多少了,别偷懒哦,今天我们就来掌握一下数据定义语言,说得高级点就是Data Definition Language,简称DDL…

    数据库 2023年6月16日
    088
  • MySQL45讲之备库并行复制策略

    前言 本文主要介绍 MySQL 备库的并行复制策略。 为什么备库需要并行复制 如果主数据库有大量的更新操作,因为主数据库可以并发写入,而从数据库只能在单个线程中执行,那么从数据库的…

    数据库 2023年5月24日
    074
  • Python–paramiko

    paramiko包含两个核心组件:SSHClient和SFTPClient。 SSHClient的作用类似于Linux的ssh命令,是对SSH会话的封装,该类封装了传输(Trans…

    数据库 2023年6月9日
    071
  • ODA→Teigha胎压–学习笔记

    数据库结构 记录对象表 对象字典 具有固定类型和固定可访问操作的特定对象的固定结构的固定集合 可以存储和操作各种类型的对象,包括它自己。 每个记录都有一个特定的类类型,每个表也有一…

    数据库 2023年6月14日
    061
  • centos下安装jdk8和maven

    创建目录 mkdir -p/usr/local/java/ 网盘拉去JDK和Maven 链接:https://pan.baidu.com/s/1GgJk8ji0r-tjGAj_ea…

    数据库 2023年6月6日
    066
  • CentOS7源码安装Python、virtualenv虚拟环境安装、uwsgi安装配置

    一:Python源码安装 1:Python源码下载XZ开头的 https://www.python.org/downloads/source/ 2:解压:tar xf Python…

    数据库 2023年6月6日
    0100
  • Python学习笔记2(未完待续)

    Python学习笔记2(未完待续) 解决plt中文乱码问题-方法1 plt.rcParams[“font.sans-serif”]=[“SimH…

    数据库 2023年6月14日
    068
  • 阿里巴巴编码规范-考试认证

    阿里巴巴编码规范-考试认证 雨打梨花深闭门,忘了青春,误了青春。 1、注册阿里云账号 2、购买认证 需要怒支付一顿早餐Q,可以用支付宝支付,选择支付宝支付然后直接输入支付密码就OK…

    数据库 2023年6月14日
    0171
  • 控制反转(IOC容器)-Autofac入门

    注意:本文为原创文章,任何形式的转载、引用(包括但不限于以上形式)等,须先征得作者同意,否则一切后果自负。 简介 Autofac 是一个令人着迷的.NET IoC 容器。 它管理类…

    数据库 2023年6月14日
    076
  • 实现线程的两种方式

    实现Runnable接口如果当前类 不仅要继承其他类( 非Thread类), 还要实现多线程,那么 只能通过当前类实现 Runnable接口来 创建Thread类对象。 实现Run…

    数据库 2023年6月16日
    0104
  • Java Script 原型链原理与继承

    所有对象都有隐式原型; 原型也是对象,也有隐式原型. function User() {}console.log(User.prototype); function User() …

    数据库 2023年6月11日
    063
  • podman基础用法

    podman的基础设置和使用 运行示例容器 列出正在运行的容器 检查正在运行的容器 测试httpd服务器 查看容器的日志 查看容器进程pid 对容器执行检查操作 还原容器 迁移容器…

    数据库 2023年6月14日
    0129
  • leetcode 617. Merge Two Binary Trees 合并二叉树(简单)

    一、题目大意 给你两棵二叉树: root1 和 root2 。 想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新…

    数据库 2023年6月16日
    065
  • 多商户商城系统功能拆解24讲-平台端分销会员

    多商户商城系统,也称为B2B2C(BBC)平台电商模式多商家商城系统。可以快速帮助企业搭建类似拼多多/京东/天猫/淘宝的综合商城。 多商户商城系统支持商家入驻加盟,同时满足平台自营…

    数据库 2023年6月14日
    087
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球