安卓遊戲發行-控件註解框架-關鍵點1

做者

你們好,我叫祥子😊; java

本人15年畢業於廣東藥科大學,於2018年8月加入37手遊安卓團隊,曾經就任於網易擔任安卓開發工程師; git

目前是37手遊安卓團隊負責人,除平常團隊相關管理外,空閒喜歡專研安卓相關技術,由於始終堅信 「技術管理" 是必定要持續關注技術,保持對技術的熱情,這樣纔不會是空中樓閣...程序員

背景

(1)正常App開發中,在寫Activity或者Fragment時,沒法避免的會用到findViewById這類的代碼,而後強制類型轉換出咱們所須要的控件類型,說實話,對於追求代碼簡潔,高可讀,而且想偷懶的程序員來講,寫這樣的重複代碼,簡直就是災難;github

因此咱們會用到控件註解框架(如:butterknife),來解決上面的困擾,具體細節網上不少博客能夠查到(如:butterknife系列),這裏就不展開講。編程

(2)遊戲發行SDK開發中,咱們並不能愉快的直接使用網上的框架,爲何?接下來咱們來看看;markdown

上面圖是發行行業的常規流程,其中能夠看到:app

  • 遊戲研發商接完咱們的SDK,給到咱們的是一個apk(這裏咱們叫它母包);
  • 而後咱們發行方須要進行從新(二次)打包,也就是《反編譯母包-準備渠道和SDK材料-融合-回編譯》,從新輸出apk上架(這裏咱們叫它渠道包);
  • 最後是把渠道包上架,用戶下載使用

若是咱們遊戲發行商SDK中,用經過ID找控件(findViewById方式),走上面的流程以後,輸出的渠道包,會有找不到控件崩潰的異常,具體緣由以下:框架

  • 接入咱們發行商的SDK輸出母包,這個SDK中已經有findViewById(R.id.sqBtn),此次編譯假設ID的值爲0x7f070001,而且對應的資源類爲R1類ide

  • 從新(二次)打包-反編譯母包,此時根據resource.arsc文件產生public.xml文件,這個public.xml會固定住ID的值,也就是R.id.sqBtn的值始終爲0x7f070001(resource.arsc文件和public.xml文件不瞭解的同窗能夠看之前的博客oop

  • 從新(二次)打包-準備渠道和SDK材料,關於《渠道和SDK材料》這裏不展開,相信發行的同窗是知道的,這裏在生成《渠道和SDK材料》的時候,實際上是通過了編譯和從新生成了R類的,假設此次生成的R.id.sqBtn的值爲0x7f070002,而且對應的資源類爲R2類

  • 從新(二次)打包-融合/回編譯,融合過程當中,會把R2類覆蓋R1類

  • 從新(二次)打包-渠道包,因爲包之前的R1類變爲了R2類,R.id.sqBtn的值變爲了0x7f070002;而後app運行,到resource.arsc中查找資源的時候,由於resource.arsc中的值爲之前的0x7f070001(因public.xml固定做用),值不同致使程序異常。

行業廣泛的方案

由於不能使用findViewById去找控件,因此大部分狀況下,發行行業安卓找控件通常採起的是getIdentifier的方式;例如:getIdentifier(「sqBtn」, 」id」 ,pkgName),這樣作的弊端是:

  • 效率方面,程序員編碼的效率有提升空間(沒有編碼的自動提示等)

  • 隱患方面,編譯的時候不像id寫法那樣,會檢查資源是否存在,若是不當心單詞寫錯,而後又沒有測試出來,會出現毀滅性的崩潰,致使線上事故

要解決的2個關鍵點

(1)關鍵點一:既然系統的R.id在發行流程中不能用,只能用getIdentifier(「sqBtn」, 」id」 ,pkgName)的方式,那麼咱們就要看看怎麼利用資源名 + getIdentifier, 經過某種方式轉換爲相似ID那樣能夠提示編程,怎麼辦呢?

37手遊有這麼一句話 「方法總比困難多」,經過gradle的插件能力能夠製造出本身的SqR,這裏咱們叫它《自制資源SqR技術》

(2)關鍵點二:有了符合咱們發行領域的SqR資源了,那麼接下來就是相似 butterknife 那樣利用註解技術實現控件注入框架,這裏咱們叫它**《自定義註解技術》**

備註:因爲涉及的知識點比較多,因此會分開來說,本章主要講關鍵點1

關鍵點1-自制資源SqR技術(這是個Gradle插件)

使用效果

1)編譯時候校驗:

2)編碼時候提示:

使用簡介

1)新增長資源,如:圖片/字符串等(通常狀況是批量先弄好,再走到第2步)

2)生成SqR資源(除了這樣點擊以外,也能夠弄成AS的快捷鍵更加方便使用)

3)愉快的使用

實現流程

1)首先須要先構建一個gradle工程,因爲這個不是本章重點,故不在這裏展開;不瞭解的同窗能夠去查相關資料,這裏推薦一個淺顯易懂的例子,戳這裏>>>

2)而後是開始寫咱們的生成SqR代碼了,寫以前先看下咱們要構造的文件最終模樣:

寫的核心思想是經過對R.java(工程爲app模塊)文件或者R.txt(工程爲lib模塊)文件進行改造

  • 假如工程爲app模塊,則經過R.java文件改,關鍵點主要3個,具體看下圖:

關鍵代碼簡析:

//rPackage:得到包名,以便獲取路徑 
//例子:com.sq.mobile.sqinject_gradle_plugin 
def rPackage = this.getPackageName(variant)println(rPackage)rPackage = rPackage.replace(".", "/")


//得到資源任務(the Android Resources processing task)
def variantOutput = variant.outputs.first()
def processResources = variantOutput.processResourcesProvider.get()
def rFiles = project.files(processResources.textSymbolOutputFile).builtBy(processResources)

//得到R.txt路徑
//R.txt路徑:sqinjectgradleplugin/app/build/intermediates/symbols/debug/R.txt
def RFilePath = rFiles.singleFile.absolutePath


//得到輸出路徑
//sqinjectgradleplugin/app/build/generated/not_namespaced_r_class_sources/debug/r
def outputDir = processResources.getSourceOutputDir()

//將原先的R文件的int換成String,並將其值使用變量名賦值
//tempFile = sqinjectgradleplugin/app/build/generated/not_namespaced_r_class_sources/debug/r/com/sq/mobile/sqinject_gradle_plugin/R.javaFile 
tempFile = new File(outputDir.absolutePath + File.separator + rPackage + File.separator + "R.java");
println("R file path: " + tempFile.absolutePath)
rFileContent = tempFile.textPattern 
pattern = Pattern.compile("public static(.*?) int (.*?)=(.*?);")
Matcher matcher = pattern.matcher(rFileContent);
while (matcher.find()) {    
  String replace = "public static final String " + matcher.group(2) + " = \"" + matcher.group(2) + "\";";    
  rFileContent = rFileContent.replaceAll(matcher.group(), replace)
}
rFileContent = rFileContent.replaceAll("class R", "class SqR")
複製代碼
  • 假如工程爲lib模塊,則經過R.txt文件改,關鍵點在於把下圖進行規整,構建咱們要的文件格式

關鍵代碼簡析:

源碼

請戳這裏>>>

總結

文章主要介紹了控件注入框架的關鍵點1,利用插件方式實現getIdentifier的ID化;

下一篇將介紹關鍵點2,利用註解技術實現SqR的註解使用,最後實現相似 butterknife 框架

;這個框架目前在咱們內部已經普遍使用,它命名爲《SqInject》框架,目前申請專利中;

後續咱們除了會講解關鍵點2以外,會一併把框架開源出來,敬請期待>>>

歡迎交流

過程當中有問題或者須要交流的同窗,能夠掃描二維碼加好友,而後進羣進行問題和技術的交流等;

相關文章
相關標籤/搜索