Android Q唯一标识符适配问题

2,980 阅读2分钟

前言

以往公司的android应用里都需要为公司服务器上传一个设备唯一标识符,以让后端对设备进行统计等。
因此,以往的策略大多是获取IMEI,MAC地址等。然而在android升级到Q版本后,google彻底禁止了第三发应用获取imei,并且mac地址返回的地址也变成了02:00:00:00:00:00。

google也曾写过唯一标识符的建议文档

下面这篇文章作者对以往的几种常见方案为什么不在适用于android Q做了比较详细的说明
Android Q适配----唯一标识符篇

其他方案

之前在网上听到过其他方案,包括获取ANDROID_ID等

    public static String getAndroidId (Context context) {
        String ANDROID_ID = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID);
        return ANDROID_ID;
    }

然而我在android 9.0上测试,发现不同应用的正式包在相同设备上得到的ANDROID_ID结果不同,因此显然该方案不可行。

采用UUID

按照google官方意见,是在安装应用时,生成UUID,保存到本地,如果本地已经存在UUID,则不在保存,这样就可以将UUID作为唯一标识符

   String uuidStr = UUID.randomUUID().toString();

然而,在android Q版本中,google对存储权限进行了调整 androidQ 新版特性可看下列文章
android Q新特性...
适配Android Q上读取多媒体文件

  • 我的方案是将UUID以文件的形式保存在多媒体文件目录下,这样一来各个不同的应用间都能采用同一个UUID

具体实现如下

  • 生成UUID
    其中 saveFileName 为存放uuid数据的文件名
    /**
     * 在媒体文件中 生成fileName文件
     * 向Mediastore添加内容
     */
    private void creatUUIDFile() {
        ContentValues values = new ContentValues();
        values.put(MediaStore.Images.Media.DISPLAY_NAME, saveFileName);
        values.put(MediaStore.Images.Media.MIME_TYPE,"image/*");
        // TODO: 2019-08-27 IS_PENDING = 1表示对应的item还没准备好
        values.put(MediaStore.Images.Media.IS_PENDING,1);

        ContentResolver resolver = this.getContentResolver();
        Uri collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);

        Uri uri = resolver.insert(collection,values);

        try {
            //访问 对于单个媒体文件,请使用 openFileDescriptor()。
            ParcelFileDescriptor fielDescriptor = resolver.openFileDescriptor(uri,"w",null);
            FileOutputStream outputStream = new FileOutputStream(fielDescriptor.getFileDescriptor());
            try {
                //讲UUID写入到文件中
                String uuidStr = UUID.randomUUID().toString();
                outputStream.write(uuidStr.getBytes());
                outputStream.close();
                Log.d(TAG, "写入 uuidStr:"+uuidStr);
            } catch (IOException e) {
                e.printStackTrace();
            }
            values.clear();
            values.put(MediaStore.Images.Media.IS_PENDING, 0);          //设置为0
            resolver.update(uri,values,null,null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
  • 判断是否已经存在 存放uuid的文件
    /**
     * 检查文件是否存在
     * @return
     */
    private String checkUUIDFileByUri(){
        Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String[] projection = {
                MediaStore.Images.Media.DISPLAY_NAME,
                MediaStore.Images.Media._ID
        };
        //查询
        ContentResolver contentResolver = this.getContentResolver();

        // 添加筛选条件
        String selection = MediaStore.Images.Media.DISPLAY_NAME + "=" + "'" + saveFileName + "'";
        Cursor mCursor = contentResolver.query(mImageUri, projection, selection, null, null);

        String getSaveContent = "";
        if (mCursor != null) {
            while (mCursor.moveToNext()) {

                int fileIdIndex = mCursor.getColumnIndex(MediaStore.Images.Media._ID);
                String thumbPath = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
                        .appendPath(String.valueOf(mCursor.getInt(fileIdIndex))).build().toString();
                Uri fileUri = Uri.parse(thumbPath);
                try {
                    ParcelFileDescriptor fielDescriptor = contentResolver.openFileDescriptor(fileUri,"r",null);
                    FileInputStream inputStream = new FileInputStream(fielDescriptor.getFileDescriptor());
                    getSaveContent = inputStreamToString(inputStream);

                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }

                //只有在得到的唯一标识符不为空的情况下才结束循环,否则一直循环
                if (!TextUtils.isEmpty(getSaveContent)){
                    break;
                }
            }
            mCursor.close();

        }
        
        return getSaveContent;
    }