在项目支持的时候,由于后台需要记录日志,而记录日志就或多或少的需要获得手机的IMEI来做标示。在安卓系统版本的迭代情况下,获取IMEI方式会遇到很多问题。下面通过代码来演示如何获取IMEI。
首先了解一下什么是IMEI
IMEI是International Mobile Equipment Identity (国际移动设备标识)的简称
即通常所说的手机序列号,用于在手机网络中识别每一部独立的手机,是国际上公认的手机标志序号,相当于移动电话的身份证。国际移动装备辨识码一般贴于机身背面与外包装上,同时也存在于手机记忆体中,通过输入*#06#即可查询。
IMEI由15位数字组成的”电子串号”,它与每台手机一一对应,而且该码是全世界唯一的
由上得出IMEI可以认为是手机唯一标识。它是存在于手机里的。那么,也就是说在pad上它是无法获取到的。
在手机4.0以下,我们可以使用:
/**
* 系统4.0的时候
* 获取手机IMEI 或者MEID
*
* @return 手机IMEI
*/
public static String getImeiOrMeid(Context ctx) {
TelephonyManager manager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
if (manager != null) {
return manager.getDeviceId();
}
return null;
}
来获取。
这时或许会问为什么方法里会带有meid。那MEID又是什么? 我们通过TelephoneyManager源码来了解一下。(注:源码是在android-28下)
/**
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
* or ESN for CDMA phones. Return null if device ID is not available.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
* MEID for CDMA.
*/
@Deprecated
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getDeviceId() {
try {
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
return telephony.getDeviceId(mContext.getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
return null;
}
}
通过注释我们了解到,如果当CDMA情况下,返回的是MEID。那么MEID又是什么呢?
MEID
MEID 移动设备识别码(Mobile Equipment Identifier)是CDMA手机的身份识别码,也是每台CDMA手机或通讯平板唯一的识别码。
那么两者有什么区别呢?也许你已经看出来了,MEID是CDMA手机的识别码,IMEI是GSM、WCDMA手机的识别码,对应到国内的运营商如下
移动:2G使用 GSM, 3G使用 TD-SCDMA 4G使用TD-LTE联通:2G使用 GSM, 3G使用 WCDMA 4G使用TD-LTE、FDD-LTE电信:2G使用 CDMA,3G使用 CDMA2000 4G使用TD-LTE、FDD-LTE
所以,得到结论,IMEI是联通移动手机的标识,MEID是电信手机的标识。当需求人员说要记录一下手机的IMEI时候,提供MEID也是一样的。因为MEID也是唯一标识的。
那么在5.0以上难道getDeviceId方法就不行了么?
不是不行,是返回的结果或许不是我们想象的一样。
因为5.0系统以上,安卓的碎片化更加严重,有单卡槽的,有双卡槽的,有全网通的,有卡槽一个是uim一个是 Sim的,有双SIM的。
5.0的系统如果想获取MEID/IMEI1/IMEI2 ----framework层提供了两个属性值“ril.cdma.meid"和“ril.gsm.imei"获取
/**
* 5.0统一使用这个获取IMEI IMEI2 MEID
*
* @param ctx
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
public static Map getImeiAndMeid(Context ctx) {
Map<String, String> map = new HashMap<String, String>();
TelephonyManager mTelephonyManager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
Class<?> clazz = null;
Method method = null;//(int slotId)
try {
clazz = Class.forName("android.os.SystemProperties");
method = clazz.getMethod("get", String.class, String.class);
String gsm = (String) method.invoke(null, "ril.gsm.imei", "");
String meid = (String) method.invoke(null, "ril.cdma.meid", "");
map.put("meid", meid);
if (!TextUtils.isEmpty(gsm)) {
//the value of gsm like:xxxxxx,xxxxxx
String imeiArray[] = gsm.split(",");
if (imeiArray != null && imeiArray.length > 0) {
map.put("imei1", imeiArray[0]);
if (imeiArray.length > 1) {
map.put("imei2", imeiArray[1]);
} else {
map.put("imei2", mTelephonyManager.getDeviceId(1));
}
} else {
map.put("imei1", mTelephonyManager.getDeviceId(0));
map.put("imei2", mTelephonyManager.getDeviceId(1));
}
} else {
map.put("imei1", mTelephonyManager.getDeviceId(0));
map.put("imei2", mTelephonyManager.getDeviceId(1));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return map;
}
而在6.0系统以后,安卓官方已经支持双卡的api了。获取更加简单,此时如果IMEI不存在直接返回null。所以直接去拿MEID。
@TargetApi(Build.VERSION_CODES.O)
public static Map getIMEIforO(Context context) {
Map<String, String> map = new HashMap<String, String>();
TelephonyManager tm = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
String imei1 = tm.getImei(0);
String imei2 = tm.getImei(1);
if(TextUtils.isEmpty(imei1)&&TextUtils.isEmpty(imei2)){
map.put("imei1", tm.getMeid()); //如果CDMA制式手机返回MEID
}else {
map.put("imei1", imei1);
map.put("imei2", imei2);
}
return map;
}
看一下getImei源码:
/**
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @param slotIndex of which IMEI is returned
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getImei(int slotIndex) {
ITelephony telephony = getITelephony();
if (telephony == null) return null;
try {
return telephony.getImeiForSlot(slotIndex, getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
return null;
}
}
以上就是不同系统如何获取唯一标识的方式。 下面放上完整工具类文件。
/**
* 工程里关于imei和meid获取不正确。避免修改后影响工程特单独写一份。
* 或许二次开发使用本工具类
*/
public class PhoneInfoUtils {
/**
* 获取当前手机系统版本号
*
* @return 系统版本号
*/
public static String getSystemVersion() {
return Build.DISPLAY;
//return android.os.Build.VERSION.RELEASE;
}
/**
* 获取手机型号
*
* @return 手机型号
*/
public static String getSystemModel() {
return Build.MODEL;
}
/**
* 获取手机厂商
*
* @return 手机厂商
*/
public static String getDeviceBrand() {
return Build.BRAND;
}
/**
* 获取SN
*
* @return
*/
public static String getSn(Context ctx) {
String serial = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
serial = (String) get.invoke(c, "ro.serialno");
} catch (Exception ignored) {
}
return serial;
}
/**
* 系统4.0的时候
* 获取手机IMEI 或者MEID
*
* @return 手机IMEI
*/
public static String getImeiOrMeid(Context ctx) {
TelephonyManager manager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
if (manager != null) {
return manager.getDeviceId();
}
return null;
}
/**
* 拿到imei或者meid后判断是有多少位数
*
* @param ctx
* @return
*/
public static int getNumber(Context ctx) {
return getImeiOrMeid(ctx).trim().length();
}
/**
* 5.0统一使用这个获取IMEI IMEI2 MEID
*
* @param ctx
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
public static Map getImeiAndMeid(Context ctx) {
Map<String, String> map = new HashMap<String, String>();
TelephonyManager mTelephonyManager = (TelephonyManager) ctx.getSystemService(Activity.TELEPHONY_SERVICE);
Class<?> clazz = null;
Method method = null;//(int slotId)
try {
clazz = Class.forName("android.os.SystemProperties");
method = clazz.getMethod("get", String.class, String.class);
String gsm = (String) method.invoke(null, "ril.gsm.imei", "");
String meid = (String) method.invoke(null, "ril.cdma.meid", "");
map.put("meid", meid);
if (!TextUtils.isEmpty(gsm)) {
//the value of gsm like:xxxxxx,xxxxxx
String imeiArray[] = gsm.split(",");
if (imeiArray != null && imeiArray.length > 0) {
map.put("imei1", imeiArray[0]);
if (imeiArray.length > 1) {
map.put("imei2", imeiArray[1]);
} else {
map.put("imei2", mTelephonyManager.getDeviceId(1));
}
} else {
map.put("imei1", mTelephonyManager.getDeviceId(0));
map.put("imei2", mTelephonyManager.getDeviceId(1));
}
} else {
map.put("imei1", mTelephonyManager.getDeviceId(0));
map.put("imei2", mTelephonyManager.getDeviceId(1));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return map;
}
@TargetApi(Build.VERSION_CODES.O)
public static Map getIMEIforO(Context context) {
Map<String, String> map = new HashMap<String, String>();
TelephonyManager tm = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
String imei1 = tm.getImei(0);
String imei2 = tm.getImei(1);
if(TextUtils.isEmpty(imei1)&&TextUtils.isEmpty(imei2)){
map.put("imei1", tm.getMeid()); //如果CDMA制式手机返回MEID
}else {
map.put("imei1", imei1);
map.put("imei2", imei2);
}
return map;
}
/**
* 获取版本号
*
* @param context
* @return
*/
public static int getVerCode(Context context) {
int vercoe = 0;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
vercoe = packageInfo.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return vercoe;
}
public static String getIMEI(Context ctx){
String imei = "";
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.M){ //4.0以下 直接获取
imei = getImeiOrMeid(ctx);
}else if(Build.VERSION.SDK_INT<Build.VERSION_CODES.O){ //5。0,6。0系统
Map imeiMaps = getImeiAndMeid(ctx);
imei = getTransform(imeiMaps);
}else{
Map imeiMaps = getIMEIforO(ctx);
imei = getTransform(imeiMaps);
}
return imei;
}
private static String getTransform( Map imeiMaps){
String imei = "";
if(imeiMaps!=null){
String imei1 = (String) imeiMaps.get("imei1");
if(TextUtils.isEmpty(imei1)){
return imei;
}
String imei2 = (String) imeiMaps.get("imei2");
if(imei2!=null) {
if (imei1.trim().length() == 15 && imei2.trim().length() == 15) {
//如果两个位数都是15。说明都是有效IMEI。根据从大到小排列
long i1 = Long.parseLong(imei1.trim());
long i2 = Long.parseLong(imei2.trim());
if(i1 > i2){
imei = imei2+";"+imei1;
}else{
imei = imei1+";"+imei2;
}
}else{ //
if(imei1.trim().length() == 15){
//如果只有imei1是有效的
imei = imei1;
}else if (imei2.trim().length() == 15){
//如果只有imei2是有效的
imei = imei2;
}else{
//如果都无效那么都为meid。只取一个就可以
imei = imei1;
}
}
}else{
imei = imei1;
}
}
return imei;
}
}
参考链接:
https://www.jianshu.com/p/740e9116281f
https://blog.csdn.net/yangbin0513/article/details/68490291
网友评论