安卓ROM定製筆記(一) 使用安卓隱藏API配合安卓studio開發系統級APP
雖然在開發中使用隱藏API是不推薦的,但是為了一些需求,還是得做的。
獲取安卓架包
在sdk中這個包叫做android.jar,有兩種方式,
第一種方式 從github上獲取,android-hidden-api,下載對應安卓版本的android.jar檔案
第二種方式 從編譯完成的安卓原始碼中獲取,
在安卓原始碼目錄/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates獲取一個叫做classes.jar檔案,如圖所示:
新增架包至安卓studio
在安卓studio project中新建資料夾名為lib,名字無關緊要,將第一步獲取的android.jar(這個名字也可以隨便取)檔案複製到lib資料夾下。如圖所示:
編寫子專案的build.gradle
什麼是子專案?如圖紅框所示:
把guestuser稱為子專案,根專案為AndroidRomLearn。
在子專案的build.gradle檔案中新增framework.jar的依賴,如圖所示:
編寫根專案中的build.gradle
實現思路分為兩步,第一步載入jar檔案,在根專案的build.gradle檔案中新增以下程式碼:
project('guestuser') { gradle.projectsEvaluated { tasks.withType(JavaCompile) { options.compilerArgs.add('-Xbootclasspath/p:'lib/framework.jar) } } }
其中add函式的引數 Xbootclasspath/p:是Java編譯的定址優先設定,不確定路徑可以用絕對路徑,目錄結構和我之前展示的一樣無需使用絕對路徑,絕對路徑需對路徑符號“/”進行轉義,"\\"
第二步,降低安卓sdk 的優先順序。先看一個檔名為guestuser.iml,以子專案名稱命名的檔案,如圖所示:
其部分內容如下所示:
這些orderEntry標籤的從上到下順序決定了在碰到同包名同類名的情況下的架包的順序,預設情況下sdk是排在第二位的,現在要做的是將sdk標籤位置置後。
在根專案中的build.gradle檔案中新增一個方法,如下所示:
def pushDownSDK(iml) { def imlFile = file(iml) try { def parsedXml = (new XmlParser()).parse(imlFile) def jdkIndexOf = parsedXml.component[1].orderEntry.findIndexOf { it.'@type' == 'jdk' } if (jdkIndexOf <= 1) { def jdkNode =parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' } parsedXml.component[1].remove(jdkNode) new Node(parsedXml.component[1], 'orderEntry',['type': 'jdk', 'jdkName': 'Android API 26 Platform', 'jdkType': 'Android SDK']) def writer = new StringWriter() new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml) imlFile.text = writer.toString() println "Push File: $iml jdk priority ok" groovy.xml.XmlUtil.serialize(parsedXml,new FileOutputStream(imlFile))//簡書上有個blog因為少寫這個,搞了我好久 } } catch (Exception e) { // do nothing } }
在專案編譯完成後應用此方法:
project('guestuser') {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs.add('-Xbootclasspath/p:lib/framework.jar')
}
}
gradle.buildFinished {
pushDownSDK("guestuser/guestuser.iml");
}
}
完整的build.gradle檔案如下所示:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
def pushDownSDK(iml) {
def imlFile = file(iml)
try {
def parsedXml = (new XmlParser()).parse(imlFile)
def jdkIndexOf = parsedXml.component[1].orderEntry.findIndexOf { it.'@type' == 'jdk' }
if (jdkIndexOf <= 1) {
def jdkNode =parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
parsedXml.component[1].remove(jdkNode)
new Node(parsedXml.component[1], 'orderEntry',['type': 'jdk', 'jdkName': 'Android API 26 Platform', 'jdkType': 'Android SDK'])
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml)
imlFile.text = writer.toString()
println "Push File: $iml jdk priority ok"
groovy.xml.XmlUtil.serialize(parsedXml,new FileOutputStream(imlFile))
}
} catch (Exception e) {
// do nothing
}
}
allprojects {
repositories {
google()
jcenter()
}
}
project('guestuser') {
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
options.compilerArgs.add('-Xbootclasspath/p:lib/framework.jar')
}
}
gradle.buildFinished {
pushDownSDK("guestuser/guestuser.iml");
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
重新編譯專案就可以使用隱藏的api了
編寫lint.xml抑制錯誤
在使用AndroidMainfest.xml配置了系統許可權時,android studio會報錯,導致無法編譯,如下圖所示:
在開發系統應用時這是不行的,所以需要一個方法抑制這個報錯,lint.xml就是這個方法,在子專案中新增一個lint.xml檔案,內容如下:
<?xml version="1.0" encoding="utf-8"?>
<lint>
<issue id="ProtectedPermissions" severity="warning"/>
</lint>
其中ProtectedPermissions定義了系統級別的許可權,降為警告就行了