背景
某些APP項目中需要針對高中低端安卓機型做不同的適配,例如:特效的開關、動畫效果的調整等。怎么在項目中對Android進行高低端機型的區分?接下來的內容會進行分析。
區分標準
區分的標準最直觀的就是跑分數據。參考現在最主流的跑分軟件安兔兔,數據主要由4部分構成,內存(RAM)、CPU、GPU、IO(數據庫、SD讀寫),其中內存、CPU、GPU性能構成主要占比,IO性能次要。內存和CPU是所有功能的根本,而GPU則是對游戲類應用影響更大些,因此在非游戲類的普通應用,更注重內存和CPU。
技術方案
我們看一下Android本身能提供哪些有用的數據給我們。先給出結論,CPU相關我們能獲取到型號、核心數、最大主頻;內存相關我們能獲取到RAM值;GPU相關的暫時無法獲取有關信息。
CPU相關
獲取CPU型號
//獲取CPU型號
public static String getCPUName() {
try {
FileReader fr = new FileReader("/proc/cpuinfo");
BufferedReader br = new BufferedReader(fr);
String text;
String last = "";
while ((text = br.readLine()) != null) {
last = text;
}
//一般機型的cpu型號都會在cpuinfo文件的最后一行
if (last.contains("Hardware")) {
String[] hardWare = last.split(":\\s+", 2);
return hardWare[1];
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return Build.HARDWARE;
}
獲取CPU核心數及最大主頻
//從文件獲取核心數
private static int getCoresFromFileInfo(String fileLocation) {
InputStream is = null;
try {
is = new FileInputStream(fileLocation);
BufferedReader buf = new BufferedReader(new InputStreamReader(is));
String fileContents = buf.readLine();
buf.close();
return getCoresFromFileString(fileContents);
} catch (IOException e) {
return DEVICEINFO_UNKNOWN;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Do nothing.
}
}
}
}
//獲取CPU核心數
public static int getNumberOfCPUCores() {
int cores;
try {
cores = getCoresFromFileInfo("/sys/devices/system/cpu/possible");
if (cores == DEVICEINFO_UNKNOWN) {
cores = getCoresFromFileInfo("/sys/devices/system/cpu/present");
}
if (cores == DEVICEINFO_UNKNOWN) {
cores = new File("/sys/devices/system/cpu/").listFiles(CPU_FILTER).length;;
}
} catch (SecurityException e) {
cores = DEVICEINFO_UNKNOWN;
} catch (NullPointerException e) {
cores = DEVICEINFO_UNKNOWN;
}
return cores;
}
//獲取CPU最大主頻
public static int getCPUMaxFreqKHz() {
int maxFreq = DEVICEINFO_UNKNOWN;
try {
for (int i = 0; i < getNumberOfCPUCores(); i++) {
String filename =
"/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq";
File cpuInfoMaxFreqFile = new File(filename);
if (cpuInfoMaxFreqFile.exists() && cpuInfoMaxFreqFile.canRead()) {
byte[] buffer = new byte[128];
FileInputStream stream = new FileInputStream(cpuInfoMaxFreqFile);
try {
stream.read(buffer);
int endIndex = 0;
//Trim the first number out of the byte buffer.
while (Character.isDigit(buffer[endIndex]) && endIndex < buffer.length) {
endIndex++;
}
String str = new String(buffer, 0, endIndex);
Integer freqBound = Integer.parseInt(str);
if (freqBound > maxFreq) {
maxFreq = freqBound;
}
} catch (NumberFormatException e) {
//Fall through and use /proc/cpuinfo.
} finally {
stream.close();
}
}
}
if (maxFreq == DEVICEINFO_UNKNOWN) {
FileInputStream stream = new FileInputStream("/proc/cpuinfo");
try {
int freqBound = parseFileForValue("cpu MHz", stream);
freqBound *= 1000; //MHz -> kHz
if (freqBound > maxFreq) maxFreq = freqBound;
} finally {
stream.close();
}
}
} catch (IOException e) {
maxFreq = DEVICEINFO_UNKNOWN; //Fall through and return unknown.
}
return maxFreq;
}
內存相關
獲取RAM容量
//獲取RAM容量
public static long getTotalMemory(Context c) {
// memInfo.totalMem not supported in pre-Jelly Bean APIs.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
ActivityManager am = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
am.getMemoryInfo(memInfo);
if (memInfo != null) {
return memInfo.totalMem;
} else {
return DEVICEINFO_UNKNOWN;
}
} else {
long totalMem = DEVICEINFO_UNKNOWN;
try {
FileInputStream stream = new FileInputStream("/proc/meminfo");
try {
totalMem = parseFileForValue("MemTotal", stream);
totalMem *= 1024;
} finally {
stream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return totalMem;
}
}
手機相關信息
//手機機型
public static String getModel() {
return Build.MODEL;
}
// 廠商信息
public static String getBrand() {
return Build.BRAND;
}
關于CPU
- 我們可以通過對比CPU的核心數和最大主頻來判斷CPU的優劣,在同系列的CPU間這樣判斷是相對可靠的,但在不同系列之間單純以此為依據就不可靠了。(由于還涉及兼容性以及其他技術因素影響,不同系列的CPU之間就算以上兩個參數相近的情況下,表現出來的性能也可能差別很大)
- 直接通過CPU型號判斷,截止2019年1月市面上大多安卓機型上的CPU可以分為這幾個系列:高通驍龍、華為海思麒麟、聯發科MTK、三星獵戶座(主要面對歐美市場,中國市場的三星主要是高通,可暫時忽略)。聯發科MTK主打中低端市場,高端處理器對標高通、麒麟、三星有明顯差距,因此重點關注的是高通驍龍和海思麒麟這兩個系列。高通主要分為200、400、600、700和800系列(不同系列適配不同機型,不代表800系列性能都比600系列好),目前最頂級是高通驍龍845。麒麟主要分為910、920、925、950、980系列,目前最頂級的是麒麟980。2018年的旗艦手機,基本都搭載了這兩款CPU。
關于內存
- 內存的對比就很直觀了,大內存優于小內存。從2018年市場上大部分的主流手機來分析,內存大致分為2G以下、3G、4G、6G以及8G這幾個檔位。
挑選市場上比較有代表性的機型進行參數分析:
image.png
結論
- 高端機型:CPU為驍龍845或麒麟980,RAM大于等于6GB
- 低端機型:驍龍或聯發科系列,CPU最大主頻小于等于1.8GHz且RAM小于4GB。麒麟系列,CPU最大主頻小于等于2.1GHz且RAM小于等于4GB
- 其余為中端機型
項目地址
https://github.com/matthewYang92/Themis
寫在最后
機型的判斷標準是根據業務場景和時間動態改變的,本身并不存在絕對標準。本文更多的是提供一種思路以及筆者自己在公司項目上的初步實踐經驗總結,并不能代表適用于所有項目更不代表是唯一答案。另外若是較大的項目,建議大家Android端本身只做數據獲取和上報,機型標準判斷邏輯應該放在服務端完成,另外還可以通過抓取安兔兔等跑分網站的跑分數據進一步完善判斷邏輯。