Android Test3 获取的ANDROID_ID值不同
Android Test3 获取的ANDROID_ID值不同
这篇文章来说明上一篇文章中说到的一个现象:在同一个项目中,创建不同的 app module,运行同一段测试代码,获取到的 ANDROID_ID
的值不同。
我也是第一次认真研究这个现象,这个还涉及到了 ANDROID_ID
值的系统访问的执行原理。这里一起来看下,有知道更详细细节的大佬,不吝在评论里添加。
下面几种场景。
ANDROID_ID
结果差异
下面是几种不同差异的场景。
1. applicationId
不同,ANDROID_ID
不同
在同一个项目目录下,创建两个不同的 app module,会产生不同有 applicationId
值。在新建的 app module 的 src/androidTest
目录下拷贝一份原有 app module 的测试代码。
@RunWith(AndroidJUnit4::class)
class ToolsAndroidTest {companion object {const val SDK_33_ANDROID_ID = "fd8aa7fe27625e8d" // 正常执行 app 程序读取到 ADNROID_ID}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(SDK_33_ANDROID_ID, deviceId)}
}
新建的 app module 命名 testsdk,原有的 app module 依然叫 app。
两个 module 的区别:
applicationId
值不同:- testsdk 的
applicationId "com.sanren1024.testsdk"
。 - app 的
applicationId "com.sanren1024.phone"
。
- testsdk 的
- 实现不同:
- testsdk 仅有测试代码,没有任何的逻辑实现,包括界面设计。
- app 中有诸多逻辑的实现,包括自定义的
Application
实现,它是一个完整功能的 app 模块。
分别运行 testsdk 和 app 的测试代码。
-
运行 testsdk 的测试代码,获取的
deviceId
值是6fafd019bf9cd426
,详细信息如下。org.junit.ComparisonFailure: expected:<[fd8aa77327a25e8d]> but was:<[6fafd019bf9cd426]> at org.junit.Assert.assertEquals(Assert.java:117) at org.junit.Assert.assertEquals(Assert.java:146) ...
-
运行 app 的测试代码,获取的
deviceId
值是fd8aa77327a25e8d
.
两者的测试获取的值不同。预期的 testsdk 结果值应该与 app 的执行值一致,而实际 testsdk 执行结果是另一个值。
2. applicationId
不同,ANDROID_ID
相同
再新建一个 app module,命名 testapp,与 testsdk 一样,只包含测试代码。testapp 的 applicationId
值为 "com.sanren1024.phone"
,这个值与 app 相同。
比较 testsdk 和 testapp 的测试代码结果,testsdk 执行结果是 6fafd019bf9cd426
, testapp 执行结果是 6fafd019bf9cd426
。看出来了,两者的结果值是相同的。
3. applicationId
相同,ANDROID_ID
不同
分别运行 app 与 testapp,这两个 app module 的 applicationId
相同,查看运行结果。
app 测试代码执行结果 fd8aa77327a25e8d
,testapp 测试代码执行结果 6fafd019bf9cd426
。两者也不同。
上面三种情况下,导致了我对 ANDROID_ID
值变化的疑惑。
分析差异
上面的几个场景中,只有 app 包含了完整的功能实现,另外两个 app module 都只保含了测试代码。所以重点是排查 app 内相关配置和可能的实现。经过仔细查看后,发现的差异是在 app 的 build.gradle
中 buildType
block 中,配置了 debug
这个 build variant 的签名。
android {//...signingConfigs {//...'platform' {storeFile file('../platform.keystore')storePassword '123456'keyAlias 'platform'keyPassword '123456'}}buildTypes {//...debug {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules-sqaDebug.pro'signingConfig signingConfigs.'platform'versionNameSuffix ".0"debuggable true}}
}
找到了这个差异,于是将 signingConfig
设置项注释掉,并再次执行测试代码。于是惊喜出现了,得到下面的异常信息。
org.junit.ComparisonFailure: expected:<[fd8aa77327a25e8d]> but was:<[6fafd019bf9cd426]>
at org.junit.Assert.assertEquals(Assert.java:117)
at org.junit.Assert.assertEquals(Assert.java:146)
...
与 场景1 中贴出的错误信息一致。那就猜想一个事实,app 读取的 ANDROID_ID
值与签名有关联。
为了验证猜想,修改 debug
block 的 signingConfig
为另一个签名文件。
android {//...signingConfigs {'debug_alter' {storeFile file('../debug_alter.jks')storePassword '123456'keyAlias 'debug_alter'keyPassword '123456'}'platform' {storeFile file('../platform.keystore')storePassword '123456'keyAlias 'platform'keyPassword '123456'}}buildTypes {//...debug {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules-sqaDebug.pro'signingConfig signingConfigs.'debug_alter'versionNameSuffix ".0"debuggable true}}
}
执行测试代码后,结果错误信息如下。
org.junit.ComparisonFailure: expected:<[fd8aa77327a25e8d]> but was:<[3e1b82e6762993df]>
at org.junit.Assert.assertEquals(Assert.java:117)
at org.junit.Assert.assertEquals(Assert.java:146)
...
从上面这段输出结果看出,这次执行后的 ADNROID_ID
结果是 3e1b82e6762993df
,与开始执行结果不同。这也佐证了上面的猜想。
随机去查看源码:
文件:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerUtils.java:61
frameworks/base/core/java/android/provider/Settings.java
// ActivityManagerUtils.java:56
public class ActivityManagerUtils {// .../*** Return a hash between [0, MAX_VALUE] generated from the android ID.*/@VisibleForTestingstatic int getAndroidIdHash() {// No synchronization is required. Double-initialization is fine here.if (sAndroidIdHash == null) {final ContentResolver resolver = ActivityThread.currentApplication().getContentResolver();// 读取 ANDROID_ID 最直接的调用位置final String androidId = Settings.Secure.getStringForUser(resolver,Settings.Secure.ANDROID_ID,resolver.getUserId()); // 获取当前使用用户idsAndroidIdHash = getUnsignedHashUnCached(sInjectedAndroidId != null ? sInjectedAndroidId : androidId);}return sAndroidIdHash;}// ...
}// Settings.java:6424
public final class Settings {// ...public static final class Secure extends NameValueTable {// ...@UnsupportedAppUsagepublic static String getStringForUser(ContentResolver resolver, String name,int userHandle) {// ...return sNameValueCache.getStringForUser(resolver, name, userHandle);}// ...}// ...private static class NameValueCache {@UnsupportedAppUsagepublic String getStringForUser(ContentResolver cr, String name, final int userHandle) {// ......}}
}
从上面源码调用流程上,它最终调用到 NameValueCache#getStringForUser(ContentResolver, String, final int)
方法,最后的值与系统的 user id 和 当前 app 的信息(签名,ApplicationInfo
等)都有关系。
结论:不同 app 的 apk 在同一台设备上读取到的 ADNROID_ID
基本肯定是不同的。同一个 app 的不同签名的 apk 在同一设备上基本是不同的。(基本不同是因为还与 Android 的系统版本有关系)
结论
造成文章开头说的 ANDROID_ID
值不同的原因是 Android 系统的设计导致的。在版本高些的 Android 系统上,ANDROID_ID
的值与系统版本,应用签名,用户ID都有关系。