一米酬、使用CrashHandler來(lái)獲取Crash信息
通過(guò)設(shè)置Thread. setDefaultUncaughtExceptionHandler;
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {}
二却妨、使用Multidex來(lái)解決方法數(shù)越界
compile 'com.android.support:multidex:1.0.2'
public class MyApplication extends MultiDexApplication { ... }
三躏惋、Android Apk編譯打包流程
編譯打包步驟:
1. 打包資源文件拄查,生成R.java文件
打包資源的工具是aapt(The Android Asset Packaing Tool(Android\sdk\build-tools\25.0.0\aapt.exe)菇怀。在這個(gè)過(guò)程中跟畅,項(xiàng)目中的AndroidManifest.xml文件和布局文件XML都會(huì)編譯咽筋,然后生成相應(yīng)的R.java,另外AndroidManifest.xml會(huì)被aapt編譯成二進(jìn)制徊件。
存放在APP的res目錄下的資源奸攻,該類資源在APP打包前大多會(huì)被編譯蒜危,變成二進(jìn)制文件,并會(huì)為每個(gè)該類文件賦予一個(gè)resource id睹耐。對(duì)于該類資源的訪問(wèn)舰褪,應(yīng)用層代碼則是通過(guò)resource id進(jìn)行訪問(wèn)的。Android應(yīng)用在編譯過(guò)程中aapt工具會(huì)對(duì)資源文件進(jìn)行編譯疏橄,并生成一個(gè)resource.arsc文件占拍,resource.arsc文件相當(dāng)于一個(gè)文件索引表,記錄了很多跟資源相關(guān)的信息捎迫。
2. 處理aidl文件晃酒,生成相應(yīng)的Java文件
這一過(guò)程中使用到的工具是aidl(Android Interface Definition Language),即Android接口描述語(yǔ)言(Android\sdk\build-tools\25.0.0\aidl.exe)窄绒。
aidl工具解析接口定義文件然后生成相應(yīng)的Java代碼接口供程序調(diào)用贝次。如果在項(xiàng)目沒(méi)有使用到aidl文件,則可以跳過(guò)這一步彰导。
3. 編譯項(xiàng)目源代碼蛔翅,生成class文件
項(xiàng)目中所有的Java代碼,包括R.java和.aidl文件位谋,都會(huì)變Java編譯器(javac)編譯成.class文件山析,生成的class文件位于工程中的bin/classes目錄下。
4. 轉(zhuǎn)換所有的class文件掏父,生成classes.dex文件
dx工具生成可供Android系統(tǒng)Dalvik虛擬機(jī)執(zhí)行的classes.dex文件笋轨,該工具位于(Android\sdk\build-tools\25.0.0\dx.bat)。
任何第三方的libraries和.class文件都會(huì)被轉(zhuǎn)換成.dex文件赊淑。dx工具的主要工作是將Java字節(jié)碼轉(zhuǎn)成成Dalvik字節(jié)碼爵政、壓縮常量池、消除冗余信息等陶缺。
5. 打包生成APK文件
所有沒(méi)有編譯的資源钾挟,如images、assets目錄下資源(該類文件是一些原始文件饱岸,APP打包時(shí)并不會(huì)對(duì)其進(jìn)行編譯掺出,而是直接打包到APP中,對(duì)于這一類資源文件的訪問(wèn)伶贰,應(yīng)用層代碼需要通過(guò)文件名對(duì)其進(jìn)行訪問(wèn))蛛砰;編譯過(guò)的資源和.dex文件都會(huì)被apkbuilder工具打包到最終的.apk文件中。
打包的工具apkbuilder位于 android-sdk/tools目錄下黍衙。apkbuilder為一個(gè)腳本文件泥畅,實(shí)際調(diào)用的是(E:\Documents\Android\sdk\tools\lib)文件中的com.android.sdklib.build.ApkbuilderMain類。
6. 對(duì)APK文件進(jìn)行簽名
一旦APK文件生成琅翻,它必須被簽名才能被安裝在設(shè)備上位仁。
在開(kāi)發(fā)過(guò)程中柑贞,主要用到的就是兩種簽名的keystore。一種是用于調(diào)試的debug.keystore聂抢,它主要用于調(diào)試钧嘶,在Eclipse或者Android Studio中直接run以后跑在手機(jī)上的就是使用的debug.keystore。
另一種就是用于發(fā)布正式版本的keystore琳疏。
7. 對(duì)簽名后的APK文件進(jìn)行對(duì)齊處理
如果你發(fā)布的apk是正式版的話有决,就必須對(duì)APK進(jìn)行對(duì)齊處理,用到的工具是zipalign(E:\Documents\Android\sdk\build-tools\25.0.0\zipalign.exe)
對(duì)齊的主要過(guò)程是將APK包中所有的資源文件距離文件起始偏移為4字節(jié)整數(shù)倍空盼,這樣通過(guò)內(nèi)存映射訪問(wèn)apk文件時(shí)的速度會(huì)更快书幕。對(duì)齊的作用就是減少運(yùn)行時(shí)內(nèi)存的使用。
四揽趾、Android Apk安裝過(guò)程
Apk安裝的主要步驟:
- 將apk文件復(fù)制到data/app目錄
- 解析apk信息
- Dalvik虛擬機(jī)執(zhí)行dexopt操作(優(yōu)化.dex文件成odex文件到data/dalvik-cache)台汇;如果是ART虛擬機(jī)執(zhí)行dex2oat操作(將.dex文件翻譯成.oat文件)
- 更新權(quán)限信息
- 完成安裝,發(fā)送Intent.ACTION_PACKAGE_ADDED廣播
五、Android的動(dòng)態(tài)加載技術(shù)
動(dòng)態(tài)加載技術(shù)也叫插件化技術(shù)篱瞎,通過(guò)插件化來(lái)減輕應(yīng)用的內(nèi)存和CPU占用苟呐。
主要解決三個(gè)基礎(chǔ)性問(wèn)題:
1、資源訪問(wèn)
ContextImpl中有兩個(gè)抽象方法:getAssets和getResources方法
2俐筋、Activity生命周期管理
將Activity生命周期方法提取出來(lái)作為一個(gè)接口牵素,通過(guò)代理Activity去調(diào)生命周期方法。
3校哎、ClassLoader的管理
同一個(gè)插件采用同一個(gè)ClassLoader加載類两波。
六、Android權(quán)限機(jī)制
Android將安全設(shè)計(jì)貫穿系統(tǒng)架構(gòu)的各個(gè)層面闷哆,覆蓋系統(tǒng)內(nèi)核、虛擬機(jī)单起、應(yīng)用程序框架層以及應(yīng)用層各個(gè)環(huán)節(jié)抱怔,力求在開(kāi)放的同時(shí),也最大程度地保護(hù)用戶的數(shù)據(jù)嘀倒、應(yīng)用程序和設(shè)備的安全屈留。Android安全模型主要提供以下幾種安全機(jī)制:
1、權(quán)限的本質(zhì)
在 Android 中测蘑,一個(gè)權(quán)限灌危,本質(zhì)上是一個(gè)字符串,一個(gè)可以表示執(zhí)行特定操作的能力的字符串碳胳。訪問(wèn) SD 卡的能力勇蝙,訪問(wèn)通訊錄的能力,啟動(dòng)或訪問(wèn)一個(gè)第三方應(yīng)用中的組件的能力挨约。
pm list permissions -f 命令可以詳細(xì)查看 Android 所有預(yù)定義的權(quán)限
權(quán)限的信息包括:定義的包名味混、標(biāo)簽产雹、描述和保護(hù)級(jí)別
+ permission:android.permission.DELETE_PACKAGES
package:android
label:null
description:null
protectionLevel:signature|privileged
2、權(quán)限的級(jí)別
normal 級(jí)別:
權(quán)限保護(hù)級(jí)別的默認(rèn)值翁锡,無(wú)須用戶確認(rèn)蔓挖,只要聲明了,就自動(dòng)默默授權(quán)馆衔。如:ACCESS_NETWORK_STATE瘟判。dangerous 級(jí)別:
賦予權(quán)限前,會(huì)彈出對(duì)話框角溃,顯式請(qǐng)求權(quán)限拷获。如:READ_SMS。因?yàn)?Android 需要在安裝時(shí)賦予權(quán)限开镣,所以安裝的確認(rèn)對(duì)話框刀诬,也會(huì)顯示列出權(quán)限清單。signature 級(jí)別:
signature 級(jí)別的權(quán)限是最嚴(yán)格的權(quán)限邪财,只會(huì)賦予與聲明權(quán)限使用相同證書(shū)的應(yīng)用程序陕壹。
以系統(tǒng)內(nèi)置 signature 級(jí)別權(quán)限為例,Android 系統(tǒng)應(yīng)用的簽名由平臺(tái)密鑰簽發(fā)树埠,默認(rèn)情況下源碼樹(shù)里有 4 個(gè)不同的密鑰文件:platform糠馆、shared、media 和 testkey怎憋。所有核心平臺(tái)的包(如:設(shè)置又碌、電話、藍(lán)牙)均使用 platform 密鑰簽發(fā)绊袋;搜索和通訊錄相關(guān)的包使用 shared 簽發(fā)毕匀;圖庫(kù)和媒體相關(guān)的包使用 media 密鑰簽發(fā);其他的應(yīng)用使用 testkey 簽發(fā)癌别。定義系統(tǒng)內(nèi)置權(quán)限的 framework-res.apk 文件是使用平臺(tái)密鑰簽發(fā)的皂岔,因此任何試圖請(qǐng)求 signature 級(jí)別內(nèi)置權(quán)限的應(yīng)用程序,需要使用與框架資源包相同的密鑰進(jìn)行簽名展姐。
- signatureOrSystem 級(jí)別:
可以看做是一種折中的級(jí)別躁垛,可被賦予與聲明權(quán)限具有相同簽名證書(shū)密鑰的應(yīng)用程序(同 signature 級(jí)別)或者系統(tǒng)鏡像的部分應(yīng)用,也就是說(shuō)這允許廠商無(wú)須共享簽名密鑰圾笨。Android 4.3 之前教馆,安裝在 system 分區(qū)下的應(yīng)用會(huì)被自動(dòng)賦予該保護(hù)級(jí)別的權(quán)限,而 Android 4.4 之后擂达,只允許安裝在 system/priv-app/ 目錄下的應(yīng)用才能被主動(dòng)賦予土铺。
3、權(quán)限的管理
在每個(gè)應(yīng)用安裝時(shí),權(quán)限就已經(jīng)賦予了舒憾,系統(tǒng)使用包管理服務(wù)來(lái)管理權(quán)限镀钓。打開(kāi)我們系統(tǒng)目錄下的 /data/system/packages.xml,可以看到文件包含了所有已定義的權(quán)限列表和所有 apk 的包信息镀迂,這可以看做是包管理服務(wù)維護(hù)的一個(gè)已安裝程序的核心數(shù)據(jù)庫(kù)丁溅,這個(gè)數(shù)據(jù)庫(kù),隨著每次應(yīng)用安裝探遵、升級(jí)或卸載而進(jìn)行更新窟赏。
注意:
6.0以下權(quán)限在/data/system/packages.xml
6.0以上權(quán)限在/data/system/users/0/runtime-permissions.xml
4、權(quán)限的賦予
我們知道箱季,Android 應(yīng)用安裝時(shí)涯穷,會(huì)被分配一個(gè)唯一的 UID,應(yīng)用啟動(dòng)時(shí)藏雏,包管理器會(huì)設(shè)置新建進(jìn)程的 UID 和 GID 為應(yīng)用程序的 UID拷况。如果應(yīng)用已經(jīng)被賦予了額外的權(quán)限,就把這些權(quán)限映射成一組 GID掘殴,作為補(bǔ)充 GID 分配給進(jìn)程赚瘦。低層就可以依賴于進(jìn)程的 UID、GID 和補(bǔ)充 GID 來(lái)決定是否賦予權(quán)限了奏寨。
內(nèi)置權(quán)限到 GID 的映射是定義在 /etc/permission/platform.xml 起意;
6.0以上動(dòng)態(tài)添加到以上位置
系統(tǒng)進(jìn)程的權(quán)限配置信息在
Android\system\core\include\private\android_filesystem_config.h
注意:
PID:表示應(yīng)用的進(jìn)程 ID,PPID 表示父進(jìn)程 ID病瞳;
UID:UID代表一個(gè)應(yīng)用揽咕;應(yīng)用安裝時(shí)分配一個(gè)唯一的;
5套菜、權(quán)限的檢查
1. 系統(tǒng)內(nèi)核層權(quán)限檢查
內(nèi)核代碼中current_has_network(void) 方法檢查了進(jìn)程的所在組亲善。如果不在 inet 組,則直接返回錯(cuò)誤逗柴。設(shè)置申請(qǐng)權(quán)限逗爹,經(jīng)過(guò)解析,逐步映射到內(nèi)核層的組 ID 和用戶 ID嚎于,最終才能通過(guò)內(nèi)核層的檢查。
2. 框架層權(quán)限檢查
Android 6.0 之前組件不能在運(yùn)行時(shí)改變權(quán)限挟冠,所以系統(tǒng)的權(quán)限檢查執(zhí)行過(guò)程是靜態(tài)的
- 動(dòng)態(tài)權(quán)限執(zhí)行:
通過(guò)IPC:Android 的核心系統(tǒng)服務(wù)統(tǒng)一會(huì)注冊(cè)到服務(wù)管理器于购,系統(tǒng)服務(wù)可以直接檢查調(diào)用者的 UID,通過(guò)限定 UID 來(lái)控制訪問(wèn)權(quán)限知染;不適合非固定UID的應(yīng)用肋僧,適合只允許以 root(UID:0) 或 system(UID:1000) 運(yùn)行的進(jìn)程訪問(wèn)的服務(wù)檢查。
- 靜態(tài)權(quán)限執(zhí)行
跨應(yīng)用組件交互
我們使用隱式 Intent 來(lái)表達(dá)意圖,搜索匹配的組件嫌吠,如果有多個(gè)止潘,彈出選擇框,目標(biāo)組件被選定后辫诅,會(huì)由 ActivityManagerService 執(zhí)行權(quán)限檢查凭戴,檢查目標(biāo)組件是否有相應(yīng)的權(quán)限要求,如果有炕矮,則把權(quán)限檢查的工作交給 PMS么夫,去檢查調(diào)用者有沒(méi)有被授權(quán)這些權(quán)限。
接下來(lái)的總體的流程和動(dòng)態(tài)執(zhí)行流程大致相同:Binder.getCallingUid()和Binder.getCallingPid()獲取調(diào)用者的 UID 和 PID肤视,然后利用 UID 映射包名档痪,再獲得相關(guān)權(quán)限集合。如果權(quán)限集合中含有所需權(quán)限即啟動(dòng)邢滑,否則拋出 SecurityException 異常腐螟。靜態(tài)權(quán)限執(zhí)行這里,我們可以詳細(xì)了解下困后,每種組件的權(quán)限檢查時(shí)機(jī)和具體順序是怎么樣的乐纸。
- 組件權(quán)限執(zhí)行
Activity
會(huì)在 startActivity() 和 startActivityForResult() 里解析到聲明權(quán)限的 Activity 時(shí),就執(zhí)行權(quán)限檢查操灿。
Service
startService()锯仪、stopService() 和 bindService(),這 3 個(gè)方法被調(diào)用時(shí)都會(huì)進(jìn)行權(quán)限檢查趾盐。
BroadCastReceiver
發(fā)送廣播除了常用的 sendBroadcast(Intent intent)庶喜,還有個(gè) sendBroadcast(Intent intent, String receiverPermission),該方法可以要求廣播接受者具備特定的權(quán)限救鲤,但是久窟,調(diào)用 sendBroadcast 是不會(huì)進(jìn)行權(quán)限檢查的,因?yàn)閺V播是異步的本缠,所以權(quán)限檢查會(huì)在 intent 傳遞到已注冊(cè)的廣播接受者時(shí)進(jìn)行斥扛,如果接收者不具備特定的權(quán)限,則不會(huì)接收到該廣播丹锹,也不會(huì)收到 SecurityException 異常稀颁。
反過(guò)來(lái),接收者可以要求廣播發(fā)送者必須具備的權(quán)限楣黍,所要求的權(quán)限在 manifest 文件中設(shè)置 <receiver> 標(biāo)簽的 permission 屬性匾灶,或者動(dòng)態(tài)注冊(cè)時(shí)指定 registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler),權(quán)限檢查也是在廣播傳遞時(shí)執(zhí)行租漂。
所以阶女,收發(fā)廣播可以分開(kāi)指定權(quán)限颊糜。值得一提的是,一些系統(tǒng)廣播被聲明為 protected秃踩,并且只能由系統(tǒng)進(jìn)程發(fā)送衬鱼,比如 PACKAGE_INSTALLED。只能由系統(tǒng)進(jìn)程發(fā)送憔杨,這個(gè)限制會(huì)在內(nèi)核層進(jìn)行檢查鸟赫,對(duì)調(diào)用者的 UID 進(jìn)行匹配,只能是 SYSTEM_UID芍秆、PHONE_UID惯疙、SHELL_UID、BLUETOOTH_UID 或 root妖啥。如果其他 UID 的進(jìn)程試圖發(fā)送系統(tǒng)廣播霉颠,則會(huì)收到 SecurityException 異常。
ContentProvider
ContentProvider 可以為讀寫(xiě)分別指定不同的權(quán)限荆虱,即:調(diào)用目標(biāo) provider蒿偎、query() 方法 和 insert()、update()怀读、delete() 都會(huì)進(jìn)行權(quán)限檢查诉位。
總結(jié):
Android 的權(quán)限的檢查會(huì)在各個(gè)層次上實(shí)施。
1菜枷、高層的組件苍糠,例如應(yīng)用和系統(tǒng)服務(wù),通過(guò)包管理器查詢應(yīng)用程序被賦予的權(quán)限啤誊,并決定是否準(zhǔn)予訪問(wèn)岳瞭。
2、低層的組件蚊锹,通常不訪問(wèn)包管理器瞳筏,比如本地守護(hù)進(jìn)程,依賴于進(jìn)程的 UID牡昆、GID 和補(bǔ)充 GID 來(lái)決定賦予姚炕。
3、訪問(wèn)系統(tǒng)資源時(shí)丢烘,如設(shè)備文件柱宦、UNIX 域套接字和網(wǎng)絡(luò)套接字,則由內(nèi)核根據(jù)所有者播瞳、目標(biāo)資源的訪問(wèn)權(quán)限和訪問(wèn)進(jìn)程的進(jìn)程屬性或者 packages.list 來(lái)進(jìn)行控制捷沸。
共享 UID
最后簡(jiǎn)單說(shuō)下共享 UID,填一下前面挖的坑狐史。雖說(shuō) Android 會(huì)為每一個(gè)應(yīng)用分配唯一的 UID痒给,但如果應(yīng)用使用相同的密鑰簽發(fā),就可以使用相同 UID 運(yùn)行骏全,也就是運(yùn)行在同一個(gè)進(jìn)程中苍柏。
這個(gè)特性被系統(tǒng)應(yīng)用和核心框架服務(wù)廣泛使用,比如:Google Play 和 Google 定位服務(wù)姜贡,請(qǐng)求同一進(jìn)程內(nèi)的 Google 登錄服務(wù)试吁,從而達(dá)到靜默自動(dòng)同步用戶數(shù)據(jù)的體驗(yàn)。
值得注意的是:Android 不支持將一個(gè)已安裝的應(yīng)用楼咳,從非共享 UID 切換到共享狀態(tài)熄捍,因?yàn)楦淖兞艘寻惭b應(yīng)用的 UID,會(huì)導(dǎo)致應(yīng)用失去對(duì)自己文件的訪問(wèn)權(quán)限(在一些早期 Android 版本中)母怜,所以如果使用共享 UID 必須從一開(kāi)始就設(shè)計(jì)好余耽。
參考:你真的了解Android權(quán)限機(jī)制嗎
七、Android劉海屏適配
1苹熏、AndroidP劉海屏的適配:
Android P 支持最新的全面屏以及為攝像頭和揚(yáng)聲器預(yù)留空間的凹口屏幕碟贾。通過(guò)全新的 DisplayCutout 類,可以確定非功能區(qū)域的位置和形狀轨域,這些區(qū)域不應(yīng)顯示內(nèi)容袱耽。要確定這些凹口屏幕區(qū)域是否存在及其位置,使用 getDisplayCutout() 函數(shù)干发。
- 設(shè)置LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式
- 設(shè)置沉浸式布局模式
- 計(jì)算狀態(tài)欄高度朱巨,進(jìn)行布局;如果有特殊UI要求枉长,則可以使用DisplayCutoutDemo類去獲取劉海屏的坐標(biāo)冀续,完成UIAndroid P 中 WindowManager.LayoutParams 新增了一個(gè)布局參數(shù)屬性layoutInDisplayCutoutMode:
DisplayCutout 類方法 | 說(shuō)明 |
---|---|
getBoundingRects() | 返回Rects的列表,每個(gè)Rects都是顯示屏上非功能區(qū)域的邊界矩形 |
getSafeInsetLeft () | 返回安全區(qū)域距離屏幕左邊的距離,單位是px |
getSafeInsetRight () | 返回安全區(qū)域距離屏幕右邊的距離,單位是px |
getSafeInsetTop () | 返回安全區(qū)域距離屏幕頂部的距離嗅绸,單位是px |
getSafeInsetBottom() | 返回安全區(qū)域距離屏幕底部的距離掌实,單位是px |
View decorView = mAc.getWindow().getDecorView();
if(decorView != null){
Log.d("hwj", "**controlView**" + android.os.Build.VERSION.SDK_INT);
Log.d("hwj", "**controlView**" + android.os.Build.VERSION_CODES.P);
WindowInsets windowInsets = decorView.getRootWindowInsets();
if(windowInsets != null){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
DisplayCutout displayCutout = windowInsets.getDisplayCutout();
//getBoundingRects返回List<Rect>,沒(méi)一個(gè)list表示一個(gè)不可顯示的區(qū)域,即劉海屏该面,可以遍歷這個(gè)list中的Rect,
//即可以獲得每一個(gè)劉海屏的坐標(biāo)位置,當(dāng)然你也可以用類似getSafeInsetBottom的api
Log.d("hwj", "**controlView**" + displayCutout.getBoundingRects());
Log.d("hwj", "**controlView**" + displayCutout.getSafeInsetBottom());
Log.d("hwj", "**controlView**" + displayCutout.getSafeInsetLeft());
Log.d("hwj", "**controlView**" + displayCutout.getSafeInsetRight());
Log.d("hwj", "**controlView**" + displayCutout.getSafeInsetTop());
}
}
}
Android P 中 WindowManager.LayoutParams 新增了一個(gè)布局參數(shù)屬性 layoutInDisplayCutoutMode:
模式 | 模式說(shuō)明 |
---|---|
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT | 只有當(dāng)DisplayCutout完全包含在系統(tǒng)欄中時(shí),才允許窗口延伸到DisplayCutout區(qū)域。 否則功炮,窗口布局不與DisplayCutout區(qū)域重疊。 |
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER | 該窗口決不允許與DisplayCutout區(qū)域重疊术唬。 |
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES | 該窗口始終允許延伸到屏幕短邊上的DisplayCutout區(qū)域薪伏。Android P 之前的劉海屏適配 |
2、AndroidP之前劉海屏的適配
不同廠商的劉海屏適配方案不盡相同粗仓,需分別查閱各自的開(kāi)發(fā)者文檔嫁怀。
3设捐、劉海屏判斷
/**
* 判斷是否是劉海屏
* @return
*/
public static boolean hasNotchScreen(Activity activity){
if (getInt("ro.miui.notch",activity) == 1 || hasNotchAtHuawei(activity) || hasNotchAtOPPO()
|| hasNotchAtVivo(activity) || isAndroidP(activity) != null){ //TODO 各種品牌
return true;
}
return false;
}
/**
* Android P 劉海屏判斷
* @param activity
* @return
*/
public static DisplayCutout isAndroidP(Activity activity){
View decorView = activity.getWindow().getDecorView();
if (decorView != null && android.os.Build.VERSION.SDK_INT >= 28){
WindowInsets windowInsets = decorView.getRootWindowInsets();
if (windowInsets != null)
return windowInsets.getDisplayCutout();
}
return null;
}
/**
* 小米劉海屏判斷.
* @return 0 if it is not notch ; return 1 means notch
* @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static int getInt(String key,Activity activity) {
int result = 0;
if (isXiaomi()){
try {
ClassLoader classLoader = activity.getClassLoader();
@SuppressWarnings("rawtypes")
Class SystemProperties = classLoader.loadClass("android.os.SystemProperties");
//參數(shù)類型
@SuppressWarnings("rawtypes")
Class[] paramTypes = new Class[2];
paramTypes[0] = String.class;
paramTypes[1] = int.class;
Method getInt = SystemProperties.getMethod("getInt", paramTypes);
//參數(shù)
Object[] params = new Object[2];
params[0] = new String(key);
params[1] = new Integer(0);
result = (Integer) getInt.invoke(SystemProperties, params);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 華為劉海屏判斷
* @return
*/
public static boolean hasNotchAtHuawei(Context context) {
boolean ret = false;
try {
ClassLoader classLoader = context.getClassLoader();
Class HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");
ret = (boolean) get.invoke(HwNotchSizeUtil);
} catch (ClassNotFoundException e) {
AppLog.e("hasNotchAtHuawei ClassNotFoundException");
} catch (NoSuchMethodException e) {
AppLog.e("hasNotchAtHuawei NoSuchMethodException");
} catch (Exception e) {
AppLog.e( "hasNotchAtHuawei Exception");
} finally {
return ret;
}
}
public static final int VIVO_NOTCH = 0x00000020;//是否有劉海
public static final int VIVO_FILLET = 0x00000008;//是否有圓角
/**
* VIVO劉海屏判斷
* @return
*/
public static boolean hasNotchAtVivo(Context context) {
boolean ret = false;
try {
ClassLoader classLoader = context.getClassLoader();
Class FtFeature = classLoader.loadClass("android.util.FtFeature");
Method method = FtFeature.getMethod("isFeatureSupport", int.class);
ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH);
} catch (ClassNotFoundException e) {
AppLog.e( "hasNotchAtVivo ClassNotFoundException");
} catch (NoSuchMethodException e) {
AppLog.e( "hasNotchAtVivo NoSuchMethodException");
} catch (Exception e) {
AppLog.e( "hasNotchAtVivo Exception");
} finally {
return ret;
}
}
/**
* OPPO劉海屏判斷
* @return
*/
public static boolean hasNotchAtOPPO(Context context) {
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}