日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android 渠道打包工具,Android渠道打包技术小结

發布時間:2025/3/11 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 渠道打包工具,Android渠道打包技术小结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

導讀

本文對比了渠道4種渠道打包方式:

與iOS的單一渠道(AppStore)不同,Android平臺在國內的渠道多入牛毛。以我們的App為例,就有27個普通渠道(應用寶,百度,360這種)和更多的推廣專用渠道。我們打包技術也經過了若干次的改進。

1.利用Gradle Product Favor打包

android?{

productFlavors?{

base?{

manifestPlaceholders?=?[?CHANNEL:”0"]

}

yingyongbao?{

manifestPlaceholders?=?[?CHANNEL:"1"]

}

baidu?{

manifestPlaceholders?=?[?CHANNEL:"2"]

}

}

}

AndroidManifest.xml

android:name="CHANNEL"

android:value="${CHANNEL}”/>

原理很簡單,gradle編譯的時候,會根據這個配置,把manifest里對應的metadata占位符替換成指定的值。然后Android這邊在運行期再去取出來就是:

publicstaticString?getChannel(Context?context)?{

String?channel?=?"";

PackageManager?pm?=?sContext.getPackageManager();

try?{

ApplicationInfo?ai?=?pm.getApplicationInfo(

context.getPackageName(),

PackageManager.GET_META_DATA);

String?value?=?ai.metaData.getString("CHANNEL");

if?(value?!=?null)?{

channel?=?value;

}

}?catch?(Exception?e)?{

//?忽略找不到包信息的異常

}

returnchannel;

}

這個辦法,缺點很明顯,每打一個渠道包都會完整得執行一遍apk的編譯打包流程,非常慢。近30個包要打一個多小時…優點就是不依賴其他工具,gradle自己就能搞定。

2.替換Assets資源打包

assets用于存放一些資源。不同與res,assets里的資源編譯時原樣保留,不需要生成什么resouce id之類的東西。因此,我們可以通過替換assets里的文件打出不同的渠道包,而不用每次都重新編譯。

我們知道apk本質上就是個zip文件,那么我們就可以通過解壓縮->替換文件->壓縮的辦法來搞定:

這里給出一份Python3的實現

#?解壓縮

src_file_path?=?'原始apk文件路徑'

extract_dir?=?'解壓的目標目錄路徑'

os.makedirs(extract_dir,?exist_ok=True)

os.system(UNZIP_PATH?+?'?-o?-d?%s?%s'%?(extract_dir,?src_file_path))

#?刪除簽名信息

shutil.rmtree(os.path.join(extract_dir,'META-INF'))

#?寫入渠道文件assets/channel.conf

channel_file_path?=?os.path.join(extract_dir,'assets','channel.conf')withopen(channel_file_path,?mode='w')asf:

f.write(channel)??#?寫入渠道號寫進去

os.chdir(extract_dir)

output_file_name?=?'輸出文件名稱'

output_file_path?=?'輸出文件路徑'

output_file_path_tmp?=?os.path.join(output_dir,?output_file_name?+'_tmp.apk')

#?壓縮

os.system(ZIP_PATH?+?'?-r?%s?*'%?output_file_path)

os.rename(output_file_path,?output_file_path_tmp)

#?重新簽名

#?jarsigner?-sigalg?MD5withRSA?-digestalg?SHA1?-keystore?your_keystore_path

#?-storepass?your_storepass?-signedjar?your_signed_apk,?your_unsigned_apk,?your_alias

signer_params?=?'?-verbose?-sigalg?MD5withRSA?-digestalg?SHA1'+?\

'?-keystore?%s?-storepass?%s?%s?%s?-sigFile?CERT'%?\

(

sign,?#?簽名文件路徑

store_pass,?#?存儲密碼

output_file_path_tmp,

alias?#?別名

)

os.system(JAR_SIGNER_PATH?+?signer_params)

#?Zip對齊

os.system(ZIP_ALIGN_PATH?+?'?-v?4?%s?%s'%?(output_file_path_tmp,?output_file_path))

os.remove(output_file_path_tmp)

在這里,幾個PATH分別表示zip、unzip、jarsigner和zipalign這幾個可執行文件的路徑。

簽名是apk的一個重要機制,它給apk里的每一個文件(META-INF目錄下的除外)計算一個hash值,記錄在META-INF下的若干文件里。Zip對齊能夠優化運行時Android讀取資源的效率,這一步雖然不是必須的,但還是推薦做一下。

采用這個方法,我們不需要再編譯Java代碼,速度有極大地提升。大約每10秒就能打一個包。

同時給出讀取渠道號的實現代碼:

publicstaticString?getChannel(Context?context)?{

String?channel?=?"";

InputStream?is=null;

try?{

is=?context.getAssets().open("channel.conf");

byte[]?buffer?=?new?byte[100];

intl?=is.read(buffer);

channel?=?new?String(buffer,?0,?l);

}?catch?(IOException?e)?{

//?如果讀不到,那么取缺省值

}?finally?{

if?(is!=null)?{

try?{

is.close();

}?catch?(Exception?ignored)?{

}

}

}

returnchannel;

}

順便說一下,還可以用aapt這個工具來替代zip&unzip來實現文件替換:

#?替換assets/channel.conf

os.chdir(base_dir)

os.system(AAPT_PATH?+?'?remove?%s?assets/channel.conf'%?output_file_path_tmp)

os.system(AAPT_PATH?+?'?add?%s?assets/channel.conf'%?output_file_path_tmp)

3.美團給出的一種方案

剛才上文提到META-INF目錄對簽名機制是豁免的,往這里面放東西就可以免去重簽名這一步,美團技術團隊就是這么做的。

import?zipfile

zipped?=?zipfile.ZipFile(your_apk,?'a',?zipfile.ZIP_DEFLATED)

empty_channel_file?=?"META-INF/mtchannel_{channel}".format(channel=your_channel)

zipped.write(your_empty_file,?empty_channel_file)

給META-INFO目錄加入一個名為“mtchannel_渠道號”的空文件,在Java這邊查找到這個文件,取得文件名即可:

publicstaticString?getChannel(Context?context)?{

ApplicationInfo?appinfo?=?context.getApplicationInfo();

String?sourceDir?=?appinfo.sourceDir;

String?ret?=?"";

ZipFile?zipfile?=?null;

try?{

zipfile?=?new?ZipFile(sourceDir);

Enumeration>?entries?=?zipfile.entries();

while?(entries.hasMoreElements())?{

ZipEntry?entry?=?((ZipEntry)?entries.nextElement());

String?entryName?=?entry.getName();

if?(entryName.startsWith("mtchannel"))?{

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)?{

returnret.substring(split[0].length()?+?1);

}?else{

return"";

}

}

這個方法省去了重簽名這一步,速度提升也很大。他們的描述是“900多個渠道不到一分鐘就能打完”,也就是不到0.06s一個包。

4.利用Zip文件comment的終極方案

另外給出了一個終極方案:我們知道Zip文件末尾有一塊區域,可以用來存放文件的comment。改動這個區域,絲毫不會影響Zip文件的內容。

打包的代碼很簡單:

shutil.copyfile(src_file_path,?output_file_path)

withzipfile.ZipFile(output_file_path,?mode='a')aszipFile:

zipFile.comment?=?bytes(channel,?encoding=‘utf8')

這個方法比前一個方法的區別在于,它不會修改Apk的內容,也就不必重新打包,速度又有提升!

按文檔中的說法,這個方法1s內可以打300多個包,也就是說單個包的時間小于10毫秒!

讀取的代碼稍微復雜一些。

Java 7的ZipFile類,有getComment方法,可以輕易地讀取comment值。然而這個方法只在Android 4.4以及更高版本才可用,我們就需要多花點時間把這段邏輯移植過來。所幸這里的邏輯不復雜,我們查看源碼,可以看到主要邏輯都在ZipFile的一個私有方法readCentralDir里,一小部分讀取二進制數據的邏輯在libcore.io.HeapBufferIterator,全部搬過來,整理一下就搞定了:

publicstaticString?getChannel(Context?context)?{

String?packagePath?=?context.getPackageCodePath();

RandomAccessFile?raf?=?null;

String?channel?=?"";

try?{

raf?=?new?RandomAccessFile(packagePath,?"r");

channel?=?readChannel(raf);

}?catch?(IOException?e)?{

//?ignore

}?finally?{

if?(raf?!=?null)?{

try?{

raf.close();

}?catch?(IOException?e)?{

//?ignore

}

}

}

returnchannel;}privatestaticfinal?long?LOCSIG?=?0x4034b50;privatestaticfinal?long?ENDSIG?=?0x6054b50;privatestaticfinalintENDHDR?=?22;privatestaticshort?peekShort(byte[]?src,intoffset)?{

return(short)?((src[offset?+?1]?<

//?Scan?back,?looking?fortheEndOfCentral?Directory?field.?If?the?zip?file?doesn't

//?have?an?overall?comment?(unrelated?toanyper-entry?comments),?we'll?hit?the?EOCD

//?onthefirsttry.

//?Noneedtosynchronize?raf?here--?we?only?do?this?when?we?first?open?the?zip?file.

long?scanOffset?=?raf.length()?-?ENDHDR;

if?(scanOffset?

throw?new?ZipException("File?too?short?to?be?a?zip?file:?"+?raf.length());

}

raf.seek(0);

final?intheaderMagic?=Integer.reverseBytes(raf.readInt());

if?(headerMagic?==?ENDSIG)?{

throw?new?ZipException("Empty?zip?archive?not?supported");

}

if?(headerMagic?!=?LOCSIG)?{

throw?new?ZipException("Not?a?zip?archive");

}

long?stopOffset?=?scanOffset?-?65536;

if?(stopOffset?

stopOffset?=?0;

}

while?(true)?{

raf.seek(scanOffset);

if?(Integer.reverseBytes(raf.readInt())?==?ENDSIG)?{

break;

}

scanOffset--;

if?(scanOffset?

throw?new?ZipException("End?Of?Central?Directory?signature?not?found");

}

}

//?ReadtheEndOfCentral?Directory.?ENDHDR?includes?the?signature?bytes,

//?which?we've?already?read.

byte[]?eocd?=?new?byte[ENDHDR?-?4];

raf.readFully(eocd);

//?Pull?outthe?information?we?need.

intposition?=?0;

intdiskNumber?=?peekShort(eocd,?position)?&?0xffff;

position?+=?2;

intdiskWithCentralDir?=?peekShort(eocd,?position)?&?0xffff;

position?+=?2;

intnumEntries?=?peekShort(eocd,?position)?&?0xffff;

position?+=?2;

inttotalNumEntries?=?peekShort(eocd,?position)?&?0xffff;

position?+=?2;

position?+=?4;?//?IgnorecentralDirSize.

//?long?centralDirOffset?=?((long)?peekInt(eocd,?position))?&?0xffffffffL;

position?+=?4;

intcommentLength?=?peekShort(eocd,?position)?&?0xffff;

position?+=?2;

if?(numEntries?!=?totalNumEntries?||?diskNumber?!=?0?||?diskWithCentralDir?!=?0)?{

throw?new?ZipException("Spanned?archives?not?supported");

}

String?comment?=?"";

if?(commentLength?>?0)?{

byte[]?commentBytes?=?new?byte[commentLength];

raf.readFully(commentBytes);

comment?=?new?String(commentBytes,?0,?commentBytes.length,?Charset.forName("UTF-8"));

}

returncomment;

}

需要注意的是,Android 7.0加入了APK Signature Scheme v2技術。在Android Plugin for Gradle 2.2,這一技術是缺省啟用的,這會導致第三、第四兩種方法打出的包在Android 7.0下面校驗失敗。解決方法有二,一是把Gradle版本改低,二是在signingConfigs/release下面加上配置v2SigningEnabled false。詳細說明見谷歌的文檔

總結

用表格說話

【編輯推薦】

【責任編輯:枯木 TEL:(010)68476606】

點贊 0

總結

以上是生活随笔為你收集整理的android 渠道打包工具,Android渠道打包技术小结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。