Android 判斷當(dāng)前網(wǎng)絡(luò)類型是否為5G

2021.1.16更新
????適配targetSdkVersion P(28)及更高的版本

項(xiàng)目中若存在判斷當(dāng)前網(wǎng)絡(luò)類型捻勉,一般都會(huì)用到如下方法:

    /**
     * get the network type
     *
     * @param ctx Context
     * @return networktype
     */
    public static int getNetWorkType(Context ctx) {
        int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
        TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
        try {
            int defaultDataSubId = getSubId();
            if (defaultDataSubId == -1) {
                networkType = tm.getNetworkType();
            } else {
                try {
                    Method dataNetworkType = TelephonyManager.class
                            .getDeclaredMethod("getDataNetworkType", new Class[]{int.class});
                    dataNetworkType.setAccessible(true);
                    networkType = (int) dataNetworkType.invoke(tm, defaultDataSubId);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
                if (networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
                    networkType = tm.getNetworkType();
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return networkType;
    }

    /**
     * get data sub id
     *
     * @return subId
     */
    private static int getSubId() {
        int defaultDataSubId = -1;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
            defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
        }
        return defaultDataSubId;
    }

至于networkType的取值,可參考TelephonyManager類奔脐。

使用上述方法撵儿,在5G網(wǎng)絡(luò)之前都沒問題镣典,到5G網(wǎng)絡(luò)時(shí)膳叨,查看Android Q源碼婶溯,發(fā)現(xiàn)TelephonyManager類里增加了這么一個(gè)類型常量:

    /** Current network is NR(New Radio) 5G. */
    public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.

意味著在5G網(wǎng)絡(luò)下上述兩個(gè)方法返回的networkType應(yīng)該是20间景,實(shí)際測(cè)試了下,然并卵艺智,返回的是13(NETWORK_TYPE_LTE倘要,表示4G)。
是Android系統(tǒng)出問題了?還是廠商定制ROM出問題了封拧?
查看運(yùn)營(yíng)商5G網(wǎng)絡(luò)的搭建志鹃,發(fā)現(xiàn)現(xiàn)在5G網(wǎng)絡(luò)基本都是通過4G增強(qiáng)型基站發(fā)射,也就是所謂的非獨(dú)立組網(wǎng)(NSA)泽西,所以系統(tǒng)獲取到的是NETWORK_TYPE_LTE曹铃,是沒錯(cuò)的。只有獨(dú)立5G組網(wǎng)(SA)捧杉,系統(tǒng)才會(huì)返回NETWORK_TYPE_NR陕见。
可廠商的手機(jī)明明就可以顯示5G網(wǎng)絡(luò)標(biāo)識(shí),不一致了味抖,怎么搞评甜?

網(wǎng)傳華為提供了自己的接口判斷是否為5G網(wǎng)絡(luò),親試確實(shí)可正常使用仔涩,如下代碼:

    /**
     * get huawei network type
     *
     * @return networkType
     */
    public static int getHwNetworkType(Context ctx) {
        int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
        if (VERSION.SDK_INT >= VERSION_CODES.O
                && ctx.checkSelfPermission(permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
            try {
                TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
                ServiceState ss;
                int subId = SubscriptionManager.getDefaultDataSubscriptionId();
                if (subId < 0) {
                    ss = tm.getServiceState();
                } else {
                    Class<TelephonyManager> classTm = TelephonyManager.class;
                    Method method = classTm
                            .getDeclaredMethod("getServiceStateForSubscriber", new Class[]{int.class});
                    method.setAccessible(true);
                    Object objSs = method.invoke(tm, subId);
                    ss = (ServiceState) objSs;
                }

                Method hwGetNetworkMethod = ServiceState.class.getMethod("getHwNetworkType");
                hwGetNetworkMethod.setAccessible(true);
                Integer hwNetType = (Integer) hwGetNetworkMethod.invoke(ss);
                if (hwNetType != null) {
                    networkType = hwNetType;
                }
            } catch (Exception e) {
                QLog.e(tag, QLog.USR, "getHwNetworkType throw ex", e);
            }
        }
        return networkType;
    }

華為的搞定了忍坷,其它廠商呢?找了半天沒找到類似接口熔脂,有點(diǎn)小無奈...

仔細(xì)看華為提供的接口佩研,里面用到了ServiceState,在Android Q上此類有g(shù)etNrState方法霞揉,"nr"跟NETWORK_TYPE_NR貌似有點(diǎn)兒像旬薯,也許這個(gè)方法正確判斷5G,但是由于系統(tǒng)限制零聚,此方法無法反射調(diào)用袍暴。但里面的nrState在toString()方法里面出現(xiàn)了,是不是可以搞事情隶症?于是有了下面通用的方法:

/**
 * network util
 */
public class NetworkUtil {

    public static final int NETWORK_TYPE_NR = 20;

    public static final int SDK_VERSION_Q = 29;

    /**
     * get the network type
     *
     * @param ctx Context
     * @return networkType
     */
    public static int getNetWorkType(Context ctx) {
        int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
        TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
        try {
            int defaultDataSubId = getSubId();
            if (defaultDataSubId == -1) {
                networkType = tm.getNetworkType();
            } else {
                Object obj = ReflectionUtil.invokeMethod(tm,
                        "android.telephony.TelephonyManager", "getDataNetworkType",
                        new Class[]{int.class}, defaultDataSubId);
                if (obj instanceof Integer) {
                    networkType = (Integer) obj;
                }
                if (networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN) {
                    networkType = tm.getNetworkType();
                }
            }
        } catch (Throwable t) {
            t.printStackTrace();
        }
        if (networkType == TelephonyManager.NETWORK_TYPE_LTE) {
            networkType = adjustNetworkType(ctx, networkType);
        }
        return networkType;
    }

    /**
     * get the 5G network type
     *
     * @param ctx Context
     * @param networkTypeFromSys this method can be call only when networkTypeFromSys = 13(LET)
     * @return correct network type
     */
    private static int adjustNetworkType(Context ctx, int networkTypeFromSys) {
        int networkType = networkTypeFromSys;
        if (VERSION.SDK_INT >= SDK_VERSION_Q
                && ctx.checkSelfPermission(permission.READ_PHONE_STATE)
                == PackageManager.PERMISSION_GRANTED) {
            try {
                TelephonyManager tm = (TelephonyManager) ctx
                        .getSystemService(Context.TELEPHONY_SERVICE);
                ServiceState ss = null;
                int defaultDataSubId = getSubId();
                if (defaultDataSubId == -1) {
                    ss = tm.getServiceState();
                } else {
                    Object obj = ReflectionUtil
                            .invokeMethod(tm, "android.telephony.TelephonyManager",
                                    "getServiceStateForSubscriber", new Class[]{int.class},
                                    defaultDataSubId);
                    if (obj instanceof ServiceState) {
                        ss = (ServiceState) obj;
                    }

                    if (ss == null) {
                        ss = tm.getServiceState();
                    }
                }
                if (ss != null && isServiceStateFiveGAvailable(ss.toString())) {
                    networkType = NETWORK_TYPE_NR;
                }
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        return networkType;
    }

    /**
     * get data sub id
     *
     * @return subId
     */
    private static int getSubId() {
        int defaultDataSubId = -1;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
            defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
        }
        return defaultDataSubId;
    }

    /**
     * check the service state str is 5G
     *
     * @param ss services state str
     * @return true if is 5G
     */
    private static boolean isServiceStateFiveGAvailable(String ss) {
        boolean available = false;
        if (!TextUtils.isEmpty(ss)
                && (ss.contains("nrState=NOT_RESTRICTED")
                || ss.contains("nrState=CONNECTED"))) {
            available = true;
        }
        return available;
    }
}

此方法為通用方法政模,使用時(shí)只需調(diào)用NetworkUtil#getNetWorkType(Context)即可,當(dāng)然前提還得先獲取下READ_PHONE_STATE權(quán)限蚂会,各大廠商皆可用淋样。

上述代碼已適配targetSdkVersion P(28)及更高的版本,ReflectionUtil代碼可參考這篇文章

轉(zhuǎn)載請(qǐng)注明出處:http://www.reibang.com/p/f63cd36f7a2b

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胁住,一起剝皮案震驚了整個(gè)濱河市趁猴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌彪见,老刑警劉巖儡司,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異余指,居然都是意外死亡捕犬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來碉碉,“玉大人柴钻,你說我怎么就攤上這事」噶福” “怎么了贴届?”我有些...
    開封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蜡吧。 經(jīng)常有香客問我毫蚓,道長(zhǎng),這世上最難降的妖魔是什么斩跌? 我笑而不...
    開封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任绍些,我火速辦了婚禮,結(jié)果婚禮上耀鸦,老公的妹妹穿的比我還像新娘柬批。我一直安慰自己,他們只是感情好袖订,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開白布氮帐。 她就那樣靜靜地躺著,像睡著了一般洛姑。 火紅的嫁衣襯著肌膚如雪上沐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天楞艾,我揣著相機(jī)與錄音参咙,去河邊找鬼。 笑死硫眯,一個(gè)胖子當(dāng)著我的面吹牛蕴侧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播两入,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼净宵,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了裹纳?” 一聲冷哼從身側(cè)響起择葡,我...
    開封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剃氧,沒想到半個(gè)月后敏储,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡朋鞍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年已添,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迫横。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酝碳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恨狈,到底是詐尸還是另有隱情疏哗,我是刑警寧澤,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布禾怠,位于F島的核電站返奉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吗氏。R本人自食惡果不足惜芽偏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望弦讽。 院中可真熱鬧污尉,春花似錦、人聲如沸往产。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仿村。三九已至锐朴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蔼囊,已是汗流浹背焚志。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留畏鼓,地道東北人酱酬。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像滴肿,于是被迫代替她去往敵國(guó)和親岳悟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361