APP多平台快速切换

因为公司的APP需要向多平台发布,并且同一份APP源码会切换厂商发多种版本的APP。然而,虽然我有做厂商的区分文件,但每次源码切换厂商总是耗时,而且手动切换(进行文本覆盖),总有时候会有些漏的和错的。特别向AndroidManifest.xml文件,大部分的内容是相同的,但因为有接入第三方的sdk,有些差异的地方需要手动改,多版本维护,会存在错漏。
为此,为了减轻工作量,特意做了个快速替换厂商文件,及部署APK的脚本。

先说下我的思路,gradle命令中

gradle assembleDebug //打包Debug包
gradle assembleRelease //打包Release包

可以进行APK的打包,当然需要部署gradle的环境。
gradle的命令需要jdk8的版本进行支持。
我当前的gradle版本是gradle-3.3
http://download.csdn.net/download/feiniyan4944/10178513

然后通过jarsigner命令可以对APK进行签名

jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore 签名文件 -storepass
密码 -signedjar 打包后的APK路径 未打包的APK路径 别名

又因为上面的命令都是在cmd跑的,然后脚本我选择了bat脚本处理。

剩下的就是对存在差异数据厂商的文件进行处理。但用bat脚本我不会处理这么大量的数据,因为我是做java开发的,于是选择java进行处理了。

先附上效果图一张

这里写图片描述
这里写图片描述

好了附上我的bat源码
appbuild.bat

:: 不显示后续命令行及当前命令行
@echo off

::选择厂商
set platform=0
::获取当前脚本的路径
set bulidPath=%cd%
::取得上一级工程目录
cd..
set iremotePath=%cd%
::差异厂商的apk存放路径
set folderName=jwzh
:: 版本号
set version=
goto ChoosePlatform
:ChoosePlatform
@echo *******************************
@echo.
@echo          版本打包
@echo.
@echo *******************************
@echo.
@echo.
@echo 请选择厂家版本
@echo 1.百度 baidu
@echo 2.谷歌 google
:: /c按键列表 /m提示内容 /d默认选择 /t等待秒数   /d 必须和 /t同时出现  
choice /c:123 /m:"" /d:1 /t:20
if %errorlevel%==1 goto baidu
if %errorlevel%==2 goto google

:baidu
set platform=1
set folderName=baidu
@echo 百度版本
goto platformReplace

:google
set platform=2
set folderName=google
@echo 谷歌版本
goto platformReplace



::java代码厂商文件替换
:platformReplace
@echo 正在编译厂商处理文件...
cd %bulidPath%\java
javac -cp AXmlResourceParser.jar;jdom-2.0.2.jar PlatformChange.java
@echo 正在进行厂商文件处理...
@echo.
java -cp AXmlResourceParser.jar;jdom-2.0.2.jar; PlatformChange %platform% %iremotePath%

@echo.


::获取配置文件的版本号,这里的配置文件不是apk本身的文件,是做的厂商数据存储文件
::开启变量延迟
setlocal enabledelayedexpansion
@echo 读取版本号
set platformPath=%iremotePath%\app\platformres\%folderName%\platform.xml
@echo %platformPath%
for /f "delims=" %%i in (%platformPath%) do (
    echo "%%i"|findstr "version" >nul
    if !errorlevel! equ 0 (
        set "version=%%i"
        goto version_goto
    )
)
goto assemble
:version_goto
::关闭变量延迟
setlocal disabledelayedexpansion    
for /f tokens^=2^ delims^=^" %%a in ("%version%") do set "version=%%a"
echo 当前版本号为:%version%
@echo.

:assemble
::打包前清空其文件夹下所有文件,防止打包不成功签名成旧版本的apk
del /q /s %iremotePath%\app\build\outputs\apk
set assemble=
cd %iremotePath%
@echo 请选择打包类型
@echo 1.Debug
@echo 2.Release
@echo 3.取消打包
choice /c 123 /m "" /d 2 /t 20
if %errorlevel%==1 goto debug 
if %errorlevel%==2 goto release 
if %errorlevel%==3 goto end

::gradle需要jdk8支持
:debug
@echo.
set assemble=1
@echo 开始进行debug打包
CALL %bulidPath%\assemble.bat
md %bulidPath%\apk\%folderName%
copy %iremotePath%\app\build\outputs\apk\debug\app-debug.apk %bulidPath%\apk\%folderName%\app_Debug_%folderName%_%version%.apk
@echo.
@echo debug版本app_Debug_%folderName%_%version%.apk打包完毕
goto end

:release
@echo.
set assemble=2
@echo 开始进行release打包
CALL %bulidPath%\assemble.bat
@echo.
@echo release版本打包完毕
goto key

::签名
:key
@echo.
md %bulidPath%\apk\%folderName%
::签名后的apk地址
set apkname=%bulidPath%\apk\%folderName%\app_%folderName%_%version%.apk
::未签名APK路径
set apkSigned=%iremotePath%\app\build\outputs\release\apk\app-release-unsigned.apk
::密钥
set key=%iremotePath%\app\AndroidappKey.jks
::密码
set password=123456
::别名
set keystore=app

echo 签名信息
echo 签名后的apk地址:%apkname%
echo 未签名APK路径:%apkSigned%
echo 密钥文件地址:%key%
echo 密钥密码:%password%
echo 别名:%keystore%
echo.
echo 正在进行apk签名...
::签名
::jarsigner打包需要jdk7支持
::jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore [你的keystore] -storepass [keystore的密码] -signedjar [签名后的apk] [未签名的apk] [keystore的别名]
jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore %key% -storepass %password% -signedjar %apkname%  %apkSigned% %keystore%
echo.
echo 签名完成
goto end

:end
echo.
if %platform%==1 echo 百度版本打包完毕
if %platform%==2 echo 谷歌版本打包完毕
echo.
pause

:: 冒号后紧跟一个非字母数字的一个特殊符号,goto无法识别的标号,可以起到注释作用
:: UTF-8格式会出现英文乱码,要ANSI 格式

assemble.bat

@echo off
if "%assemble%"=="1" goto debug
if "%assemble%"=="2" goto release


:debug
gradle assembleDebug

:release
gradle assembleRelease

处理厂商文件的java源码
PlatformChange.java

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;
import java.util.Scanner;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.xml.sax.InputSource;



public class PlatformChange {
    static String platform;//厂商
    static String iremotePath;//工程地址
    static String version;//版本

    static String platforPackage;//厂商包名
    static String jpushkey;//推送的appkey
    static String folderName;

    public static void main(String[] args) {
        platform = args[0];//厂商
        iremotePath = args[1];//工程地址

        System.out.println("厂商" + platform);
        System.out.println("工程地址:" + iremotePath);

        System.out.println("读取版本信息文件");
        readPlatformXml();
        System.out.println("处理AndroidManifest版本信息");
        changeAndroidManifest();
        String oldPath = iremotePath + "\\app\\platformres\\" + folderName + "\\res";
        String newPath = iremotePath + "\\app\\src\\main\\res";
        System.out.println("厂家文件夹替换");
        copyFolder(oldPath,newPath);
        //修改build.gradle包名
        System.out.println("处理build.gradle包名");
        changeBuild();
    }

    /**
     * 读取版本信息
     */
    public static void readPlatformXml(){
        if("1".equals(platform)){
            folderName = "baidu";
        }else if("2".equals(platform)){
            folderName = "google";
        }
        boolean isChange = false;
        String platformXmlPath = iremotePath + "\\app\\platformres\\" + folderName + "\\platform.xml";//厂商文件
        SAXBuilder builder = new SAXBuilder();
        Document document = null;
        try{
            InputStreamReader isr = new InputStreamReader(new FileInputStream(platformXmlPath), "UTF-8");
            InputSource is = new InputSource(isr);
            document = builder.build(is);
        }catch (Exception e) {
            e.printStackTrace();
        }

        Scanner scanner = new Scanner(System.in);

        Element data = document.getRootElement();//根节点-->manifest
        Attribute platforPackageAttr = data.getAttribute("package");
        Attribute versionAttr = data.getAttribute("version");

        if(platforPackageAttr == null){
            System.out.println("厂商" + folderName + "包名为空,请输入包名");
            platforPackage = scanner.next();
            data.setAttribute("package",platforPackage);
            isChange = true;
        }else{
            platforPackage = platforPackageAttr.getValue();
        }

        if(versionAttr == null){
            System.out.println("厂商" + folderName + "版本号为空,请输入版本号,格式:(1.0.00)");
            version = scanner.next();
            data.setAttribute("version",version);
            isChange = true;
        }else{
            version = versionAttr.getValue();
            System.out.println();
            System.out.println("当前app版本的版本号:" + version + ",是否需要修改,y为修改,任意键不修改");
            String str = scanner.next();
            if("y".equalsIgnoreCase(str)){
                while (true) {
                    System.out.println("请输入新的版本号,格式:(1.0.00)");
                    String versionStr = scanner.next();
                    if(versionStr.matches("[0-9].[0].[0-9]{2}")){
                        version = versionStr;
                        data.setAttribute("version",version);
                        isChange = true;
                        break;
                    }else if("n".equalsIgnoreCase(versionStr)){
                        break;
                    }
                    System.out.println("格式有误,请重新输入,输入n退出");
                }
                isChange = true;
            }
        }

        if(isChange){
            try {
                System.out.println("readPlatformXml:" + platformXmlPath);

                doc2XML(document,platformXmlPath);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public static void changeAndroidManifest(){
        String AndroidManifestPath = iremotePath + "\\app\\src\\main\\AndroidManifest.xml";//AndroidManifest文件地址

        SAXBuilder builder = new SAXBuilder();
        Document document = null;

        try{
            InputStreamReader isr = new InputStreamReader(new FileInputStream(AndroidManifestPath), "UTF-8");
            InputSource is = new InputSource(isr);
            document = builder.build(is);
        }catch (Exception e) {
            e.printStackTrace();
        }
        Element manifestElem = document.getRootElement();//根节点-->manifest
        manifestElem.setAttribute("android:versionName",version);//APP版本
        manifestElem.setAttribute("android:versionCode",version.replace(".", ""));//APP版本

        Element permissionElem = manifestElem.getChild("permission");
        permissionElem.setAttribute("android:name",platforPackage + ".permission.JPUSH_MESSAGE");

        List<Element> usesPermissions = manifestElem.getChildren("uses-permission");//子节点集合
        for(Element elem : usesPermissions){
            String nameEle = elem.getAttribute("android:name").getValue();
            if(nameEle.indexOf(".permission.JPUSH_MESSAGE") > 0){
                elem.setAttribute("android:name",platforPackage + ".permission.JPUSH_MESSAGE");
            }
        }

        try {
            doc2XML(document,AndroidManifestPath);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**   
     * DOCUMENT输出XML   
     *    
     * @param doc JDOM
     * @param filePath 输出的地址
     * @throws Exception   
     */    
    public static void doc2XML(Document doc, String filePath) throws Exception{
        Format format = Format.getCompactFormat();      
        format.setEncoding("UTF-8"); //字符编码UTF-8     
        format.setIndent("     ");//setIndent是设置分隔附的意思,一般都是用空格,就是当你新节点后,自动换行并缩进,有层次感,如果这样写setIndent(""),就只有换行功能,而不会缩进了,如果写成setIndent(null),这样就即不换行也不缩进,全部以一行显示了,默认的就是这样的效果,不好看。        
        format.setLineSeparator("\n");
        format.setEncodingOut(true);
        format.setAttributeLineSeparator(true);
        format.setAttributeSeparatorStr("\n");
        XMLOutputter outputter = new XMLOutputter(format);  
        OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(filePath), "UTF-8");  
        outputter.output(doc, writer);  
        writer.close();  
    }   


    private static void changeBuild(){  
        String buildName = iremotePath + "\\app\\build.gradle";//build.gradle文件地址
        try {
            // read file content from file
            StringBuffer content= new StringBuffer("");
            InputStreamReader isr = new InputStreamReader(new FileInputStream(buildName), "UTF-8");
            BufferedReader br = new BufferedReader(isr);
            String str = null;

            while((str = br.readLine()) != null) {
                if (str.trim().startsWith("applicationId")) {  
                    content.append("        applicationId \"" + platforPackage + "\"");  
                }else{
                    content.append(str);  
                }
                content.append("\r\n");  
            }

            br.close();
            isr.close();

            Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(buildName), "UTF-8"));  
            writer.write(content.toString());  
            writer.close();
      }

      catch(FileNotFoundException e) {
                  e.printStackTrace();
            }
            catch(IOException e) {
                  e.printStackTrace();
            }
      }


    /**   
     *  复制整个文件夹内容   
     *  @param  oldPath  String  原文件路径  如:c:/fqf   
     *  @param  newPath  String  复制后路径  如:f:/fqf/ff   
     *  @return  boolean   
     */    
    public static void copyFolder(String  oldPath,  String  newPath)  {
        try  {    
           (new  File(newPath)).mkdirs();  //如果文件夹不存在  则建立新文件夹    
           File  a=new  File(oldPath);    
           String[]  file=a.list();    
           File  temp=null;    
           for  (int  i  =  0;  i  <  file.length;  i++)  {    
               if(oldPath.endsWith(File.separator)){    
                   temp=new  File(oldPath+file[i]);    
               }    
               else{    
                   temp=new  File(oldPath+File.separator+file[i]);    
               }    

               if(temp.isFile()){    
                   FileInputStream  input  =  new  FileInputStream(temp);    
                   FileOutputStream  output  =  new  FileOutputStream(newPath  +  "/"  +   
                           (temp.getName()).toString());    
                   byte[]  b  =  new  byte[1024  *  5];    
                   int  len;    
                   while  (  (len  =  input.read(b))  !=  -1)  {    
                       output.write(b,  0,  len);    
                   }    
                   output.flush();    
                   output.close();    
                   input.close();    
               }    
               if(temp.isDirectory()){//如果是子文件夹    
                   copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]);    
               }    
           }    
       }    
       catch  (Exception  e)  {    
           System.out.println("复制整个文件夹内容操作出错");    
           e.printStackTrace();    

       }    
   }
}

这里面对AndroidManifest.xml的处理我用的是jdom.jar的包。但该jar包写入”android:xxx“结构的节点内容无法修改只能新增。因此,我对jdom的源码进行修改。
jdom.jar源码及Demo地址在文章末尾。

特比说明下,gradle的环境要与项目环境一致,否则会打包apk失败。
jdom源码
http://download.csdn.net/download/feiniyan4944/10178610
Demo源码
↓↓↓↓
http://download.csdn.net/download/feiniyan4944/10178500

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值