背景
某些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这几个档位。
挑选市场上比较有代表性的机型进行参数分析:
结论
- 高端机型:CPU为骁龙845或麒麟980,RAM大于等于6GB
- 低端机型:骁龙或联发科系列,CPU最大主频小于等于1.8GHz且RAM小于4GB。麒麟系列,CPU最大主频小于等于2.1GHz且RAM小于等于4GB
- 其余为中端机型
项目地址
https://github.com/matthewYang92/Themis
写在最后
机型的判断标准是根据业务场景和时间动态改变的,本身并不存在绝对标准。本文更多的是提供一种思路以及笔者自己在公司项目上的初步实践经验总结,并不能代表适用于所有项目更不代表是唯一答案。另外若是较大的项目,建议大家Android端本身只做数据获取和上报,机型标准判断逻辑应该放在服务端完成,另外还可以通过抓取安兔兔等跑分网站的跑分数据进一步完善判断逻辑。