Android開發之指令碼替換PackageName

Tsy遠發表於2019-02-25

本篇文章介紹瞭如何利用指令碼自動替換ApplicationId和PackageName

1 前言

平時如果想要替換包名一般是在AS中右鍵Rename進行操作。但是如果遇到一份程式碼希望匯出幾種不同的包名(不是ApplicationId),並且程式碼還會持續更新,這時候就需要使用指令碼在打包時自動匯出成不同包名的apk。

有的人會說直接可以在build.gradle裡面的productFlavors裡面設定,然後用placeHolder填充包名就行啦。 注意,這時候就要區分ApplicationId和PackageName了。

ApplicationId,是在手機中標識唯一應用的id。(看起來就是包名)

defaultConfig {
    applicationId "com.tsy.xxx"
    minSdkVersion 9
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"

    multiDexEnabled true    // Enabling multidex support.
}複製程式碼

PackageName,AndroidManifest裡面標明的package和每個java檔案裡面的package頭的包名。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tsy.xxx">複製程式碼
package com.tsy.xxx.xxx;

import xxx

public class MainActivity {
    ...
}複製程式碼

平時使用productFlavors+PlaceHolder匯出不同包只能匯出ApplicationId不同的包,的確可以匯出2個同時在手機安裝的包,但是其實package並沒有改變。當遇到以下情況時就會出現問題。

  1. 微信第三方接入時回撥要求在包名路徑下的.wxapi.WXCallBackxxx 固定寫死這個檔案才能回撥。就是說如果你修改了ApplicationId沒有修改PackageName是無法收到微信回撥的。
  2. 動態獲取資源的時候。經常會動態根據包名和資源名獲取資源,這時候由於PackageName沒有修改,也是會出錯。

2 怎麼修改PackageName

當我們瞭解了ApplicationId和PackageName的區別後,該怎麼修改PackageName呢。

正常情況下,修改PackageName可能是在以前的專案的基礎上開發新專案,這時候手動在AS裡面通過右鍵修改啪啪啪。即可搞定。

但是當出現以下需求呢,

  1. 希望一份程式碼可以匯出多個不同的包。
  2. 這份程式碼還可以持續迭代開發,並且每次釋出版本都希望匯出多個不同包。

這時候如果還使用手動修改包名明顯已經無法滿足。希望達到先用程式碼開發一個能執行的版本,每次釋出的時候用指令碼匯出多個不同包名的包。

3 匯出指令碼怎麼寫

好。前面介紹了那麼多前提,乾貨來了。使用指令碼釋出不同包名的包的步驟應該是以下的:

  1. 先從標準程式碼複製一份臨時程式碼出來
  2. 替換一些資原始檔或者string或者配置檔案。(需求是:不同的包希望圖示和app名不同)
  3. 使用gradle指令碼替換臨時程式碼裡的所有包名為目標包名
  4. clean & rebuild

3.1 複製臨時程式碼

這一步就是shell指令碼的執行復制,不再贅述。

3.2 替換資原始檔、配置資訊

這一步我使用php寫的,用shell指令碼呼叫執行php(如果只是替換資原始檔用shell指令碼複製就行),使用php寫主要考慮到修改string.xml中的某個值,比如修改app名。

修改string.xml部分程式碼如下:

$app_name = “指定的app_name”;

//讀取XML並解析
$string_doc = new DOMDocument();
$string_doc->load($temp_prj_dir . 'app/src/main/res/values/strings.xml');
$stringList = $string_doc->getElementsByTagName("string");

$len = $stringList->length;
for($i=0;$i<$len;$i++) {
    $item  = $stringList->item($i);//獲取列表中單條記錄
    if($item->getAttribute('name') == "app_name") {
        $item->nodeValue = $app_name;
        break;
    }
}
$string_doc->save($temp_prj_dir . 'app/src/main/res/values/strings.xml');
echo "修改app_name success\n";複製程式碼

在這一步裡可以對其他一些配置資訊進行自定義和替換。用來區別不同的包。

3.3 替換包名

替換包名用gradle指令碼寫的。

task replacePackageName {
    FileTree tree = fileTree(dir: 'src/main')
    tree.include '**/*.java'
    tree.include '**/*.xml'
    tree.each {File mfile ->
        fileReader(mfile.path, packageName)
    }
    fileReader("build.gradle", packageName)
}

def fileReader(path, target_package) {
    def readerString = "";
    def hasReplace = false

    file(path).withReader('UTF-8') { reader ->
        reader.eachLine {
            if (it.find("com.tsy.xxx")) {
                it = it.replace("com.tsy.xxx", target_package)
                hasReplace = true
            }
            readerString <<= it
            readerString << '\n'
        }

        if(hasReplace) {
            println(path + " has replace package.")
            file(path).withWriter('UTF-8') {
                within ->
                    within.append(readerString)
            }
        }
        return readerString
    }
}複製程式碼

可以看到,需要替換的檔案應該是所有的java檔案、xml檔案還有build.gradle檔案。其實就是讀取檔案樹,正則替換。php好的人直接php也可以替換成功。

3.4 clean & rebuild

替換成功後,用gradle命令執行下clean然後再匯出就OK拉。

4 結尾

最後給大家看下指令碼執行的最終效果。

copy, replace config and packageName
copy, replace config and packageName

clean and rebuild
clean and rebuild

export success
export success

結尾

更多文章關注我的公眾號

我的公眾號
我的公眾號

相關文章