Android中如何?OEM

來源:互聯網
上載者:User
     前幾天接到個需求,如何根據一個基礎的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中指定的,應用程式名稱的話有可能直接寫死,但多數是這種情況
             android:icon ="@drawable/ic_launcher"            android:label ="@string/app_name"

     我們只要覆蓋drawable-*下對應名字的icon圖片和修改values-*路徑下strings.xml中對應名字的屬性值就行了,為了簡單起見在這裡以drawable-hdpi和values-zh-rCN路徑來介紹

AndroidManifest.xml解析     通過上面的介紹,我們需要從 AndroidManifest.xml擷取icon和label兩個屬性的值,下面是一個簡單的解析類,該注意的地方都有注釋

/** * @author Tibib * */public class AndroidManifestParser {           public String NS = "http://schemas.android.com/apk/res/android" ;    public AppInfo parse(InputStream in) throws Exception {        try {              //使用pull解析庫             XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();              NS = parser.getNamespace();              //設定使用 namespaces特性            parser.setFeature(XmlPullParser. FEATURE_PROCESS_NAMESPACES , true );            parser.setInput(in, "UTF-8" );            parser.nextTag();            return readAppInfo(parser);        } catch (Exception e){             e.printStackTrace();              throw e;        } finally {            in.close();        }    }       private AppInfo readAppInfo(XmlPullParser parser) throws Exception{       AppInfo appInfo = new AppInfo();        while (parser.next() != XmlPullParser. END_TAG) {            if (parser.getEventType() != XmlPullParser. START_TAG) {                continue ;            }            String name = parser.getName();            // Starts by looking for the General tag            if ("application" .equals(name)){             String attrLabelValue = parser.getAttributeValue( NS, "label" );             String attrIconValue = parser.getAttributeValue( NS, "icon" );             appInfo.setAppName(attrLabelValue.split( "/" )[1]);             appInfo.setIconName(attrIconValue.split( "/" )[1]);            }            else {                skip(parser);            }        }        return appInfo;       }        // Skips tags the parser isn't interested in. Uses depth to handle nested tags. i.e.,    // if the next tag after a START_TAG isn't a matching END_TAG, it keeps going until it    // finds the matching END_TAG (as indicated by the value of "depth" being 0).    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {        if (parser.getEventType() != XmlPullParser. START_TAG) {            throw new IllegalStateException();        }        int depth = 1;        while (depth != 0) {            switch (parser.next()) {            case XmlPullParser. END_TAG:                    depth--;                    break ;            case XmlPullParser. START_TAG:                    depth++;                    break ;            }        }    }}

修改strings.xml中name屬性為app_name(具體名稱看配置)的值

/** * @author Tibib * */public class XmlModifyUtil {        /**        * 使用的是 jdom庫        */        public static void modifyXML(File modifyXmlFile, String appNameAttrValue,                    String appNameText) {             OutputStreamWriter bos = null ;              try {                    SAXBuilder builder = new SAXBuilder();                     if (modifyXmlFile.exists()) {                           Document document = (Document) builder.build(modifyXmlFile);                           Element root = document.getRootElement();                           List<Element> stringChildList = root.getChildren( "string");                            for (Element element : stringChildList) {                                 String nameAttrValue = element.getAttribute("name" )                                               .getValue();                                  if (nameAttrValue.equals(appNameAttrValue)) {                                        element.setText(appNameText);                                 }                           }                           String xmlFileData = new XMLOutputter().outputString(document);                            // strings.xml預設是UTF-8格式                           bos = new OutputStreamWriter(                                         new FileOutputStream(modifyXmlFile), "UTF-8" );                           bos.write(xmlFileData);                           bos.flush();                    } else {                           System. out .println("File does not exist" );                    }             } catch (Exception ex) {                    ex.printStackTrace();             } finally {                     if (bos != null ) {                            try {                                 bos.close();                           } catch (IOException e) {                                 e.printStackTrace();                           }                    }             }       }}

執行編譯和簽名命令
我把反編譯和簽名工具都放在了同一目錄,並且事先把基礎apk反編譯好,現在只需要用代碼來執行編譯和簽名命令就行了。在Java中可以通過Runtime類來執行DOS命令

        private static void createApk(String apkName) throws IOException, InterruptedException {             File dir = new File(wpPath );              // 編譯命令,其中azbz是基礎apk反編譯後的檔案夾             String backCommand = "cmd /c apktool.bat b azbz " +apkName+".apk" ;              // 簽名命令             String signCommand = "cmd /c java -jar signapk.jar platform.x509.pem platform.pk8 "+apkName+ ".apk " +apkName+"_signed.apk" ;              // 這個命令執行完成會產生一個未簽名的 apk             Runtime backR = Runtime. getRuntime();             Process backP = backR.exec(backCommand, null , dir);              // 等待執行完再往下執行             backP.waitFor();              // 簽名 apk, 這裡使用的google提供的認證             Runtime signR = Runtime. getRuntime();             Process signP = signR.exec(signCommand, null , dir);             signP.waitFor();       }

下面是隨手寫的一個產生兩個icon和名稱不同的Apk例子

public class ExecDosCommand {               static String wpPath_app = "E:" +File. separator+ "decode apk"+File. separator+ "azbz" +File.separator ;        static String iconPath = wpPath_app +"res" +File. separator+ "drawable-hdpi"+File. separator ;        static String stringPath = wpPath_app +"res" +File. separator+ "values-zh-rCN"+File. separator +"strings.xml" ;        static String manifestPath = wpPath_app+ "AndroidManifest.xml";               static String wpPath = "E:" + File. separator + "decode apk"+File. separator;               public static void main(String[] args) throws Exception {             AndroidManifestParser parser = new AndroidManifestParser();             AppInfo appInfo = parser.parse( new FileInputStream( manifestPath));                           for (int i = 0; i < 2; i++) {                                         coverIcon(appInfo, i);                                         modifyAppName(appInfo, i);                                         createApk( "修改"+(i+1));             }                    }        private static void modifyAppName(AppInfo appInfo, int i) {             XmlModifyUtil. modifyXML( new File( stringPath ),                           appInfo.getAppName(), "修改" +(i+1));       }        private static void coverIcon(AppInfo appInfo, int i)                     throws FileNotFoundException, IOException {             BufferedOutputStream bos = new BufferedOutputStream(                            new FileOutputStream(iconPath +appInfo.getIconName()+ ".png"));             BufferedInputStream bis = new BufferedInputStream(                            new FileInputStream(wpPath +File. separator+ "image"+File. separator +"icon" +(i+1)+".png" ));                           byte [] buffer = new byte[1024];              int temp = 0;              while ((temp = bis.read(buffer)) != -1 ){                    bos.write(buffer, 0, temp);             }             bos.flush();             bos.close();             bis.close();       }        private static void createApk(String apkName) throws IOException, InterruptedException {             File dir = new File(wpPath );              // 編譯命令             String backCommand = "cmd /c apktool.bat b azbz " +apkName+".apk" ;              // 簽名命令             String signCommand = "cmd /c java -jar signapk.jar platform.x509.pem platform.pk8 "+apkName+ ".apk " +apkName+"_signed.apk" ;              // 這個命令執行完成會產生一個未簽名的 apk              Runtime backR = Runtime .getRuntime();             Process backP = backR.exec(backCommand, null , dir);              // 等待執行完再往下執行             backP.waitFor();              // 簽名 apk, 這裡使用的google提供的認證              Runtime signR = Runtime .getRuntime();             Process signP = signR.exec(signCommand, null , dir);             signP.waitFor();       }}
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.