android外掛自定義之多渠道打包外掛(支援微信資源混淆andResGuard)
前言
自定義android studio外掛?想想就覺得是一件令人興奮的事。最近閒來無事決定完善之前的一些程式碼操作,然後就想到了之前的apk多渠道打包工具,覺得還是太麻煩,何不用外掛的形式引入工程自動打包呢。說做就做…
資料整理
簡單流程圖
ejApkRelease task
1.初始化task
建立EjApkTask繼承gradle api的DefaultTask來建立自己的task
public class EjApkTask extends DefaultTask{
def android
def buildConfigs = []
EjApkTask(){
description = 'Assemble ej APK'
group = 'andChannelApk'
outputs.upToDateWhen { false }
android = project.extensions.android
...
}
@TaskAction
run(){
...
}
}
其中:
group表示在android studio 右側的gradle任務列表為此task建立一個自己的目錄:
會自動將大寫轉為小寫,寫法參照AndResGuard原碼寫的
run方法表示此task執行的方法
建立,並初始化ejApkRelease task
project.afterEvaluate {
System.out.println("ejApkRelease is start")
def taskName = "resguardRelease"
def ejTask = "ejApkRelease"
def task = project.task(ejTask, type: EjApkTask)
//判斷是否存在resguardRelease task
if(project.tasks .findByPath(taskName) != null){
System.out.println("ejApkRelease is exit")
task.dependsOn "resguardRelease"
} else {
task.dependsOn "assembleRelease"
}
}
其中project.afterEvaluate閉包的呼叫位置實在 專案的settings.gradle構建之後,build.gradle構建之前。
task.dependsOn 表示將此任務task的呼叫位置放在resguardRelease或者assembleRelease task的後面
2.task邏輯處理
通過api 獲取app-release.apk檔案位置,獲取gradle配置的簽名檔案,獲取渠道檔案的路徑配置:
//渠道檔案配置位置
String channel = project.properties.get("channelFile")
buildConfigs.each{ config ->
if (config.file == null || !config.file.exists()) {
System.out.println("ejApkRelease EjApkTask apk file not exit 1")
return
}
//簽名檔案
def signConfig = config.signConfig
//app-release.apk位置
String path = config.file.getAbsolutePath()
System.out.println("path:"+path)
InputParam.Builder builder = new InputParam.Builder()
.setChannel(channel)
.setInputFolder(useFolder(config.file))
.setApkPath(path)
.setSignFile(signConfig.storeFile)
.setKeypass(signConfig.keyPassword)
.setStorealias(signConfig.keyAlias)
.setStorepass(signConfig.storePassword)
InputParam inputParam = builder.build()
Main.gradleRun(inputParam)
}
其中:
project.properties.get() 表示獲取執行專案(app)目錄下的gradle.properties檔案裡面的配置
解壓,修改渠道檔案,打包簽名
1.解壓:
解壓方式很簡單,通過java自帶api實現解壓:
@SuppressWarnings("rawtypes")
public static HashMap<String, Integer> unZipAPk(String fileName, String filePath) throws IOException {
checkDirectory(filePath);
ZipFile zipFile = new ZipFile(fileName);
Enumeration emu = zipFile.entries();
HashMap<String, Integer> compress = new HashMap<>();
while (emu.hasMoreElements()) {
ZipEntry entry = (ZipEntry) emu.nextElement();
if (entry.isDirectory()) {
new File(filePath, entry.getName()).mkdirs();
continue;
}
BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
File file = new File(filePath + File.separator + entry.getName());
File parent = file.getParentFile();
if (parent != null && (!parent.exists())) {
parent.mkdirs();
}
//要用linux的斜槓
String compatibaleresult = entry.getName();
if (compatibaleresult.contains("\\")) {
compatibaleresult = compatibaleresult.replace("\\", "/");
}
compress.put(compatibaleresult, entry.getMethod());
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER);
byte[] buf = new byte[BUFFER];
int len;
while ((len = bis.read(buf, 0, BUFFER)) != -1) {
fos.write(buf, 0, len);
}
bos.flush();
bos.close();
bis.close();
}
zipFile.close();
return compress;
}
1.修改渠道檔案:
先刪除解壓包存在的簽名檔案,再尋找assets資料夾是否存在,存在直接修改渠道檔案,不存在則新增assets資料夾並新增渠道檔案,通過渠道檔案獲取要打的渠道,迴圈修改,並打包,簽名:
public void buildApk() throws Exception {
//刪除簽名檔案
File sinFile = new File(tempFile,"META-INF");
if(sinFile.exists()){
FileZipUtils.deleteDir(sinFile);
System.out.println("刪除原簽名檔案");
}
//存放簽名包位置
File fileassets = new File(tempFile,"assets");
if(!fileassets.exists()){
fileassets.mkdirs();
}
File fileEjChannel = new File(fileassets,"ej_channel");
if(!fileEjChannel.exists()){
fileEjChannel.createNewFile();
}
//打包apk位置
File fileChannel = new File(fileApk,"channel");
if(fileChannel.exists()){
fileChannel.delete();
}
File unSinedFiles = new File(fileChannel,"unsign");
File sinedFiles = new File(fileChannel,"sign");
unSinedFiles.mkdirs();
sinedFiles.mkdirs();
File fileChannelTxt = new File(inputParam.channel);
if(!fileChannelTxt.exists()){
System.out.println("channel.txt not exit");
return;
}
//獲取所有渠道
InputStream in = new FileInputStream(fileChannelTxt);
int size = in.available();
byte[] buffer = new byte[size];
in.read(buffer);
in.close();
String allChannel = new String(buffer, "utf-8");
//獲取所有渠道陣列
String[] channels = allChannel.split(",");
//迴圈渠道打包
for(String content : channels){
{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(fileEjChannel),"UTF-8");
System.out.println("channel content:"+content);
//渠道名稱加密
String channel = ChannelEncode.encode(content);
//不加密
// String channel = content;
System.out.println("channel:"+channel);
osw.write(channel, 0, channel.length());
osw.flush();
osw.close();
//壓縮檔案
File outApkUnsin = new File(unSinedFiles,"release-"+content+"-unsin.apk");
FileZipUtils.compress(tempFile,outApkUnsin);
//簽名
File outApkSign = new File(sinedFiles,"release-"+content+"-sin.apk");
signWithV1sign(outApkUnsin,outApkSign);
}
}
}
使用方法
最後講一下使用方式,由於本外掛沒有上傳jcenter,所以需要下載下來編譯一下,編譯方式:
通過雙擊圖中的uploadArchives 任務,會在專案的根目錄建立本地maven倉庫repo
再在專案中引入此外掛即可
這是編譯通過之後會在右側出現:
任務,點選執行此任務即可打出渠道包