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