Android相關(guān)硬件信息的獲取方式整理匯總
最近需要做個項目,里面涉及到一些收集Android硬件信息,雖說項目還沒開始,但也不影響自己先事先準(zhǔn)備起來.
檢測是手機還是平板
Android中沒有提供特定的方法來判斷設(shè)備是手機還是平板,只能通過別的方式來間接判斷,比如通過判斷屏幕尺寸
infoText.text = checkIsTablet()
private fun checkIsTablet(): String {
val metrics = resources.displayMetrics
val widthInches = metrics.widthPixels / metrics.xdpi
val heightInches = metrics.heightPixels / metrics.ydpi
val diagonalInches = sqrt(widthInches.pow(2.0f) + heightInches.pow(2.0f))
return if (diagonalInches >= 7.0) {
"手機還是平板:平板"
} else {
"手機還是平板:手機"
}
}
驗證一下,分別將這段代碼運行在一個手機模擬器上以及平板模擬器上,結(jié)果如下

判斷是否為折疊屏
其實在折疊屏沒出現(xiàn)的時候,判斷手機或者是平板使用上述方法還是夠用的,但是在折疊屏面前就顯得信心不足了,折疊屏一展開,那就是一個長著平板臉的手機,為了識別折疊屏,Android10出來了一個新的感應(yīng)器類型TYPE_HINGE_ANGLE,可以通過是否存在這種感應(yīng)器來識別折疊屏
private fun checkIsFoldScreen(): String {
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
val hingeAngleSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
return if (hingeAngleSensor == null) {
"是否折疊屏:否"
} else {
"是否折疊屏: 是"
}
}
裝了一個折疊屏的模擬器,拿來試一下看看能不能識別

如果說想要具體拿到折疊屏的狀態(tài),比如是全展開還是半展開,或者收起狀態(tài),就要使用Jetpack WindowManager這個庫了,分別可以通過以下api拿到不同的狀態(tài)

不過這個得用真機試了,模擬器上拿不到FoldingFeature,等我買了折疊屏再試下
屏幕密度與密度比例
這兩個值相信基本每個項目都會用到,屏幕密度一般用來判斷屏幕適配,加載不同的圖片資源,密度比例一般用來單位換算,這倆值都可以通過DisplayMetrics來獲得
infoText.text = checkScreenDpiAndDensity()
private fun checkScreenDpiAndDensity(): String {
val displayMetric = resources.displayMetrics
val dpi = displayMetric.densityDpi
val density = displayMetric.density
return "屏幕密度:${dpi} 密度比例:${density}"
}

屏幕像素
private fun checkScreenPixel(): String {
val displayMetrics = DisplayMetrics()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
display?.getRealMetrics(displayMetrics)
}else{
windowManager.defaultDisplay.getRealMetrics(displayMetrics);
}
val wPixel = displayMetrics.widthPixels
val hPixel = displayMetrics.heightPixels
return "像素(寬):${wPixel} 像素(高):${hPixel}"
}

物理尺寸
物理尺寸在安卓上單位是英寸,它表示一個屏幕對角線的長度,至于如何計算對角線,就要用到上學(xué)時候用到的勾股定理,x,y分別是屏幕的寬高,注意的是由于單位是英寸,所以也要把上面計算出來的像素轉(zhuǎn)換成英寸,具體代碼如下
private fun checkPhysicalSize(): String {
val displayMetrics = DisplayMetrics()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
display?.getRealMetrics(displayMetrics)
}else{
windowManager.defaultDisplay.getRealMetrics(displayMetrics);
}
val widthInches = displayMetrics.widthPixels / displayMetrics.xdpi
val heightInches = displayMetrics.heightPixels / displayMetrics.ydpi
val diagonalInches = sqrt(
widthInches.pow(2.0f) + heightInches.pow(2.0f)
)
return "物理尺寸 $diagonalInches"
}

刷新率
刷新率一般就是指Android屏幕上每秒更新畫面的頻率,單位是赫茲,正常來講,普通設(shè)備的刷新率都為60赫茲,獲取刷新率的代碼如下
private fun checkRefreshRate(): String {
val mDisplay = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
display
}else{
windowManager.defaultDisplay
}
return "刷新率 ${mDisplay?.refreshRate}"
}

廣色域
有的設(shè)備支持廣色域,有的設(shè)備僅僅支持標(biāo)準(zhǔn)色域,廣色域的意思是屏幕可以顯示比標(biāo)準(zhǔn)色域(sRGB)更加豐富的顏色范圍,判斷一個設(shè)備是否支持廣色域的方式如下
private fun checkColorGamut(): String {
val config: Configuration = resources.configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val isWideColorGamut: Boolean = config.isScreenWideColorGamut
val support = if(isWideColorGamut) "支持" else "不支持"
return "是否支持廣色域模式:${support}"
}
return "不支持廣色域"
}

獲取內(nèi)存(Runtime)
一般來講應(yīng)用想獲取內(nèi)存信息,用的最多的就是通過Runtime來獲取,可以通過它獲取應(yīng)用最大可用內(nèi)存,當(dāng)前分配的內(nèi)存,當(dāng)前空閑內(nèi)存,已使用內(nèi)存
infoText.text = checkMemoryRuntime()
private fun checkMemoryRuntime(): String {
val runtime = Runtime.getRuntime()
val maxMemory = runtime.maxMemory() // 應(yīng)用最大可用內(nèi)存
val totalMemory = runtime.totalMemory() // 當(dāng)前分配的內(nèi)存
val freeMemory = runtime.freeMemory() // 當(dāng)前空閑內(nèi)存
val usedMemory = totalMemory - freeMemory // 已使用內(nèi)存
return "最大可用內(nèi)存:${maxMemory} 當(dāng)前分配的內(nèi)存:${totalMemory} 當(dāng)前空閑內(nèi)存:${freeMemory} 已使用內(nèi)存${usedMemory}"
}

獲取內(nèi)存(MemoryInfo)
還有一種方式就是通過獲取MemoryInfo來拿到內(nèi)存信息,比如總內(nèi)存,當(dāng)前空閑內(nèi)存以及判斷內(nèi)存是否過低
infoText.text = checkMemoryMemoInfo()
private fun checkMemoryMemoInfo(): String {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
val totalMem = memoryInfo.totalMem
val availMem = memoryInfo.availMem
val lowMemory = memoryInfo.lowMemory
return "總內(nèi)存:${totalMem} 當(dāng)前空閑內(nèi)存:${availMem} 內(nèi)存是否過低${lowMemory}"
}

磁盤空間(外部存儲與內(nèi)部存儲)
可以通過StatFs來獲取外部存儲以及內(nèi)存存儲容量
//外部存儲
infoText.text = checkExternalStorageInfo()
private fun checkExternalStorageInfo(): String {
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
val path = Environment.getExternalStorageDirectory() // 外部存儲根目錄
val stat = StatFs(path.path)
val blockSize = stat.blockSizeLong
val totalBlocks = stat.blockCountLong
val availableBlocks = stat.availableBlocksLong
val totalSize = blockSize * totalBlocks
val availableSize = blockSize * availableBlocks
val usedSize = totalSize - availableSize
return "外部存儲總?cè)萘?${totalSize} 外部存儲可用容量:${availableSize} 外部存儲已用容量:${usedSize}"
}
return ""
}

//內(nèi)部存儲
infoText.text = checkInternalStorageInfo()
private fun checkInternalStorageInfo(): String {
val path = Environment.getDataDirectory() // 內(nèi)部存儲根目錄
val stat = StatFs(path.path)
val blockSize = stat.blockSizeLong // 每個block的大小
val totalBlocks = stat.blockCountLong // 總block數(shù)
val availableBlocks = stat.availableBlocksLong // 可用block數(shù)
val totalSize = blockSize * totalBlocks // 總?cè)萘?
val availableSize = blockSize * availableBlocks // 可用容量
val usedSize = totalSize - availableSize // 已用容量
return "內(nèi)部存儲總?cè)萘?${totalSize} 內(nèi)部存儲可用容量:${availableSize} 內(nèi)部存儲已用容量:${usedSize}"
}

CPU內(nèi)核數(shù)量
獲取CPU的內(nèi)核數(shù)量很簡單,Runtime類中有現(xiàn)成的方法
infoText.text = checkCPUcoreNumber()
private fun checkCPUcoreNumber() =
"cpu核心數(shù) : ${Runtime.getRuntime().availableProcessors()}"

CPU架構(gòu)
infoText.text = checkCPUArchitecture()
private fun checkCPUArchitecture() =
"cpu架構(gòu):${Build.SUPPORTED_ABIS[0]}"

CPU硬件信息
infoText.text = checkCPUHardware()
private fun checkCPUHardware() =
"硬件信息:${Build.HARDWARE}"

檢測設(shè)備是否root
同樣的沒有任何api可以直接去判斷設(shè)備是否有root權(quán)限,我們只能從以下幾個方式去判斷
- 判斷檢查是否存在相關(guān)root文件
var fileRooted = false
val paths = arrayOf(
"/system/app/Superuser.apk",
"/sbin/su",
"/system/bin/su",
"/system/xbin/su",
"/data/local/xbin/su",
"/data/local/bin/su",
"/system/sd/xbin/su",
"/system/bin/failsafe/su",
"/data/local/su",
"/su/bin/su"
)
for (path in paths) {
if (File(path).exists()) {
fileRooted = true
}
}
- 檢查是否存在su命令
var suCmdExest = false
var process: Process? = null
try {
process = Runtime.getRuntime().exec(arrayOf("which", "su"))
val reader = BufferedReader(InputStreamReader(process.inputStream))
suCmdExest = reader.readLine() != null
} catch (e: Exception) {
suCmdExest = false
} finally {
process?.destroy()
}
- 檢查
Build.TAGS里面是否存在test-keys
var testKeys = false
val buildTags = Build.TAGS
testKeys = buildTags != null && buildTags.contains("test-keys")
- 執(zhí)行su命令
var suCmdExecute = false
var suprocess: Process? = null
try {
suprocess = Runtime.getRuntime().exec("su")
val out = suprocess.outputStream
out.write("exit\n".toByteArray())
out.flush()
out.close()
suCmdExecute = suprocess.waitFor() == 0
} catch (e: java.lang.Exception) {
suCmdExecute = false
} finally {
process?.destroy()
}
- Magisk 文件是否存在
var giskFile = false
val magiskPaths = arrayOf(
"/sbin/.magisk",
"/sbin/magisk",
"/cache/.disable_magisk",
"/cache/magisk.log",
"/data/adb/magisk",
"/data/adb/modules",
"/data/magisk",
"/data/magisk.img"
)
for (path in magiskPaths) {
if (File(path).exists()) {
giskFile = true
}
}
保險起見,可以把上述幾個變量放在一起判斷設(shè)備是否有root權(quán)限
val gotRoot = fileRooted || suCmdExest || testKeys || suCmdExecute || giskFile
網(wǎng)絡(luò)情況
這個也是在應(yīng)用當(dāng)中經(jīng)常會用到的一個屬性,判斷設(shè)備是連接的是wifi,還是連接的是2,3,4,5G網(wǎng)絡(luò),首先通過獲取 NetworkCapabilities來判斷是否連接的是wifi還是移動網(wǎng)絡(luò)
private fun checkNetworkType(): String {
var net = ""
val cManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val capabilities: NetworkCapabilities? =
cManager.getNetworkCapabilities(cManager.activeNetwork)
capabilities?.let { cb ->
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
net = "WIFI"
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
net = getMobileNetworkType(cManager)
}
}
return "網(wǎng)絡(luò)情況:${net}"
}
當(dāng)判斷出非wifi網(wǎng)絡(luò)的時候,再通過getMobileNetworkType函數(shù)來得出具體的網(wǎng)絡(luò)類型
private fun getMobileNetworkType(cManager: ConnectivityManager): String {
val networkInfo = cManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
if (networkInfo != null) {
val networkType = networkInfo.subtype
return when (networkType) {
TelephonyManager.NETWORK_TYPE_GPRS, TelephonyManager.NETWORK_TYPE_EDGE -> "2G"
TelephonyManager.NETWORK_TYPE_UMTS, TelephonyManager.NETWORK_TYPE_HSPA -> "3G"
TelephonyManager.NETWORK_TYPE_LTE -> "4G"
TelephonyManager.NETWORK_TYPE_NR -> "5G"
else -> "UNKNOWN"
}
}
return "UNKNOWN"
}

總結(jié)
文章就到此為止,在拿這些信息的時候,發(fā)現(xiàn)有不少api都已經(jīng)廢棄了,所以還是要注意一下盡量別用廢棄的api去實現(xiàn)功能,防止以后出問題。
以上就是Android相關(guān)硬件信息的獲取方式整理匯總的詳細內(nèi)容,更多關(guān)于Android硬件信息獲取的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android如何在root設(shè)備上開啟ViewServer詳解
這篇文章主要給大家介紹了關(guān)于Android中如何在root設(shè)備上開啟ViewServer的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-12-12
Android Studio 1.2版安裝設(shè)置圖文教程
這篇文章主要介紹了Android Studio 1.2版安裝設(shè)置圖文教程,本文詳細講解了下載、安裝Android Studio 1.2教程,以及常用設(shè)置詳細圖文教程,需要的朋友可以參考下2015-05-05
Kotlin Service服務(wù)組件開發(fā)詳解
這幾天分析了一下的啟動過程,于是乎,今天寫一下Service使用; 給我的感覺是它并不復(fù)雜,千萬不要被一坨一坨的代碼嚇住了,雖然彎彎繞繞不少,重載函數(shù)一個接著一個,就向走迷宮一樣,但只要抓住主線閱讀,很快就能找到出口2022-12-12
Android 中SwipeRefreshLayout與ViewPager滑動事件沖突解決方法
這篇文章主要介紹了Android 中SwipeRefreshLayout與ViewPager滑動事件沖突解決方法的相關(guān)資料,需要的朋友可以參考下2017-04-04
android跑馬燈出現(xiàn)重復(fù)跳動以及不滾動問題的解決方法
這篇文章主要介紹了android跑馬燈出現(xiàn)重復(fù)跳動以及不滾動問題的解決方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09

