如何用代码区分Android高低端机型?

背景

某些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端本身只做数据获取和上报,机型标准判断逻辑应该放在服务端完成,另外还可以通过抓取安兔兔等跑分网站的跑分数据进一步完善判断逻辑。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容