一、前言->需求
最近公司的项目需要试上线,安卓包会放到多个渠道进行推广,玩家会进行下载安装登录,后台为了得到渠道包的下载使用数据,就会给每个渠道包加入了不同的渠道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>+ "/AllChannels"<span>);
FileSystem.delete(serverDir </span>+ "/UnSignePackages"<span>);
String[] files </span>= FileSystem.ls(serverDir + "/SignedPackages/"<span>);
</span><span>for</span>(<span>int</span> i = 0; i < files.length; ++<span>i)
{
String file </span>=<span> files[i];
</span><span>if</span>(file.endsWith(".apk"<span>))
{
FileSystem.delete(serverDir </span>+ "/SignedPackages/" +<span> file);
}
}
String version </span>= Config.getString("version").replaceAll("\\.",""<span>);
String app_id </span>= Config.getString("app_id"<span>);
String appchi_id </span>= Config.getString("appchi_id"<span>);
String channel_id </span>= Config.getString("channel_id"<span>);
String channelchi_ids </span>= Config.getString("channelchi_ids"<span>);
String[] channelChiIds </span>= channelchi_ids.split(","<span>);
String xmlFile </span>= serverDir + "/" + file.substring(0,file.lastIndexOf("."))+"/AndroidManifest.xml"<span>;
String xmlStr </span>=<span> FileSystem.read(xmlFile);
String packname </span>= "com.istorm.hllad"<span>;
Pattern pattern </span>= Pattern.compile("com.istorm.\\w+ad"<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("android:name=\"app_id\" android:value=\"\\d{4,8}\"", "android:name=\"app_id\" android:value=\""+app_id+"\""<span>);
xmlStr </span>= xmlStr.replaceFirst("android:name=\"appchi_id\" android:value=\"\\d{4,8}\"", "android:name=\"appchi_id\" android:value=\""+appchi_id+"\""<span>);
xmlStr </span>= xmlStr.replaceFirst("android:name=\"channel_id\" android:value=\"\\d{4,8}\"", "android:name=\"channel_id\" android:value=\""+channel_id+"\""<span>);
</span><span>for</span> (<span>int</span> i = 0; i < channelChiIds.length; i++<span>) {
String channelchi_id </span>=<span> channelChiIds[i];
String newXmlStr </span>= xmlStr.replaceFirst("android:name=\"channelchi_id\" android:value=\"\\d{8}\"", "android:name=\"channelchi_id\" android:value=\""+channelchi_id+"\""<span>);
String newXmlFileName </span>= serverDir + "/AllChannels/"+ channelchi_id +"/AndroidManifest.xml"<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 < channelChiIds.length; i++<span>)
{
String channelchi_id </span>=<span> channelChiIds[i];
String subFileName </span>= file.substring(0, file.lastIndexOf("."<span>));
String newXmlFileName </span>= serverDir + "/AllChannels/" +<span> channelchi_id
</span>+ "/AndroidManifest.xml"<span>;
String dstXmlFileName </span>= serverDir + "/"
+ file.substring(0, file.lastIndexOf("."<span>))
</span>+ "/AndroidManifest.xml"<span>;
FileSystem
.write(dstXmlFileName, FileSystem.read(newXmlFileName), </span><span>false</span><span>);
String outFileName </span>= packname.substring(packname.lastIndexOf(".") + 1<span>,
packname.length());
outFileName </span>= outFileName.substring(0, 3<span>);
</span><span>if</span> (subFileName.indexOf("debug") > -1<span>) {
outFileName </span>+= "_d_"<span>;
} </span><span>else</span><span> {
outFileName </span>+= "_r_"<span>;
}
outFileName </span>+= channelchi_id + "_" + version + "_"
+ DateTime.date("MMddHHmm") + ".apk"<span>;
String cmd </span>= serverDir + "/apktool.bat b " + serverDir + "/"
+ file.substring(0, file.lastIndexOf(".")) + " -o " +<span> serverDir
</span>+ "/UnSignePackages/" +<span> outFileName;
String s </span>= ""<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 + "\n"<span>;
}
} </span><span>catch</span><span> (IOException e) {
e.printStackTrace();
}
System.out.println(</span>"cmd==" +<span> s);
System.out.println(</span>"===打未签名包:" + outFileName + "..............ok!"<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>}
四、演示例子
需要代码的同学们可以加我QQ:296464231(注明加我原因)
Original: https://www.cnblogs.com/hellohuang/p/9152059.html
Author: HolleHuang
Title: apk生成多渠道的安装包
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/611182/
转载文章受原作者版权保护。转载请注明原作者出处!