当前位置: 首页 > news >正文

Android Test2 获取系统android id

Android Test2 获取系统 android id

这篇文章针对一个常用的功能做一个测试。

在项目中,时常会遇到的一个需求就是:一台设备的唯一标识值。然后,在网络请求中将这个识别值传送到后端服务器,用作后端数据查询的条件。Android 设备上最常用的一个标识值就是 android id,其他也有可作为设备唯一标识值的一些参数值。

Android 标识值

列举下 Android 系统上可以用来作为唯一标识的一些值和可用性的场景。

硬件上

  • IMEI(国际移动设备标识码)—— 这个值的获取需要在设备上有 SIM 卡,否则获取到的就是空值(null)。在 Android 10+ 以上系统,需要系统权限,作为第三方应用无法获取。
  • MEID(移动设备识别码)—— 这个值的获取是需要在 CDMA 网络环境下。这个值的获取在 Android 8+ 以上系统,也收到 SIM 卡和系统权限限制。
  • 序列号(Serial Number)—— 这个值的设置与厂商有关,也有厂商不设置这个值,那么返回的就是空。

系统上

  • ANDROID_ID —— 设备首次开机时产生,在设备重置后会重新生成。
  • MAC(物理地址)—— 一般是获取 WIFI 的 MAC 地址,但这个值在高版本的 Android 系统上,已经是动态生成。不是很合适作为一个设备的唯一标识值。如果 WIFI 未起作用,直接尝试获取 MAC 地址,获取的值可能总是 02:00:00:00:00:00

软件生成

  • UUID(通用唯一识别码)—— 首次启动 app/sdk 时生成,并做存储。应用写在后会失效,需要做好持久化设计。
  • OAID(开放匿名设备标识符)—— 这个一般在国内用于广告追踪,可以被重置。

在我的项目中,使用了 ANDROID_ID 作为识别符,因为获取它无需申请权限,且一般情况下,设备不会被轻易重置。


获取 ANDROID_ID

项目运行的 Android 系统版本要求是 11+,因此只需读取 Settings.Secure.ANDROID_ID 值。

public final class Tools {public static String getDeviceId( @NonNull Context context ) {// Android 10+if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ) {return Settings.Secure.getString( context.getContentResolver(), Settings.Secure.ANDROID_ID );}return ""}
}

下来就是考虑如何通过测试代码获取这个值。

依据第一篇文章的介绍,通过单元测试或者仪器测试都可以尝试。

  • 可以是通过单元测试,代码写于 src/test 中并运行在纯 JVM 环境中。
  • 可以是通过仪器测试,代码写于 src/androidTest 中并运行与真机之上。

单元测试

因为涉及的目标测试方法的参数是 android.content.Context 类,属于 android frameworks 的 API,因此需要引入 robolectric(在 JVM 环境中模拟 android frameworks 的环境)。

下面就看下单元测试代码

@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.R])
class ToolsTest {// android id samples: ["02edb4a7f2a32b72"]private lateinit var _context: Context@Beforefun init() {val application = RuntimeEnvironment.getApplication() // ApplicationProvider.getApplicationContext<Context>()_context = application.applicationContextval actualContentResolver = _context.contentResolver//("02edb4a7f2a32b72")Settings.Secure.putString(actualContentResolver,Settings.Secure.ANDROID_ID,"02edb4a7f2a32b72")}@Testfun test_getDeviceId_shouldReturnDeviceId() {val deviceId = Tools.getDeviceId(_context)Assert.assertEquals(deviceId,  "02edb4a7f2a32b72")}
}

上面的代码很简单,做一个基本的描述:

  • 类声明 ToolsTest 头部有标注 @RunWith(RobolectricTestRunner::class)@Config(sdk = [Build.VERSION_CODES.R]),通过这两个标注启用 robolectric 的 shadow 机制,在运行过程中将对 android frameworks API 的调用路由到 robolectric 的 API 调用。

  • RuntimeEnvironment.getApplication() 在测试环境下获取一个 Application 实例,这个方法不会去获取 app/sdk 的真实实现的实例,因为上面的测试代码是运行的 JVM 环境中,不会直接访问 android frameworks 的 API。注释的代码 ApplicationProvider.getApplicationContext<Context>() 也可以运行,这个 API 来自 androidx.test (androidx.test.core.app.ApplicationProvider)包,它的优势在于可以跨环境运行,既可以基于 JVM 环境运行,也可以在 Android 真机上运行,它也是首选的(preferable)获取 Context 的使用方法。

  • 通过 android.content.Context 获取 android.content.ContentResolver 实例,用于准备修改保存 Settings.Secure.ANDROID_ID 字段的值。

  • init() 方法的执行会在 test_getDeviceId_shouldReturnDeviceId() 方法之前运行,由于它标注了 @Before,每个 @Test 方法的执行前都会执行 @Before 方法。在这个方法中,将 Settings.Secure.ANDROID_ID 的值修改为 02edb4a7f2a32b72,也作为最终的比较目标。

  • test_getDeviceId_shouldReturnDeviceId() 方法中,调用 Tools.getDeviceId(Context) 方法获取想要的 Settings.Secure.ANDROID_ID 值,并与 @Before 方法中初始设置的值作比较。

接着运行 test_getDeviceId_shouldReturnDeviceId() 方法,最后的结果是 passed 状态,即运行成功。

unit test



真机测试

真机测试的代码编辑在 src/androidTest 目录中,因为程序运行时,IDE 会单独打包一个 ‘<application_id>.test’ 的 apk,安装到连接的真实设备,然后运行测试代码。

这里在测试时还遇到一个有意思的问题,在相同的项目中,创建一个新的 app module,并编辑相同的测试代码,运行结果获取的 ANDROID_ID 值是与另一个app module 执行的结果是不同的——由于文章长度不想过长,这个下一篇文章说明。

首先在 app module 的 build.gradledependencies block 中检查依赖项的配置。当前不作界面测试,因此这里不配置 Espresso, Rules 等库。

android {defaultConfig {// other configstestInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}
}denpendencies {// ...testImplementation "junit:junit:4.13.2"androidTestImplementation "androidx.test.ext:junit:1.2.1"
}

针对这篇文章内的例子,这两个基本库就可以了。

接着,就是编辑测试代码。

@RunWith(AndroidJUnit4::class)
class ToolsAndroidTest {companion object {const val SDK_33_ANDROID_ID = "fd8aa7fe27625e8d"}private lateinit var _appContext: Context@Beforefun setup() {_appContext = ApplicationProvider.getApplicationContext<Context>()}@Testfun test_getDeviceId_shouldReturnDeviceId() {val deviceId = Tools.getDeviceId(_appContext)Assert.assertNotEquals(deviceId, "", "Unexpected device id.")Assert.assertEquals(deviceId, SDK_33_ANDROID_ID)}
}

最后运行测试方法,等待编译好的 ‘<application_id>.test’ apk 安装,最后查看测试代码执行结果。

Instrumentation Test

到这里,通过 robolectric 模拟 Android 环境下测试的获取 ANDROID_ID 和真机测试的两种测试场景都可以了。

相关文章:

  • DeepSeek 助力 Vue3 开发:打造丝滑的日历(Calendar),日历_天气预报日历示例(CalendarView01_18)
  • WordZero:让Markdown与Word文档自由转换的Golang利器
  • 装备制造项目管理具备什么特征?如何选择适配的项目管理软件系统进行项目管控?
  • 【SSM】SpringBoot笔记2:整合Junit、MyBatis
  • 【SSM】SpringMVC学习笔记7:前后端数据传输协议和异常处理
  • 2025前端微服务 - 无界 的实战应用
  • 深入浅出 Scrapy:打造高效、强大的 Python 网络爬虫
  • 初探Service服务发现机制
  • QT使用WxSQLite3打开加密数据库并查询
  • Django CMS 的 Demo
  • Qt多线程访问同一个数据库源码分享(基于Sqlite实现)
  • 更新版【飞云翻倍系统】新增支撑压力多线参考技术,操盘技术图文解说
  • 4.2 HarmonyOS NEXT分布式AI应用实践:联邦学习、跨设备协作与个性化推荐实战
  • Struts2漏洞由浅入深
  • AIGC图像去噪:核心原理、算法实现与深度学习模型详解
  • Rocketmq消息队列 消息模型 详解
  • [论文阅读] 人工智能+软件工程 | MemFL:给大模型装上“项目记忆”,让软件故障定位又快又准
  • 在 Ubuntu 24.04 LTS 上安装 Jenkins 并配置全局工具(Git、JDK、Maven)
  • 探秘半导体制造设备钢结构防震基座的承重奥秘-江苏泊苏系统集成有限公司
  • 好得睐:以品质守味、以科技筑基,传递便捷与品质
  • 网站建设公司需要icp证/百度一下手机版
  • 微信小程序怎么添加/杭州网站运营十年乐云seo
  • 政府门户网站建设合同/没被屏蔽的国外新闻网站
  • 吉安网站建设优化服务/永久免费建个人网站
  • 网站建设和网络推广是干嘛/seo研究中心vip课程
  • 嘉祥网站建设/足球世界排名一览表