Now Android multi-channel packaging is commonly used gradle set Productflavor way, through Gradle AR, you can execute a command, hit multiple packages, but this way every time to go through the packaging process, and now many packages are just channel number inconsistent, There is no need to go through the compilation and packaging process again.
See the United States Regiment solution, they take advantage of the signature of the loophole, META-INF add empty files in the directory, can not re-sign the application, this article describes a user to execute the gradle ar command, automatically run the channel package generation script, hit multiple channel package. To get started with the Gradle script, see the Dengfanping blog post: http://blog.csdn.net/innost/article/details/48228651.
The following is a packaged script:
apply plugin: ' com.android.application ' def versionnamestring= "1.0" def versioncodeint=1def appname= "pack Test" //the name of your app def releaseapk= ' app/build/outputs/apk/ app-release.apk ' Def packagechannel (string versionname,string appname,string releaseapk) { try { def stdout = New bytearrayoutputstream () exec { //Execute Python script commandLine ' Python ', Rootproject.getrootdir (). GetAbsolutePath () + "/app/mulit _channel.py ",versionname,appname,releaseapk standardOutput = stdout } returN stdout.tostring (). Trim () } catch (ignored) { return "UnKnown"; }}android { compileSdkVersion 22 buildToolsVersion "22.0.1" defaultConfig { applicationId " Com.ndktest " minSdkVersion 14 targetsdkversion 22 versioncode versionCodeInt versionName versionNameString } signingConfigs { debug { // No debug config } release { storefile file (".. /keystore/netstars.keystore ") storepassword "123456" keyalias "Netstars.keystore" keypassword "123456" } } buildTypes { release { buildConfigField "boolean", "Log_debug", "false " minifyEnabled true zipalignenabled true // Remove useless resource files shrinkResources true signingconfig Signingconfigs.release proguardfiles getdefaultproguardfile (' proguard-android.txt '), ' Proguard-rules.pro ' } debug { minifyEnabled false debuggable true } } sourceSets { Main { jnilibs.srcdirs = [' Libs '] } } project.afterEvaluate { //after release is executed tasks.getbyname (" Assemblerelease ") { it.dolast{ def rapk=new file (releaseapk); if (Rapk.exists ()) { packagechannel (Versionnamestring,appname,rapk.absolutepath) } &nbSp; } } }}dependencies { compile filetree (dir: ' Libs ', include: [' *.jar ']) compile ' com.android.support:appcompat-v7:22.2.0 '}
Python script:
#!/usr/bin/python# coding=utf-8import zipfileimport shutilimport osimport datetimeimport sys# empty file Easy to write this empty file into the APK package as a channel file src_empty_file = ' Empty.txt ' # Create an empty file (does not exist) F = open (src_empty_file, ' W ') f.close () # get channel list channel_file = ' Channel.txt ' F = open (channel_file) lines = f.readlines () f.close () src_apk=sys.argv[3]# file name (with extension) src_apk_file_name = os.path.basename (src_apk) print (src_apk_ file_name) # split file name with suffix temp_list = os.path.splitext (src_apk_file_name) # name without extensionsrc_apk_name = temp_list[0]# suffix name, including . for example: ".apk " src_ apk_extension = temp_list[1]# Create a build directory, associated with file name output_dir = '.. /output ' + '/' # directory does not exist then create if not os.path.exists (Output_dir): Os.mkdir (Output_dir) # to traverse the channel number and create the APK file corresponding to the channel number For line in lines: # get the current channel number because you get it from the channel file with \ n, all strip a bit Target_channel = line.strip () #获取日期 now = Datetime.datetime.now () nowtime=now.strftime ('%y-%m-%d ') # Splicing corresponding channel number Apk length=len (SYS.ARGV) if length>1 : target_apk = output_dir +sys.argv[2]+ "V" + sys.argv[1]+ "_" +nowtime+ "_" + target_channel + src_apk_extension else: target_apk = output_dir +src_apk_ name + "_" + target_channel + src_apk_extension # Copy build new Apk shutil.copy (src_apk, target_apk) # Zip Gets the newly created APK file Zipped = zipfile. ZipFile (target_apk, ' a ', zipfile. zip_deflated) # Initialize channel information empty_channel_file = Meta-inf/channel_{channel} ". Format (channel = target_channel) # write channel information zipped.write (Src_empty_file, empty_channel_file) # Close the Zip stream zipped.close ()
1. Get to channel number:
import android.content.context;import android.content.sharedpreferences;import android.content.sharedpreferences.editor;import android.content.pm.applicationinfo;import android.content.pm.packagemanager.namenotfoundexception;import android.preference.preferencemanager; Import android.text.textutils;import java.io.ioexception;import java.util.enumeration;import java.util.zip.zipentry;import java.util.zip.zipfile; /*** *https://github.com/gavinct/ androidmultichannelbuildtool ***/public class channelutil { private static final String CHANNEL_KEY = "CHANNEL"; private static final string channel_version_key = "CHANNEL_VERSION"; private static String mChannel; /** * return to market. If get failed return "" * @param context * @return */ public static string getchannel (context context) { return getchannel (context, ""); } /** * back to market. if get failed return defaultchannel * @param context * @param defaultChannel * @return */ public static string getchannel (Context context, string defaultchannel) { //in-Memory get if (! Textutils.isempty (Mchannel)) { return mchannel; Get in } //sp Mchannel = getchannelbysharedprefErences (context); if (! Textutils.isempty (Mchannel)) { return mchannel; } //get from apk &NBSP;MCHANNEL&NBSP;=&NBSP;GETCHANNELFROMAPK (Context, channel_key); if (! Textutils.isempty (Mchannel)) { //Save SP in standby savechannelbysharedpreferences (Context, mchannel); return mChannel; } //all get failed return defaultChannel; } /** * get version information from the APK * @ param context * @param channelkey * @return */ private static string getchannelfromapk (Context context, string channelkey) { //get applicationinfo appinfo = from the APK package context.getapplicationinfo (); string sourcedir = appinfo.sourcedir; //by default in meta-inf/, so you need to stitch it up a bit. String key = "meta-inf/" + channelkey ; 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 (key)) { ret = entryName; break; } } } catch (ioexception e ) { e.printstacktrace (); } finally { if (zipfile != null) { try { zipfile.close (); } catch ( Ioexception e) { e.printstacktrace (); } } } String[] split = Ret.split ("_"); string channel = ""; if (split != null && split.length >=&NBSP;2) { channel = Ret.substring (Split[0].length () + 1); } return channel; } /** * Local save channel & corresponding version number * @param context * @param channel */ private static void Savechannelbysharedpreferences (Context context, string channel) { sharedpreferences sp = preferencemanager.getdefaultsharedpreferences (context); editor editor = sp.edit (); Editor.putstring (Channel_key, channel); editor.putint (CHANNEL_VERSION_ Key, getversioncode (context)); editor.commit (); } /** * get channel * @param from the SP context * @return NULL to get an exception, the value in the SP is invalidated, the SP does not have this value */ private static string getchannelbysharedpreferences (Context context) { sharedprefereNces sp = preferencemanager.getdefaultsharedpreferences (context); int currentversioncode = getversioncode (context); if ( currentversioncode == -1) { //get error return ""; } int versioncodesaved = sp.getint (channel_version_key, -1); if (versioncodesaved == -1) { //local no stored channel corresponding version number //for the first time use or Originally stored version number exception return ""; } if (currentversioncode != versioncodesaved) { return ""; } return sp.getstring (channel_key, ""); } /** * get the version number * @param context * from the package information @return */ private static int getversioncode (Context context) { try{ Return context.getpackagemanager (). Getpackageinfo (Context.getpackagename (), 0) .versionCode; }catch (namenotfoundexception e) { e.printstacktrace (); } return -1; }}
The Friend League SDK provides the ability to set channel numbers through code, combined with the above packaging script and get the script information code, I believe that the multi-channel packaging problem can be solved basically.
Item Demo:http://git.oschina.net/fengcunhan/androidmulitchannel
Android Multi-Channel packaging