专栏名称: 安卓开发精选
伯乐在线旗下账号,分享安卓应用相关内容,包括:安卓应用开发、设计和动态等。
目录
相关文章推荐
鸿洋  ·  Android AppOpsService是什么? ·  昨天  
鸿洋  ·  打造Android热修复专家 ·  2 天前  
郭霖  ·  从零实现一个 KMP 插桩框架: EzHook ·  5 天前  
郭霖  ·  Android Studio 中的 ... ·  6 天前  
鸿洋  ·  AndroidManifest中配置的各种信息解读 ·  5 天前  
51好读  ›  专栏  ›  安卓开发精选

Android 多渠道打包方式详解(上)

安卓开发精选  · 公众号  · android  · 2016-09-22 08:18

正文

(点击上方公众号,可快速关注)


来源:伯乐在线专栏作者 - joe

链接:http://android.jobbole.com/84758/

点击 → 了解如何加入专栏作者


面试的时候,如果面试官突然问到:你们渠道包是怎么打的?如果你说是用gradle一个一个编译的,然后他很鄙视的说这个效率太低啦,你们写过什么脚本自己打渠道包没?你肯定心里想,卧槽,这么狂炫吊炸天,自己写脚本打包?!其实这个根本也不是太难啦!!今天就来聊聊多渠道打包的原理以及如何自己DIY多渠道打包的工具!


渠道包出现


当一个产品到发版的时候,我们搞Android的就会面临一个超级尴尬的问题:国内这么多的渠道,渠道统计是必须做滴,那么十多个主要渠道再加无限量的地推渠道包就成了一个巨坑了!这一块耗费的时间是一个无底洞啊!!!


方式一览


这里一共会介绍三种渠道包的实现方式,分别是:


1、使用gradle配置直接编译出不同的渠道包。

2、通过反编译修改对应的渠道号。

3、META-INF里面新加一个文件。


Gradle方式


不管是用友盟统计还是其他什么的,首先肯定都是要有一些准备工作的,由于本人就比较了解友盟的,所以就用友盟统计来举例啦!

友盟统计提供了两种渠道统计策略,其实就是一个自动挡的一个手动挡的。


meta-data

        android:name="UMENG_APPKEY"

        android:value="xxxxxxxx"/>

    meta-data

        android:name="UMENG_CHANNEL"

        android:value="${GRADLE_CHANNEL_VALUE}"/>


在对应的build.gradle里面配置对应的信息:


productFlavors.all { flavor ->

    flavor.manifestPlaceholders = [GRADLE_CHANNEL_VALUE: name]

}

productFlavors {

    dev {

    }

    baidu {

        minSdkVersion 18

        applicationId "com.test.michat"

    }

}


如果手动去设置对应的渠道号的话,就在程序入口处调用以下方法:


MobclickAgent. startWithConfigure(UMAnalyticsConfig config)

 

UMAnalyticsConfig(Context context, String appkey, String channelId)

 

UMAnalyticsConfig(Context context, String appkey, String channelId, EScenarioType eType)

 

UMAnalyticsConfig(Context context, String appkey, String channelId, EScenarioType eType,Boolean isCrashEnable)


那么怎么获取到对应的渠道号呢?!这个方法在之后的所有方式中都要使用滴,其实不管是哪种方式,最后都会调用这个方法去读相关数据的!!


private String getChannel(Context context) {

    try {

        PackageManager pm = context.getPackageManager();

        ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);

        return appInfo.metaData.getString("CHANNEL_VALUE");

    } catch (PackageManager.NameNotFoundException ignored) {

    }

    return "";

 

}


 buildVariants.png


当然你也可以使用命令行:gradlew assemble 组装出所有的渠道包!!


反编译方式


gradle方式用着也挺不错的,为什么还要去搞什么反编译这么麻烦的东西呢?因为它有一个很大的问题,那就是每一个包都是要去编译打包的!这是相当的耗时!time is 加班啊!谁也不想加班打渠道包咯!!反编译的方式就是节省了每个渠道包都去编译的时间,而是编译好一个渠道包之后就使用该渠道包,通过反编译动态修改AndroidManifest.xml里面的信息,然后再重新打包签名!


说到反编译,那么这里就不得不提大名鼎鼎的apktool.jar了!纳尼,你说你从未听说过?!没事儿,以前没有听过,现在会用了就行了!!


然后总结一下接下来的一系列套路:


解包->修改相关参数->打包->签名->Zipalign优化


1、解包


apktool d your_original_apk build


你没有看错,就是这样的!因为我们是站在巨人的肩膀上工作的嘛,所以好多工作就不同自己搞了!


执行以上命令之后,如果不出什么意外,你就会得到一个文件夹:



相关代码:


try {

        brut.apktool.Main.main(new String[]{"d", "-f", apkFilePath, "-o", outPath});

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        callback("解包失败 !!!!!\r\n" + e.getMessage());

    }


2、修改对应的参数


打开对应的AndroidManifest.xml,你没有看错,什么都在里面,直接修改就好了!等等,xml解析你不会?!没有关系,这里有dom4j.jar给你使用啦!!

修改反编译之后的AndroidManifest文件相关代码


try {

        File androidManifestFile = new File(appFolderName + File.separator + "AndroidManifest.xml");

        Document document = new SAXReader().read(androidManifestFile);//使用dom4j的sax解析

        Element element = document.getRootElement().element("application");

        List list = element.elements("meta-data");//获取到所有的“meta-data”

        List metaData = manifest.getMetaData();

        boolean isUpdate = false;

        for (MetaData data : metaData) {

            String name = data.getName();

            String value = data.getValue();

            callback(" meta-data name='" + name + "' value='" + value + "'");

            for (Element s : list) {

                Attribute attribute = s.attribute("name");

                //更新相关渠道号

                 if ( "UMENG_CHANNEL".equals(name)&&"UMENG_CHANNEL".equals(attribute.getValue())) {//更换相关的渠道号

                    s.attribute("value").setValue(value);

                    isUpdate = true;

                    callback("更新1 AndroidManifest.xml meta-data name='" + attribute.getValue() + "' value='" + value + "'");

                   break;

                 }

 

            }

        }

        if(isUpdate){//更新后重新写入

            XMLWriter writer = new XMLWriter(new FileOutputStream(androidManifestFile));

            writer.write(document);

            writer.close();

            callback("更新 AndroidManifest.xml 完成 ~ ");

        }

    } catch (Exception e) {

        e.printStackTrace();

        return false;

    }


3、打包


apktool b build your_unsigned_apk


还是这么简单:


try {

        brut.apktool.Main.main(new String[]{"b", buildApkFolderPath, "-o", buildApkOutPath});

        return true;

    } catch (Exception e) {

        e.printStackTrace();

        callback("打包失败 !!!!!\r\n" + e.getMessage());

    }


4、签名


jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore your_keystore_path -storepass your_storepass -signedjar your_signed_apk, your_unsigned_apk, your_alias


这个是jdk里面直接提供了的,只要你的环境变量配置好了的,就没有什么问题啦!重新签名相关代码


executeCommand("jarsigner", "-verbose", "-sigalg", "SHA1withRSA", "-digestalg", "SHA1", "-keystore", keystoreFilePath, apkFilePath, alias, "-storepass", password);

 

 

/**

* 执行命令

*

* @param command 命令

*/

private synchronized boolean executeCommand(String... command) {

    Process process = null;

    BufferedReader reader = null;

    try {

        ProcessBuilder builder = new ProcessBuilder();

        builder.command(command);

        builder.redirectErrorStream(true);

        process = builder.start();

        reader = new BufferedReader(new InputStreamReader(process.getInputStream(),"UTF-8"));

        String line;

        while ((line = reader.readLine()) != null) {

            callback(line);

            if (line.contains("Exception") || line.contains("Unable to open")) {

                return false;

            }

        }

        return true;

    } catch (IOException e) {

        e.printStackTrace();

        callback(e.getMessage());

    } finally {

        close(reader);

        if (process != null) {

            process.destroy();

        }

    }

    return false;

}


5、Zipalign优化



如图所示,sdk/build-tools里面每个版本都是有这个东西的,加到环境变量中就好了!!!


zipalign 优化处理相关代码


* 需要安装并Android SDK并配置环境变量Build Tools路径

* 优化apk文件,这个需要Android Build Tools 中的zipalign程序文件

*

* @param apkFilePath 要优化的apk文件路径

* @param outFilePath 优化后的apk存放文件路径

*/

public boolean zipalign(String apkFilePath, String outFilePath) {

    return executeCommand("zipalign", "-f", "-v", "4", apkFilePath, outFilePath);

}


接下文


专栏作者简介( 点击 → 加入专栏作者 


joe:90后程序猿。。

打赏支持作者写出更多好文章,谢谢!



关注「安卓开发精选」
看更多精选安卓技术文章
↓↓↓