Android studio无法直接使用系统隐藏类、变量、接口导致的编译错误的 解决经验
一、错误常见现象:
直接使用@UnsupportedAppUsage注释的变量、@SystemApi的接口,import 系统隐藏类@hide(不希望外部apk直接使用或者类内大多是前两者变量和接口)如import android.os.ServiceManager,所有使用这些代码的地方直接飘红,因为这些注解的函数、类、变量是不编译进谷歌提供的sdk里的,直接使用的话编译器检查自然过不了。
二、解决办法:
1. 通常谷歌合作厂商为了方便会搞一个jar包出来不同于直接自动download的androidsdk中的jar,并进行替换,其中内容不同于原内容,也消除了隐藏变量接口的注解,以便直接使用。
(补充解释jar 是java的归档格式,是一个程序集(微软体系都这么叫),展开基本全是class文件。sdk 不限语言,不限平台,这个范围很广。)
操作步骤:
将提供的android.jar对路径下android.jar替换,原jar重名为bak即可。androidstudio会检测到并自动重新index目录到项目cache中,就不再飘红可以直接使用了。(有时厂商还有其独特的framework-res.apk也是要直接放到这个目录下的,以方便apk直接调用其在框架创建的品牌前缀的系统类)(如果还有其他的jar包使用,不能放在这里,要放在对应项目的中lib文件夹中,并在gradle中的dependencies中添加 "implementation files('libs\\abcdefg.jar')" 或在androidstudio界面中右键此包选择"将此包作为库文件使用"等类似字眼)
如果可以自己编译系统源码的话,通过make framework-minus-apex -j16,然后拿此目录的jar包:out/target/common/obj/JAVA_LIBRARIES/framework-minus-apex_intermediates/classes.jar即可。
2. 使用反射、假接口和假的aidl进行模拟访问骗过apk编译就OK了,根据虚拟机原理apk将使用系统已经加载好的这些类,只要假接口和aidl的包路径和系统里的一样,且不要在apk配置中开启代码混淆,就可以顺利访问到系统内的实际功能代码。
操作步骤:
举例解释:
public void disableNavigation(){ try { //STATUS_BAR_SERVICE是系统api,不暴露给apk用,拿出来放到apk类变量中或者反射用都可以,这里拿出来了。DISABLE_NAVIGATION是View.java中的同理被我拿出来了。 //ServiceManager的getService是注解不暴露给外部apk用的,我们这里通过反射调用了,接收它用了apk里我们自己写的aidl假接口 Method method = null; method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class); IBinder binder = (IBinder) method.invoke(null, STATUS_BAR_SERVICE); if (binder != null) { IStatusBarService mISs = IStatusBarService.Stub.asInterface(binder); mISs.disable(DISABLE_NAVIGATION, new Binder(), getApplicationContext().getPackageName()); } } catch (RemoteException e) { e.printStackTrace(); } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } //原代码 // IStatusBarService mISs = IStatusBarService.Stub.asInterface(ServiceManager.getService(STATUS_BAR_SERVICE)); // try { // mISs.disable(StatusBarManager.DISABLE_NAVIGATION, new Binder(), getApplicationContext().getPackageName()); // } catch (RemoteException e) { // e.printStackTrace(); // } }
只将需要的接口写到apk的aidl中即可。
但是注意,如果调用的是hide-api,可能会报在对应类或jar包中找不到相应函数(具体什么样的函数无法被反射到很难从注解角度下结论,谷歌可能是有一份api暴露名单,具体问题需要具体验证),那是因为你的应用不是系统应用或者没有系统签名,应用如果无法改变,
就要在/frameworks/base/core/java/android/content/pm/ApplicationInfo.java的isAllowedToUseHiddenApis函数里加应用白名单。
3.同理,使用假java类也可以解决一些类似的问题:
(如果你觉得有用的话可以关注下博主~欢迎评论区讨论~请注明转载链接~)
本文来自博客园,作者:小汀,转载请注明原文链接:https://www.cnblogs.com/1118zjg/p/16281454.html