一、背景

某些APP项目中需要针对高中低端安卓机型做不同的适配,例如:特效的开关、动画效果的调整等。怎么在项目中对Android进行高低端机型的区分?接下来的内容会进行分析。

二、区分标准

区分的标准最直观的就是跑分数据。参考现在最主流的跑分软件安兔兔,数据主要由4部分构成,内存(RAM)、CPU、GPU、IO(数据库、SD读写),其中内存、CPU、GPU性能构成主要占比,IO性能次要。内存和CPU是所有功能的根本,而GPU则是对游戏类应用影响更大些,因此在非游戏类的普通应用,更注重内存和CPU。

三、技术方案

我们看一下Android本身能提供哪些有用的数据给我们。先给出结论,CPU相关我们能获取到型号、核心数、最大主频;内存相关我们能获取到RAM值;GPU相关的暂时无法获取有关信息。

1.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 -> kHzif (freqBound > maxFreq) maxFreq = freqBound;} finally {stream.close();}}} catch (IOException e) {maxFreq = DEVICEINFO_UNKNOWN; //Fall through and return unknown.}return maxFreq;
}
  • 我们可以通过对比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。
2.内存相关

获取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;}
}
  • 内存的对比就很直观了,大内存优于小内存。从2018年市场上大部分的主流手机来分析,内存大致分为2G以下、3G、4G、6G以及8G这几个档位。
3.手机相关信息
//手机机型
public static String getModel() {return Build.MODEL;
}
// 厂商信息
public static String getBrand() {return Build.BRAND;
}

四、结论

  • 高端机型:CPU为骁龙845或麒麟980,RAM大于等于6GB
  • 低端机型:骁龙或联发科系列,CPU最大主频小于等于1.8GHz且RAM小于4GB。麒麟系列,CPU最大主频小于等于2.1GHz且RAM小于等于4GB
  • 其余为中端机型

五、代码实现


import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Build;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;public class Themis {public static final String TAG = "Themis";public static final int DEVICE_LEVEL_HIGH = 2;public static final int DEVICE_LEVEL_MID = 1;public static final int DEVICE_LEVEL_LOW = 0;public static final int DEVICE_LEVEL_UNKNOWN = -1;/*** The default return value of any method in this class when an* error occurs or when processing fails (Currently set to -1). Use this to check if* the information about the device in question was successfully obtained.*/public static final int DEVICEINFO_UNKNOWN = -1;private static final FileFilter CPU_FILTER = new FileFilter() {@Overridepublic boolean accept(File pathname) {String path = pathname.getName();//regex is slow, so checking char by char.if (path.startsWith("cpu")) {for (int i = 3; i < path.length(); i++) {if (!Character.isDigit(path.charAt(i))) {return false;}}return true;}return false;}};/*** Calculates the total RAM of the device through Android API or /proc/meminfo.** @param c - Context object for current running activity.* @return Total RAM that the device has, or DEVICEINFO_UNKNOWN = -1 in the event of an error.*/@TargetApi(Build.VERSION_CODES.JELLY_BEAN)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;}}/*** Method for reading the clock speed of a CPU core on the device. Will read from either* {@code /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq} or {@code /proc/cpuinfo}.** @return Clock speed of a core on the device, or -1 in the event of an error.*/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 -> kHzif (freqBound > maxFreq) maxFreq = freqBound;} finally {stream.close();}}} catch (IOException e) {maxFreq = DEVICEINFO_UNKNOWN; //Fall through and return unknown.}return maxFreq;}/*** Reads the number of CPU cores from the first available information from* {@code /sys/devices/system/cpu/possible}, {@code /sys/devices/system/cpu/present},* then {@code /sys/devices/system/cpu/}.** @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error.*/public static int getNumberOfCPUCores() {if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {// Gingerbread doesn't support giving a single application access to both cores, but a// handful of devices (Atrix 4G and Droid X2 for example) were released with a dual-core// chipset and Gingerbread; that can let an app in the background run without impacting// the foreground application. But for our purposes, it makes them single core.return 1;}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;}/*** Tries to read file contents from the file location to determine the number of cores on device.* @param fileLocation The location of the file with CPU information* @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error.*/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.}}}}/*** Converts from a CPU core information format to number of cores.* @param str The CPU core information string, in the format of "0-N"* @return The number of cores represented by this string*/private static int getCoresFromFileString(String str) {if (str == null || !str.matches("0-[\\d]+$")) {return DEVICEINFO_UNKNOWN;}return Integer.valueOf(str.substring(2)) + 1;}/*** Helper method for reading values from system files, using a minimised buffer.** @param textToMatch - Text in the system files to read for.* @param stream      - FileInputStream of the system file being read from.* @return A numerical value following textToMatch in specified the system file.* -1 in the event of a failure.*/private static int parseFileForValue(String textToMatch, FileInputStream stream) {byte[] buffer = new byte[1024];try {int length = stream.read(buffer);for (int i = 0; i < length; i++) {if (buffer[i] == '\n' || i == 0) {if (buffer[i] == '\n') i++;for (int j = i; j < length; j++) {int textIndex = j - i;//Text doesn't match query at some point.if (buffer[j] != textToMatch.charAt(textIndex)) {break;}//Text matches query here.if (textIndex == textToMatch.length() - 1) {return extractValue(buffer, j);}}}}} catch (IOException e) {//Ignore any exceptions and fall through to return unknown value.} catch (NumberFormatException e) {}return DEVICEINFO_UNKNOWN;}/*** Helper method used by {@link #parseFileForValue(String, FileInputStream) parseFileForValue}. Parses* the next available number after the match in the file being read and returns it as an integer.* @param index - The index in the buffer array to begin looking.* @return The next number on that line in the buffer, returned as an int. Returns* DEVICEINFO_UNKNOWN = -1 in the event that no more numbers exist on the same line.*/private static int extractValue(byte[] buffer, int index) {while (index < buffer.length && buffer[index] != '\n') {if (Character.isDigit(buffer[index])) {int start = index;index++;while (index < buffer.length && Character.isDigit(buffer[index])) {index++;}String str = new String(buffer, 0, start, index - start);return Integer.parseInt(str);}index++;}return DEVICEINFO_UNKNOWN;}/*** 获取当前剩余内存(ram)* @param context* @return*/public static long getAvailMemory(Context context) {ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();am.getMemoryInfo(mi);return mi.availMem;}/*** 获取厂商信息* @return*/public static String getBrand() {return Build.BRAND;}/*** 获取手机机型* @return*/public static String getModel() {return Build.MODEL;}/*** 获取硬件信息(cpu型号)* @return*/public static String getHardWare() {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;}/*** Level judgement based on current memory and CPU.* @param context - Context object.* @return*/public static int judgeDeviceLevel(Context context) {int level = DEVICE_LEVEL_UNKNOWN;int ramLevel = judgeMemory(context);int cpuLevel = judgeCPU();if (ramLevel == 0 || ramLevel == 1 || cpuLevel == 0) {level = DEVICE_LEVEL_LOW;} else if (ramLevel == 2 && (cpuLevel >= 1)) {level = DEVICE_LEVEL_MID;} else if (ramLevel > 2) {if (cpuLevel > 1) {level = DEVICE_LEVEL_HIGH;} else {level = DEVICE_LEVEL_MID;}}return level;}/*** 评定内存的等级.* @return*/private static int judgeMemory(Context context) {long ramMB = getTotalMemory(context) / (1024 * 1024);int level = -1;if (ramMB <= 2000) { //2G或以下的最低档level = 0;} else if (ramMB <= 3000) { //2-3Glevel = 1;} else if (ramMB <= 4000) { //4G档 2018主流中端机level = 2;} else if (ramMB <= 6000) { //6G档 高端机level = 3;} else { //6G以上 旗舰机配置level = 4;}return level;}/*** 评定CPU等级.(按频率和厂商型号综合判断)* @return*/private static int judgeCPU() {int level = -1;String cpuName = getHardWare();int freqMHz = getCPUMaxFreqKHz() / 1000;//if (cpuName.contains("qcom") || cpuName.contains("Qualcomm")) { //高通骁龙////} else if (cpuName.contains("kirin")) { //海思麒麟////}if (freqMHz <= 1600) { //1.5G 低端level = 0;} else if (freqMHz <= 2000) { //2GHz 低中端level = 1;} else if (freqMHz <= 2500) { //2.2 2.3g 中高端level = 2;} else { //高端level = 3;}return level;}}

Android区分高低端机型相关推荐

  1. Unity针对高低端机型的优化

    1.为什么要区分高低端机型 我们在制作游戏项目时,画质和流畅度都是非常重要的.市面上的游戏设备百花齐放,各大厂商推出不同型号.不同性能的设备来满足大众的需求. 一款游戏的画质和流畅度是决定游戏能否畅销 ...

  2. Unity 检测手机性能,区分高中低端机型

    项目中针对高中低端机型需做不同的适配,如低端机型不开启全屏泛光,降低阴影质量,关闭抗锯齿效果等等.首先我在项目中用到的测试机都安装上安兔兔并进行跑分,当然这个跑分不是给游戏用,而是我们先确定手机是什么 ...

  3. Unity 检测手机性能,区分高中低端机型(URP)

    有兴趣可查看之前写的标准渲染管线的版本 适配了URP渲染管线,并增加了抛弃第一次采样的数据(涉及Shader编译会影响采样数据) 可设置隔帧采样不影响游戏运行 一般在游戏启动进登录界面时可以开始,Ca ...

  4. msm android机型适配,小米系统MIUI10适配全机型了,高通机型通用适配,高通通刷包...

    原标题:小米系统MIUI10适配全机型了,高通机型通用适配,高通通刷包 今天给大家带来小米系统MIUI10,提供的是miui10稳定版高通通刷包,小米可以说是以系统开始发家的,系统流畅 功能 定制性在 ...

  5. 芯片短缺蔓延至手机市场:骁龙888短缺 三星中低端机型生产受阻

    近日,据国外媒体报道,自2020年下半年以来,芯片短缺问题就成为半导体行业的主旋律.如今,芯片短缺问题日益严重,包括汽车.手机.游戏机.PC在内的产业相继受到影响. 据悉,高通是HTC.索尼.诺基亚. ...

  6. 最新的三星android版本号,三星率先公布Android 12/13升级机型名单,可惜S9/Note9被抛弃...

    三星率先公布Android 12/13升级机型名单,可惜S9/Note9被抛弃 2020-08-18 17:07:15 5点赞 5收藏 25评论 前不久发布Galaxy Note20系列手机时,三星曾 ...

  7. 【转载】【原帖名】如何通过紧急刷机模式dump几乎任何高通机型的存储设备

    本帖是由最初发表在XDA-developers的帖子翻译,并加以说明. 近期在安卓高通救砖.找资料的时候看到这篇帖子.转过来给大家学习用. 作为通用教程,只要手机能够进入紧急刷写模式(EDL,或900 ...

  8. android+主流品牌手机,9月Android手机品牌及机型排行报告发布

    近日,国内最大的Android 手机内容获取与管理平台豌豆荚,发布了<9月Android 手机品牌及机型排行报告>.报告显示,三星在中国延续了其在全球市场的领先势头,但随着手机硬件和系统的 ...

  9. android8.0索尼z5,Z5 惨遭抛弃?索尼公布 Android 8.0 升级机型

    索尼在 IFA 上发布了预搭载 Android 8.0 奥利奥的 Xperia XZ1/XZ1 Compact 后,同时放出了可以升级 Android O 的具体机型名单,我们来看一下. Xperia ...

最新文章

  1. 安全 - MySQL 出现严重的密码安全漏洞,许多系统存在风险
  2. c++向量和数组的区别_数学学习如此容易:用Python计算特征值和特征向量
  3. 利用Windows API获得系统高级功能
  4. idea中没有j2ee_idea神器功能大全
  5. CSS样式之内容居中方法
  6. mysql 缓冲查询_MySQL查询高速缓冲
  7. python xmlrpc_Python xmlrpc用法
  8. 将参数传递给Bash函数
  9. Python中的break和continue的使用方法
  10. android 文件管理 显示缩略图,android将缩略图保存到缩略图文件夹_android_开发99编程知识库...
  11. Redis基础--使用treeNMS管理及监控Redis
  12. Spss 的基本方法使用步骤
  13. 3. Spring Boot 从零开始学习
  14. 黑马程序员-java学习第一天
  15. 模块七:mixer模块
  16. python报时功能_Python(PyS60)做的简单语音整点报时的实现 | 学步园
  17. 台式计算机锁屏快捷键,台式机锁屏快捷键是什么
  18. ssh: connect to host master port 22: No route to host
  19. Using RCU‘s CPU Stall Detector(待更新)
  20. R 计算变量之间的相关性

热门文章

  1. Python的turtle模块用法及实例 六:魔法阵七:樱花树 八:小猪佩奇九:多来爱梦
  2. 分布式部署LNMP+WordPress
  3. 2003 r2 远程管理html,在 System x 3850 M2上安装 Windows 2003 Server R2 x86 64
  4. Share-Nothing架构
  5. 【循环结构】for循环求和
  6. 测试nb信号的软件_NB-IoT网测仪
  7. 【渝粤题库】国家开放大学2021春1304管理案例分析题目
  8. 快速 fieldset 属性详解
  9. [SAP]JCO数据类型映射关系表
  10. 第13章 传智书城项目设计