Android 有兩種類型的 API 不能通過 SDK 訪問驹碍。一種是在 com.android.internal 包中的 API忿晕,稱之為 internal API呐芥。另一種是被標記為 @hide
屬性的類和方法介牙,這是一組小級別的被隱藏的 API壮虫,稱之為 hidden API。
當使用 Android SDK 進行開發(fā)的時候耻瑟,應用默認引用了 android.jar旨指,它位于 SDKDir\platforms\android-X 目錄下(X 代表 API 級別),默認移除了所有的被@hide
標識的方法或者類以及 internal 包下的類喳整。谆构。當應用在設備上運行時,它會加載 framework.jar框都。簡單來說搬素,framework.jar 和 android.jar 等同,但是沒有移除 internal API 和 hidden API魏保。Hidden API 之所以被隱藏熬尺,是想阻止開發(fā)者使用 SDK 中未完成或不穩(wěn)定的部分。
舉個栗子:這是沒有移除 internal API 的 android.jar谓罗,可以看到包里的類是完整的粱哼。
比如 AssetManager 的 addAssetPath 方法被 @hide
標記,它屬于 hidden API檩咱,我們無法直接調(diào)用該方法揭措。
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
public final int addAssetPath(String path) {
synchronized (this) {
int res = addAssetPathNative(path);
makeStringBlocks(mStringBlocks);
return res;
}
}
但是,人的需求是無限的刻蚯。如果我們非要使用不可用的 API 怎么辦呢绊含?最簡單的就是 Java 的反射,反射 @hiden
的方法或類炊汹,修改訪問修飾符躬充,然后就可以搞事情了~~還有一種方法是從設備中提取,簡單說就是把設備上的 /system/framework/framework.jar 提取出來讨便,經(jīng)過一系列轉(zhuǎn)換充甚,最終得到完整的 android.jar,具體的步驟可以參考這篇文章:android怎樣調(diào)用@hide和internal API器钟。另外一種方式非常簡單津坑,GitHub 上有一個項目:android-hidden-api,里面提供了眾多版本完整的 android.jar 包傲霸,下圖所示疆瑰。
我們把工程 clone 下來眉反,找到對應平臺的 android.jar 包,替換掉 Android SDK 下面的 jar穆役,最好先備份一下原始的 jar寸五,重新編譯工程或者重啟 Studio 就行了。
比如 AssetManager 的 addAssetPath 方法耿币,沒有替換之前是這樣梳杏,Studio 提示錯誤,編譯失敗~o(>_<)o ~
但是在使用了完整的 android.jar 后淹接,發(fā)現(xiàn)竟然不會報錯了十性,代碼可以通過編譯,終于可以愉快地使用想要的方法了 O(∩_∩)O~
這種方式對于個人開發(fā)來說沒有問題塑悼,你把 android.jar 替換掉就好了劲适,但是如果面對團隊開發(fā),就非常痛苦了 %>_<%厢蒜,每個人都要替換 SDK 的 android.jar霞势,代價和風險可想而知。那么有沒有好的解決辦法斑鸦,既可以讓工程編譯通過愕贡,又能夠免去多人替換 jar 的成本呢?答案是有的巷屿。
Studio 默認引用的是 SDK 下面的 android.jar固以,那我們把它的引用改成完整的 jar 的路徑不就行了么?
我們把完整的 android.jar 放在工程 libs 目錄下嘱巾,也就是平時依賴 jar 的地方嘴纺,然后在工程 build.gradle 配置的 dependencies 里,以 provided 的方式引用 android.jar浓冒。因為每個工程模塊依賴 android.jar 的類型就是 provided,這樣不會把 android.jar 打包到應用中尖坤,運行環(huán)境中存在 framework.jar稳懒,應用直接就可以使用。
dependencies {
// compile fileTree(include: ['*.jar'], dir: 'libs') 這行一定要去掉慢味,當然為 android.jar 換個目錄也行
testCompile 'junit:junit:4.12'
provided files('libs/hidden_api_23.jar')
}
最后還要在工程根目錄的 build.gradle 里面配置當前 project场梆,加上下面的代碼就行了。
project('app') { // app是你工程的名字纯路,配置只對當前工程有效
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
// 注意修改 jar 包的路徑或油,替換 app/libs/hidden_api_23.jar,其他部分不要改
// Xbootclasspath/p:是 Java 編譯的尋址優(yōu)先設置驰唬,先找缺省路徑還是全路徑
options.compilerArgs.add('-Xbootclasspath/p:app/libs/hidden_api_23.jar')
}
}
}
現(xiàn)在重新編譯工程顶岸,雖然會在代碼中出現(xiàn)錯誤提示腔彰,但是編譯打包運行都是正常的。_
在開發(fā)中使用隱藏 API 和內(nèi)部 API 是不推薦的做法辖佣,但是為了實現(xiàn)一些「黑科技」霹抛,這些又是必須的~