随着Android 版本的更新,获取手机序列号的方式也有所不同。最近在工作中看到了,查阅资料在这里总结下。
一、获取序列号兼容写法
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final int CODE_READ_PHONE_STATE = 0x11;
private String serialNumber = "UNKNOWN";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "current Build.VERSION.SDK_INT: "+Build.VERSION.SDK_INT);
getSerialNumber();
}
@RequiresApi(api = Build.VERSION_CODES.O)
public void checkPermission() {
if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, CODE_READ_PHONE_STATE);
} else {
serialNumber = Build.getSerial();
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
@SuppressLint("MissingPermission")
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == CODE_READ_PHONE_STATE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
serialNumber = Build.getSerial();
} else {
Log.i(TAG, "you deny the permission:READ_PHONE_STATE");
}
}
@SuppressLint("HardwareIds")
public String getSerialNumber() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {// 版本>=android Q(安卓10)
Log.i(TAG, "TODO use yourself style ");
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { // android O_MR1(安卓8.1)、安卓9
checkPermission();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){// android O(安卓 8.0)
this.serialNumber = Build.SERIAL;
}else {//below android 8.0
try {
// 方式1:执行adb 命令获得
Runtime runtime = Runtime.getRuntime();
Process process = null;
process = runtime.exec("getprop ro.serialno");
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
this.serialNumber = br.readLine();
// 方式2:反射获得(不推荐使用)
// Class<?> c = Class.forName("android.os.SystemProperties");
// Method get = c.getMethod("get", String.class);
// serialNumber = (String) get.invoke(c, "ro.serialno");
// Log.i(TAG, " sdk version low android O ,reflect get" + serialNumber);
} catch (IOException e) {
e.printStackTrace();
}
}
return serialNumber;
}
}
二、测试过程及其结论
1、有关Build.SERIAL字段获取方式
1、Android api 9 出现的这个字段,Api26(8.0)开始废弃。但是8.0(api 26)上还能使用8.1开始不能使用这个字段获取。
2、安卓8.1及其以上使用这种方式获取会返回UNKNOWN字符串。
3、8.0开始废弃啦,8.0及其以上版本建议使用Build.getSerial()方法代替。
个人结论:
- 8.0还可以用这个方式获取,因为getSerial()方式需要“read phone state” 权限,而读取手机权限时总要去让用户决定,相对来说有点限制。
2、有关Build.getSerial()获取方式
(1)、测试结论:
- 安卓Q(api 29)手机上直接崩溃,即从Q开始获取序列号的方式用户自己实现,例如从自己定义的数据库中查询。
- 安卓8.1(O, api 27)、安卓9(P, api 28)上可以获得,需要“read phone state” 权限,否则崩溃
- Build.getSerial()是安卓8.1-9.0上Build.SERIAL废弃后的替代方案。
(2)安卓10(即安卓Q,api 29)上的替代方案
官方文档这样说的:安卓10 开始获取持久性表示符被google添加了限制,建议使用可重置的标识符。但是以下几种情况除外(可以获得到):
1、app是系统预装app ,且被授予了READ_PRIVILEGED_PHONE_STATE权限。
2、如果app是设备或配置文件所有者,并且已被授予Manifest.permission读取手机状态许可。配置文件所有者是一个在设备上拥有托管配置文件的应用程序;有关更多详细信息,请参阅工作配置文件。配置文件所有者访问已弃用,将在将来的版本中删除。
3、如果呼app对任何活动订阅具有运营商特权。
4、如果app是默认的SMS角色持有者(参考RoleManager.isRoleHeld(String)理解)
如果不是上述四种情况:
1、安卓8.1及其以上 , 使用Build.SERIAL 返回Build#UNKNOWN
2、安卓8.0到安卓9.0上使用Build.getSerial且没有READ_PHONE_STATE 权限系统就抛出安全异常
3、安卓10开始使用Build.getSerial系统就抛出安全异常
3、有关代码中8.0以下获取方式思考
(1)Build.SERIAL方式获取
这种获取方式支持到8.0及其以下,但会8.0以下网上文章好多都使用反射或者执行命令获得获得我想大概是这种方式是api 9添加的, 虽说api9以下机子几乎没人用了但是这种并不是兼容写法。使用反射或者执行命令才是真正兼容的吧!
(2)执行命令或者反射获取测试
1、二者在8.0(>=8.0)版本上获取值为空,8.0以下可以获得
2、不建议使用反射方式获取,反射影响性能,而且还是不安全做法(如下图)
The end
给出了自己总结的一种方案,,,,,溜溜球。