博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
操作XML以及签名实例
阅读量:4206 次
发布时间:2019-05-26

本文共 6385 字,大约阅读时间需要 21 分钟。

 前几天接到个需求,如何根据一个基础的Android App来生成100个或更多的App,要求App icon和App name都不一样(可能还会有配置文件)。这个有点类似于为App贴上自己的标签,但具体功能由别人提供,有点类似于OEM,下面来分析下如何实现
     仔细想一下其实这个就是apk的编译和反编译的应用,再加上个签名(不签名的话无法使用)。只不过是用代码实现罢了

准备工作

     1、配置好Java开发环境
     2、下载google提供的apk编译和反编译工具 (包含apktool.jar、apktool.bat、aapt.exe三个文件)
     3、下载google提供的签名工具(包含sign.bat、signapk.jar两个文件)

icon覆盖和strings文件修改

      我们都知道,在Android应用中应用的icon和应用的名称是在AndroidManifest.xml中指定的,应用名称的话有可能直接写死,但多数是这种情况
[html] 
  1. android:icon ="@drawable/ic_launcher"  
  2. ndroid:label ="@string/app_name"  
     我们只要覆盖drawable-*下对应名字的icon图片和修改values-*路径下strings.xml中对应名字的属性值就行了,为了简单起见在这里以drawable-hdpi和values-zh-rCN路径来介绍

AndroidManifest.xml解析

     通过上面的介绍,我们需要从 AndroidManifest.xml获取icon和label两个属性的值,下面是一个简单的解析类,该注意的地方都有注释
[java] 
  1. /** 
  2.  * @author Tibib 
  3.  * 
  4.  */  
  5. public class AndroidManifestParser {  
  6.      
  7.         public String NS = "http://schemas.android.com/apk/res/android" ;  
  8.   
  9.     public AppInfo parse(InputStream in) throws Exception {  
  10.         try {  
  11.               //使用pull解析库  
  12.              XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();  
  13.               NS = parser.getNamespace();  
  14.               //设置使用 namespaces特性  
  15.             parser.setFeature(XmlPullParser. FEATURE_PROCESS_NAMESPACES , true );  
  16.             parser.setInput(in, "UTF-8" );  
  17.             parser.nextTag();  
  18.             return readAppInfo(parser);  
  19.         } catch (Exception e){  
  20.              e.printStackTrace();  
  21.               throw e;  
  22.         } finally {  
  23.             in.close();  
  24.         }  
  25.     }  
  26.   
  27.      
  28.     private AppInfo readAppInfo(XmlPullParser parser) throws Exception{  
  29.        AppInfo appInfo = new AppInfo();  
  30.         while (parser.next() != XmlPullParser. END_TAG) {  
  31.             if (parser.getEventType() != XmlPullParser. START_TAG) {  
  32.                 continue ;  
  33.             }  
  34.             String name = parser.getName();  
  35.             // Starts by looking for the General tag  
  36.             if ("application" .equals(name)){  
  37.              String attrLabelValue = parser.getAttributeValue( NS, "label" );  
  38.              String attrIconValue = parser.getAttributeValue( NS, "icon" );  
  39.              appInfo.setAppName(attrLabelValue.split( "/" )[1]);  
  40.              appInfo.setIconName(attrIconValue.split( "/" )[1]);  
  41.             }  
  42.             else {  
  43.                 skip(parser);  
  44.             }  
  45.         }  
  46.         return appInfo;  
  47.        }  
  48.   
  49.         // Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e.,  
  50.     // if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it  
  51.     // finds the matching END_TAG (as indicated by the value of "depth" being 0).  
  52.     private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {  
  53.         if (parser.getEventType() != XmlPullParser. START_TAG) {  
  54.             throw new IllegalStateException();  
  55.         }  
  56.         int depth = 1;  
  57.         while (depth != 0) {  
  58.             switch (parser.next()) {  
  59.             case XmlPullParser. END_TAG:  
  60.                     depth--;  
  61.                     break ;  
  62.             case XmlPullParser. START_TAG:  
  63.                     depth++;  
  64.                     break ;  
  65.             }  
  66.         }  
  67.     }  
  68.   
  69. }  

修改strings.xml中name属性为app_name(具体名称看配置)的值

[java] 
  1. /** 
  2.  * @author Tibib 
  3.  * 
  4.  */  
  5. public class XmlModifyUtil {  
  6.         /** 
  7.         * 使用的是 jdom库 
  8.         */  
  9.         public static void modifyXML(File modifyXmlFile, String appNameAttrValue,  
  10.                     String appNameText) {  
  11.   
  12.              OutputStreamWriter bos = null ;  
  13.               try {  
  14.                     SAXBuilder builder = new SAXBuilder();  
  15.                      if (modifyXmlFile.exists()) {  
  16.                            Document document = (Document) builder.build(modifyXmlFile);  
  17.                            Element root = document.getRootElement();  
  18.                            List<Element> stringChildList = root.getChildren( "string");  
  19.                             for (Element element : stringChildList) {  
  20.                                  String nameAttrValue = element.getAttribute("name" )  
  21.                                                .getValue();  
  22.                                   if (nameAttrValue.equals(appNameAttrValue)) {  
  23.                                         element.setText(appNameText);  
  24.                                  }  
  25.                            }  
  26.   
  27.                            String xmlFileData = new XMLOutputter().outputString(document);  
  28.                             // strings.xml默认是UTF-8格式  
  29.                            bos = new OutputStreamWriter(  
  30.                                          new FileOutputStream(modifyXmlFile), "UTF-8" );  
  31.                            bos.write(xmlFileData);  
  32.                            bos.flush();  
  33.   
  34.                     } else {  
  35.                            System. out .println("File does not exist" );  
  36.                     }  
  37.              } catch (Exception ex) {  
  38.                     ex.printStackTrace();  
  39.              } finally {  
  40.                      if (bos != null ) {  
  41.                             try {  
  42.                                  bos.close();  
  43.                            } catch (IOException e) {  
  44.                                  e.printStackTrace();  
  45.                            }  
  46.                     }  
  47.              }  
  48.        }  
  49.   
  50. }  

执行编译和签名命令

我把反编译和签名工具都放在了同一目录,并且事先把基础apk反编译好,现在只需要用代码来执行编译和签名命令就行了。在Java中可以通过
Runtime类来执行DOS命令
[java] 
  1. private static void createApk(String apkName) throws IOException, InterruptedException {  
  2.      File dir = new File(wpPath );  
  3.       // 编译命令,其中azbz是基础apk反编译后的文件夹  
  4.      String backCommand = "cmd /c apktool.bat b azbz " +apkName+".apk" ;  
  5.       // 签名命令  
  6.      String signCommand = "cmd /c java -jar signapk.jar platform.x509.pem platform.pk8 "+apkName+ ".apk " +apkName+"_signed.apk" ;  
  7.   
  8.       // 这个命令执行完成会生成一个未签名的 apk  
  9.      Runtime backR = Runtime. getRuntime();  
  10.      Process backP = backR.exec(backCommand, null , dir);  
  11.       // 等待执行完再往下执行  
  12.      backP.waitFor();  
  13.   
  14.       // 签名 apk, 这里使用的google提供的证书  
  15.      Runtime signR = Runtime. getRuntime();  
  16.      Process signP = signR.exec(signCommand, null , dir);  
  17.      signP.waitFor();  
下面是随手写的一个生成两个icon和名称不同的Apk例子
[java] 
  1. public class ExecDosCommand {  
  2.          
  3.         static String wpPath_app = "E:" +File. separator+ "decode apk"+File. separator+ "azbz" +File.separator ;  
  4.         static String iconPath = wpPath_app +"res" +File. separator+ "drawable-hdpi"+File. separator ;  
  5.         static String stringPath = wpPath_app +"res" +File. separator+ "values-zh-rCN"+File. separator +"strings.xml" ;  
  6.         static String manifestPath = wpPath_app+ "AndroidManifest.xml";  
  7.          
  8.         static String wpPath = "E:" + File. separator + "decode apk"+File. separator;  
  9.          
  10.         public static void main(String[] args) throws Exception {  
  11.   
  12.   
  13.              AndroidManifestParser parser = new AndroidManifestParser();  
  14.              AppInfo appInfo = parser.parse( new FileInputStream( manifestPath));  
  15.                
  16.               for (int i = 0; i < 2; i++) {  
  17.                       
  18.                      coverIcon(appInfo, i);  
  19.                       
  20.                      modifyAppName(appInfo, i);  
  21.                       
  22.                      createApk( "修改"+(i+1));  
  23.              }  
  24.                
  25.        }  
  26.   
  27.         private static void modifyAppName(AppInfo appInfo, int i) {  
  28.              XmlModifyUtil. modifyXML( new File( stringPath ),  
  29.                            appInfo.getAppName(), "修改" +(i+1));  
  30.        }  
  31.   
  32.         private static void coverIcon(AppInfo appInfo, int i)  
  33.                      throws FileNotFoundException, IOException {  
  34.              BufferedOutputStream bos = new BufferedOutputStream(  
  35.                             new FileOutputStream(iconPath +appInfo.getIconName()+ ".png"));  
  36.              BufferedInputStream bis = new BufferedInputStream(  
  37.                             new FileInputStream(wpPath +File. separator+ "image"+File. separator +"icon" +(i+1)+".png" ));  
  38.                
  39.               byte [] buffer = new byte[1024];  
  40.               int temp = 0;  
  41.               while ((temp = bis.read(buffer)) != -1 ){  
  42.                     bos.write(buffer, 0, temp);  
  43.              }  
  44.              bos.flush();  
  45.              bos.close();  
  46.              bis.close();  
  47.        }  
  48.   
  49.         private static void createApk(String apkName) throws IOException, InterruptedException {  
  50.              File dir = new File(wpPath );  
  51.               // 编译命令  
  52.              String backCommand = "cmd /c apktool.bat b azbz " +apkName+".apk" ;  
  53.               // 签名命令  
  54.              String signCommand = "cmd /c java -jar signapk.jar platform.x509.pem platform.pk8 "+apkName+ ".apk " +apkName+"_signed.apk" ;  
  55.   
  56.               // 这个命令执行完成会生成一个未签名的 apk  
  57.               Runtime backR = Runtime .getRuntime();  
  58.              Process backP = backR.exec(backCommand, null , dir);  
  59.               // 等待执行完再往下执行  
  60.              backP.waitFor();  
  61.   
  62.               // 签名 apk, 这里使用的google提供的证书  
  63.               Runtime signR = Runtime .getRuntime();  
  64.              Process signP = signR.exec(signCommand, null , dir);  
  65.              signP.waitFor();  
  66.        }  
  67.   
  68. }  

转载地址:http://dhlli.baihongyu.com/

你可能感兴趣的文章
python与正则表达式
查看>>
安装.Net Framework 4.7.2时出现“不受信任提供程序信任的根证书中终止”的解决方法
查看>>
input type=“button“与input type=“submit“的区别
查看>>
解决Github代码下载慢问题!
查看>>
1.idea中Maven创建项目及2.对idea中生命周期的理解3.pom文件夹下groupId、artifactId含义
查看>>
LeetCode-栈|双指针-42. 接雨水
查看>>
stdin,stdout,stderr详解
查看>>
Linux文件和设备编程
查看>>
文件描述符
查看>>
终端驱动程序:几个简单例子
查看>>
登录linux密码验证很慢的解决办法
查看>>
fcntl函数总结
查看>>
HTML条件注释
查看>>
Putty远程服务器的SSH经验
查看>>
内核态与用户态
查看>>
使用mingw(fedora)移植virt-viewer
查看>>
趣链 BitXHub跨链平台 (4)跨链网关“初介绍”
查看>>
C++ 字符串string操作
查看>>
MySQL必知必会 -- 了解SQL和MySQL
查看>>
MySQL必知必会 -- 使用MySQL
查看>>