Android Multi-Channel packaging

Source: Internet
Author: User

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  &GT;=&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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.