上篇文章Android修炼之Pie 适配的搬运工中介绍了Android P的一些行为变动并提供了一些对齐刘海和非SDK接口的适配建议,大部分人仍是更加关心非SDK接口的问题,因此本文来讲一下如何检测非SDK接口。java
通常来讲,SDK接口是指在Android框架软件包索引中记录的接口,其对立面天然就叫非SDK接口了。Android 9引入了针对非SDK接口的使用限制,不管是直接使用仍是经过反射或JNI间接使用。这意味着,经过反射之类的语义来操做某个类时,不该访问SDK中未列出的函数或字段(一般都有@hide
标志)。linux
Android 9将非SDK接口都放在了各名单中:android
这些名单均可以在:非SDK接口名单中找到。若是不想编译AOSP,能够直接下载该页的tgz
文件,解压便可看到对应的txt
文件。git
在Android 9的系统中,如你的应用中使用了非SDK接口,系统将会在APP运行时打印日志;若是访问了某些“列入灰名单的”非SDK接口,系统还可能显示Toast;若是访问了“列入黑名单的”非SDK接口,系统将引起异常。github
日志消息微信
在开发过程当中,咱们能够经过AS的logcat来查看日志,不然就是经过adb logcat
了。非SDK接口的日志会显示在正在运行的应用的PID下,大概格式是:app
Accessing hidden field Landroid/net/wifi/WifiManager;->WIFI_SCAN_AVAILABLE:Ljava/lang/String; (dark greylist, reflection)
复制代码
这里给出几个示例:框架
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
Field field = null;
try {
field = wifiManager.getClass().getDeclaredField("WIFI_SCAN_AVAILABLE");
Log.d("ThirdActivity", (String) field.get(wifiManager));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 直接在浅灰名单中找的接口
public void testLightGreyList(View view) {
ReflectUtils.getMethod(TelephonyManager.class, "isMultiSimEnabled");
}
// 直接在深灰名单中找的接口
@TargetApi(Build.VERSION_CODES.M)
public void testDarkGreyList(View view) {
ReflectUtils.getField(CarrierConfigManager.class, "KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL");
}
// 直接在黑名单中找的接口
public void testBlackList(View view) {
try {
ReflectUtils.getMethod(ReflectUtils.getClass("android.net.util.IpUtils"), "ipChecksum", ByteBuffer.class, int.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
复制代码
他们的日志以下:ide
Accessing hidden field Landroid/net/wifi/WifiManager;->WIFI_SCAN_AVAILABLE:Ljava/lang/String; (dark greylist, reflection)
Accessing hidden method Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z (light greylist, reflection)
Accessing hidden field Landroid/telephony/CarrierConfigManager;->KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL:Ljava/lang/String; (dark greylist, reflection)
Accessing hidden method Landroid/net/util/IpUtils;->ipChecksum(Ljava/nio/ByteBuffer;I)S (blacklist, reflection)
复制代码
注意:在debug的时候,只有触发了对应的调用逻辑,才会打印日志,而不是静态检测。因此新项目中,咱们在开发过程当中就要注意非SDK接口的调用。函数
Toast和Dialog提示
可能由于个人模拟器用的是Android P的正式版,去除了Toast提示。因此,在测试的时候并无看到相关的Toast提示。在预览版中应该有Toast提示功能。固然,也多是我没有触发“某些灰名单中的非SDK接口”(官方文档原话)的缘由。
不过,当我把TargetSDK改成27时,出现过这种对话框提示:
引起异常
官方说明:
经测试,只有深灰名单和黑名单中的非SDK接口会引起异常和错误。
注意:当TargetSDK>=28时,深灰名单和黑名单都会引起异常;而当TargetSDK<28时,只有黑名单会引起异常。示例结果以下:
谷歌提供了一个静态检测工具:veridex,能够帮助咱们检测apk中是否使用了非SDK接口。这里使用veridex-linux.zip
进行测试,解压后目录结构以下:
appcompat.sh
就是运行脚本。
将待检测的apk文件拷贝到veridex目录下,运行如下命令便可:
./appcompat.sh --dex-file=文件名.apk --imprecise
复制代码
结果:
若是去掉--imprecise
会检测不出来黑名单的接口。
咱们主要关注黑名单和深灰名单。能够看到,日志中给出了调用非SDK接口的位置,很容易定位。
--imprecise
参数,能够看到更多详细的信息;