这阵子忙着整理项目了,因此就没怎么出新的文章了,不过下面写的这篇文章对你们颇有帮助。关于双卡双待的信息获取,包含了imei、phonenumber、operatorName(sim卡生产商,国内就主要指三大运营商了)、NetworkType(这里就主要是4G、3G等了)。java
睡着国内的双卡手机出现,致使获取双卡的信息也是成了一个头痛的事了。google给开发者暴露的api仍是停留在单卡上,因此在这里我就整理出相关的代码,让更多的猿友少走弯路。android
首先从phonenumber的获取着手吧,顺便带着你们一块儿去看下相关的源码,之前获取phonenumber我是这么获取的:git
((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE))
.getLine1Number();
复制代码
这里就调用了TelephonyManager的getLine1Number方法,这里顺道去源码看看getLine1Number是怎么获取的:github
/** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * OR * {@link android.Manifest.permission#READ_SMS} * <p> * The default SMS app can also use this. */
public String getLine1Number() {
return getLine1Number(getSubId());
}
复制代码
注: 我这里源码都是android-25下面的,刚看了下android-23下面的源码是这么调用的:api
/** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * OR * {@link android.Manifest.permission#READ_SMS} * <p> * The default SMS app can also use this. */
public String getLine1Number() {
return getLine1NumberForSubscriber(getDefaultSubscription());
}
复制代码
仍是有些区别的,起码方法的调用是不同的,因此建议你在看该篇文章的时候仍是把compileSdk升到25: compileSdkVersion 25
能够看到25的api是继续调了:getLine1Number(getSubId())
该方法,那就继续往下走吧:网络
/** * Returns the phone number string for line 1, for example, the MSISDN * for a GSM phone for a particular subscription. Return null if it is unavailable. * <p> * Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * OR * {@link android.Manifest.permission#READ_SMS} * <p> * The default SMS app can also use this. * * @param subId whose phone number for line 1 is returned * @hide */
public String getLine1Number(int subId) {
String number = null;
try {
ITelephony telephony = getITelephony();
if (telephony != null)
number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName());
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
}
if (number != null) {
return number;
}
try {
IPhoneSubInfo info = getSubscriberInfo();
if (info == null)
return null;
return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
// This could happen before phone restarts due to crashing
return null;
}
}
复制代码
看到这的时候真的是心灰意冷啊,为何这么说,该方法居然是hide类型的方法,对于这种方法咋们就用到反射了,后面会详细介绍的,看看它的参数是如何解释的: @param subId whose phone number for line 1 is returned
反正我是英语很差的哈,接着我就去查了查相关的说法,这里去看看这篇文章是如何解释的(subid指的是什么),简单来讲subid
指的就是sim卡的索引了,当有一个sim卡的时候subid=1,有两个的时候subid=2。依次类推就能够知道有几个卡subid就是多少了。不过这里的subid仍是能够经过反射来获取subid,后面也会讲到如何获取咱们的subid:app
private static final String SIM_LINE_NUMBER = "getLine1Number";
private static final String SIM_STATE = "getSimState";
public static String getSimPhonenumber(Context context, int slotIdx) {
if (PermissionUtil.hasSelfPermission(context, Manifest.permission.READ_PHONE_STATE) ||
PermissionUtil.hasSelfPermission(context, "android.permission.READ_PRIVILEGED_PHONE_STATE")) {
Log.d(TAG, "READ_PHONE_STATE permission has BEEN granted to getSimPhonenumber().");
if (getSimStateBySlotIdx(context, slotIdx)) {
return (String) getSimByMethod(context, SIM_LINE_NUMBER, getSubidBySlotId(context, slotIdx));
}
return null;
} else {
Log.d(TAG, "READ_PHONE_STATE permission has NOT been granted to getSimPhonenumber().");
return null;
}
}
/** *获取相应卡的状态 * @param slotIdx:0(sim1),1(sim2) * @return true:使用中;false:未使用中 */
public static boolean getSimStateBySlotIdx(Context context, int slotIdx) {
boolean isReady = false;
Object getSimState = getSimByMethod(context, SIM_STATE, slotIdx);
if (getSimState != null) {
int simState = Integer.parseInt(getSimState.toString());
if ((simState != TelephonyManager.SIM_STATE_ABSENT) && (simState != TelephonyManager.SIM_STATE_UNKNOWN)) {
isReady = true;
}
}
return isReady;
}
/** * 经过slotid获取相应卡的subid * @param context * @param slotId * @return */
public static int getSubidBySlotId(Context context, int slotId) {
SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(
Context.TELEPHONY_SUBSCRIPTION_SERVICE);
try {
Class<?> telephonyClass = Class.forName(subscriptionManager.getClass().getName());
Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
Method getSimState = telephonyClass.getMethod("getSubId", parameter);
Object[] obParameter = new Object[1];
obParameter[0] = slotId;
Object ob_phone = getSimState.invoke(subscriptionManager, obParameter);
if (ob_phone != null) {
Log.d(TAG, "slotId:" + slotId + ";" + ((int[]) ob_phone)[0]);
return ((int[]) ob_phone)[0];
}
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
/** *经过反射调用相应的方法 * */
public static Object getSimByMethod(Context context, String method, int param) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
try {
Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
Class<?>[] parameter = new Class[1];
parameter[0] = int.class;
Method getSimState = telephonyClass.getMethod(method, parameter);
Object[] obParameter = new Object[1];
obParameter[0] = param;
Object ob_phone = getSimState.invoke(telephony, obParameter);
if (ob_phone != null) {
return ob_phone;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
复制代码
能够看到getSimPhonenumber
方法须要slotIdx
参数,这里仍是去这篇文章看看slotldx
是咋回事(slotldx究竟是啥玩意),经过了解后,slotldx指的是那个卡槽了,若是当前要获取卡1,slotldx=0;若是是卡2,slotldx=1;到此知道为啥getSimPhonenumber
方法须要定义这么个参数了吧。至于说getSimState
方法,仍是同样经过反射去获取每一个卡的状态的,这里就不赘述源码了。上面能够看到获取subId的代码了吧,就是getSubidBySlotId
方法了,这里经过反射调用了SubscriptionManager
类中的getSubId
方法,须要的参数也是咱们的slotId
。源码以下:ide
/** @hide */
public static int[] getSubId(int slotId) {
if (!isValidSlotId(slotId)) {
logd("[getSubId]- fail");
return null;
}
int[] subId = null;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
subId = iSub.getSubId(slotId);
}
} catch (RemoteException ex) {
// ignore it
}
return subId;
}
复制代码
还有imei
、operatorName
、NetworkType
均可以经过相应的方法获取了:post
private static final String SIM_OPERATOR_NAME = "getNetworkOperatorName";
private static final String SIM_NETWORK_TYPE = "getNetworkType";
private static final String SIM_IMEI = "getImei";
//获取相应卡的imei
public static String getSimImei(Context context, int slotIdx) {
if (PermissionUtil.hasSelfPermission(context, Manifest.permission.READ_PHONE_STATE) ||
PermissionUtil.hasSelfPermission(context, "android.permission.READ_PRIVILEGED_PHONE_STATE")) {
Log.d(TAG, "READ_PHONE_STATE permission has BEEN granted to getSimImei().");
if (getSimStateBySlotIdx(context, slotIdx)) {
//sim1
if (slotIdx == 0) {
//这里的参数传的是slotldx
return (String) getSimByMethod(context, SIM_IMEI, 0);
} else if (slotIdx == 1) {
return (String) getSimByMethod(context, SIM_IMEI, 1);
}
}
return null;
} else {
Log.d(TAG, "READ_PHONE_STATE permission has NOT been granted to getSimImei().");
return null;
}
}
public static String getSimNetworkName(Context context, int slotIdx) {
if (getSimStateBySlotIdx(context, slotIdx)) {
return getNetworkName((int)
getSimByMethod(context, SIM_NETWORK_TYPE, getSubidBySlotId(context, slotIdx)));
}
return "UNKNOWN";
}
public static String getSimOperatorName(Context context, int slotIdx) {
if (getSimStateBySlotIdx(context, slotIdx)) {
return (String) getSimByMethod(context, SIM_OPERATOR_NAME, getSubidBySlotId(context, slotIdx));
}
return null;
}
复制代码
到此相关的属性获取基本ok了,你们若是还须要获取什么属性,直接去TelephonyManager
查看相关的源码。还有一个就是插卡和拔卡的监听、网络变化的监听:ui
//网络变化的监听
public class SimConnectReceive extends BroadcastReceiver {
private static final String TAG = SimConnectReceive.class.getSimpleName();
public final static String ACTION_SIM_STATE_CHANGED = ConnectivityManager.CONNECTIVITY_ACTION;
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_SIM_STATE_CHANGED)) {
Log.d(TAG, "onReceive");
EventBus.getDefault().post(new SimConnectChange());
}
}
}
//插卡和拔卡的监听
public class SimStateReceive extends BroadcastReceiver {
private static final String TAG = SimStateReceive.class.getSimpleName();
public final static String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_SIM_STATE_CHANGED)) {
Log.d(TAG, "onReceive");
EventBus.getDefault().post(new SimStateChange());
}
}
}
复制代码
还有就是不要忘了manifest中权限: <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
欢迎客官到本店光临:184793647
(qq群)
最后贴上该功能的代码:
thanks:DualSIMCard
有什么问题能够email我:a1002326270@163.com