此文章合適遊戲發行行業的安卓開發相關人員查閱,固然其餘行業的也能夠的哈,只是可能有些行業內術語(如:cp)會干擾閱讀等,嘻嘻java
該文件是apktool在反編譯apk時,根據apk包中的resources.arsc文件生成。數組
publc.xml是aapt在打包資源時用來固定資源id的,若是資源在public.xml中有對應的id了,那麼打包資源時就用已經有的id緩存
在開發中咱們一般使用 Resources.getXXX(resid) 來獲取某個資源,傳入的是 ID 這個 ID 定義在 R.XXX 類裏面,R 類是編譯器自動生成的,打開看知道app
其實資源 ID 就是一個常量,對咱們引用了某個ID,編譯成 APK 以後,這個ID的值就固定了,優化
而 apktool 在從新打包 apk 的時候會對資源從新編譯 (編譯成 resources.arsc 你有zip打開 apk 看獲得spa
編譯資源的時候天然須要對全部資源ID進行從新編排(這是一個隨機過程)試想假設原包裏面 drawable/a.png id=0x7f020003 那麼從新編排資源ID後就多是 0x7f020004 又假設原包 0x7f020004 對應圖片 b.png,那你從新打包後的 apk 當顯示 a.png 的時候就會替換成 b.png,這還好不會出錯,假若生成的一個 id 原包不存在,那會致使程序崩潰的!code
因此 public.xml 的做用就是把對象資源 ID 寫死!cdn
共四個字節32位xml
PackageID: 一般系統資源PackageID是01,而咱們本身的資源PackageID是7f對象
TypeID: 好比attr爲01,string爲02。可是並不固定,並不必定attr就是01。可是在public.xml中,同類型的該字節必定是同樣的,不然回編譯會失敗。
R類這裏有個知識點library,模塊中生成的R類中的成員的值不是常量,不帶final。app模塊生成的R類的值是常量值。而常量值在java編譯時會被優化,最終代碼中輸出的就是常量值,而不是R.id.xxx這樣。而library的由於是變量,不會被優化,代碼中會保留R.id.xxx
從本質上講,其實並無啥關係。可是因爲在代碼中咱們會使用R.id去查找資源,這就關聯上了。若是都用getIdentifier的方式先獲取id,那把R類刪了也沒事。
public.xml打包後對應的就是resources.arsc中的值,而資源值生成Java類,這個類就是R類。也就是說平時使用R類,就是用裏面的索引值去到resources.arsc中找到對應資源位置,再去加載。
切包過程當中,R類屬於代碼,採用直接覆蓋的方式,可是因爲咱們生成的R類跟母包的R類其實值會是不一樣的。
而public.xml是用的cp(遊戲的)的,爲何用cp的?由於cp創建的是app工程,R類是常量值,若是咱們把母包中public.xml中已有的值給改了,萬一母包中用了,那就gg了
因爲R類在library中使用的時候是個變量,保留了R.id.xxx這種形式,解決方法就有了,糾正R類中的值跟public.xml對應,這樣就能繼續愉快的使用R.id.xxx了。
咱們的切包過程有幾個步驟:
private void init() {
List<Element> elements = mDocument.getRootElement().elements();
for (Element element : elements) {
String type = element.attribute(TYPE).getStringValue();
String name = element.attribute(NAME).getStringValue();
String id = element.attribute(ID).getStringValue();
Map<String, String> typeMap = mTypeMap.get(type);
if (typeMap == null) {
typeMap = new HashMap<>();
typeMap.put(name, id);
mTypeMap.put(type, typeMap);
} else {
typeMap.put(name, id);
}
}
}
複製代碼
合併策略:
a、channelPublic中有,而matrixPublic中沒有,增長到matrixPublic中 好比增長以下數據到matrixPublic中
<public type="attr" name="iconSrc" id="0x7f0200a8" />
複製代碼
若是該type在matrixPublic中已經存在:
首先要獲取到attr在matrixPublic中的PackageId+TypeId。 在一個public.xml文件中,同類型好比attr對應的PackageId+TypeId是不能變的,不然回編譯失敗。所以要添加數據時,數據的PackageId+TypeId須要糾正爲matrixPublic的值。
其次資源值,不能和已有的資源值重複,正常狀況下public.xml中的值是aapt生成的有序的,這裏能夠掃描matrixPublic中attr類型值的最大值,而後加一做爲新加的iconSrc的id值
若是該type在matrixPublic中不存在(假設母包中matrixPublic中不存在attr類型):
首先要獲取類型已經被佔用的有哪些,即獲取到matrixPublic中的TypeId,正常狀況也是有序的,獲取出最大的TypeId,加一做爲新Type的起始值。賦值給iconSrc的id值
b、channelPublic中有,而matrixPublic中也有的,不須要處理,保留matrixPublic中的值不變
掃描R類在PublicAndRHelper中
掃描覆蓋完R類的smali代碼中全部的R類,R$styleable類除外,由於styleable中保存的是一些數組的值,規則不一樣。
private void scannerRClass(String path) {
File smaliFilePath = new File(path);
for (File file : smaliFilePath.listFiles()) {
if (file.isDirectory()) {
scannerRClass(file.getAbsolutePath());
} else if(file.isFile()){
if (file.getName().equals("R.smali") || file.getName().startsWith("R$")) {
//此處過濾掉styleable文件
if (!file.getName().endsWith("R$styleable.smali")) {
mRClassFileList.add(file.getAbsolutePath());
}
}
}
}
}
複製代碼
針對每個R類調用糾正R類中方法,糾正R類值在RValueHelper類中 策略:匹配出要糾正的行,獲取到type,name。在public.xml中找出對應的值,糾正。
注意這裏的糾正不要用replace(oldValue,newValue)這種方式,要用替換行的方式,由於存在新值在R類中也存在後,後續替換出問題。好比a替換成b,b替換成c的狀況最終R類中的a和b都被替換成了c
其次是styleable的處理,當掃描到的R是attr類型的時候,判斷是否有styleable類型的存在,若是存在,則緩存下來attr中所作的糾正,用於糾正styleable。
static void handle(String RFilePath, PublicXmlBean publicXmlBean) {
File RFile = new File(RFilePath);
String RStyleFilePath = "";
Map<String, String> cacheMap = null;
if (RFile.getName().endsWith("R$attr.smali")) {
RStyleFilePath = RFilePath.replace("R$attr", "R$styleable");
File RStyleAbleFile = new File(RStyleFilePath);
//styleable存在,則把attr文件替換過的值緩存
if (RStyleAbleFile.exists()) {
cacheMap = new HashMap<>();
}
}
String rFileContent = FileUtil.read(RFilePath);
//找到RFile中是屬性的每一行
ArrayList<String> lines = FileUtil.readAllLines(RFilePath, ".field public static final");
String regex = ".field public static final (.*):(.*) = (.*)";
for (String line : lines) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String type = RFile.getName().replace("R$", "").replace(".smali", "");
String name = matcher.group(1);
String resetValue = publicXmlBean.getValue(type, name);
if (StringUtils.isEmpty(resetValue)) {
resetValue = publicXmlBean.addValue(type, matcher.group(1));
}
//替換到文件內容中
rFileContent = rFileContent.replace(line, ".field public static final " + name + ":" + matcher.group(2) + " = " + resetValue);
if (cacheMap != null) {
//換過的值緩存起來
cacheMap.put(matcher.group(3), resetValue);
}
}
}
FileUtil.write(RFilePath, rFileContent);
if (cacheMap != null) {
//糾正R$styleable的值
List<String> styleAbleLines = FileUtil.readAllLines(RStyleFilePath);
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(RStyleFilePath));
for (String styleAbleLine : styleAbleLines) {
for (String key : cacheMap.keySet()) {
if (styleAbleLine.contains(key)) {
styleAbleLine = styleAbleLine.replace(key, cacheMap.get(key));
}
}
bw.write(styleAbleLine);
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
bw = null;
}
}
}
}
}
複製代碼