详解Java/Android利用反射技术调用@hide类和函数
背景介绍
公司的主要产品中有个核心的部件,它是使用Android工业板子作为硬件基础,在里面开发一个app作为控制程序。这款产品需要现场部署,意味着我们在更新的过程中不会有人为的干预,既然没有人为的干预,那么决定了该app需要实现静默更新
功能。
所谓的静默更新,就是不需要在人的干预下,实现app的自主更新,并且完成自我启动。该方法区别于传统的人为升级,不需要人点击即可完成升级,升级过程中程序可以没有任何的感知。关于静默升级的内容在这里不做展开,这里着重讲一下研发升级功能的时候遇到的一个问题,即如何利用反射技术调用@hide类或者@hide函数。
为了会遇到这个问题呢?那是因为Android的framework层代码已经把实现更新的代码进行@hide隐藏(详细请看下面贴出的源代码),隐藏的目的就是不想人随意的去使用该功能,其二就是这些功能需要一定的系统权限才能运用。而我们的静默更能恰好用到了隐藏的一些类和函数,所以碰到了利用反射调用@hide类的问题。
涉及到的类代码如下:
package android.app;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.content.pm.IPackageInstallObserver2;
import android.os.Bundle;
/** {@hide} */
public class PackageInstallObserver {
@UnsupportedAppUsage
public PackageInstallObserver() {
}
private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) {
PackageInstallObserver.this.onUserActionRequired(intent);
}
@Override
public void onPackageInstalled(String basePackageName, int returnCode,
String msg, Bundle extras) {
PackageInstallObserver.this.onPackageInstalled(basePackageName, returnCode, msg,
extras);
}
};
/** {@hide} */
public IPackageInstallObserver2 getBinder() {
return mBinder;
}
public void onUserActionRequired(Intent intent) {
}
/**
* This method will be called to report the result of the package
* installation attempt.
*
* @param basePackageName Name of the package whose installation was
* attempted
* @param extras If non-null, this Bundle contains extras providing
* additional information about an install failure. See
* {@link android.content.pm.PackageManager} for documentation
* about which extras apply to various failures; in particular
* the strings named EXTRA_FAILURE_*.
* @param returnCode The numeric success or failure code indicating the
* basic outcome
* @hide
*/
@UnsupportedAppUsage
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
}
}
可以看出,PackageInstallObserver
已经被隐藏,甚至你不能在代码中new一个PackageInstallObserver出来。
利用反射调用@hide方法
除了隐藏类,有些类仅仅是隐藏掉了一些方法,这一类方法需要使用反射技术来调用。这是一个很普通的用法,问网上也有很多示例,这里不作为展开。笔者在代码中涉及到反射调用@hide方法的示例如下:
Class<?> pmClz = packageManager.getClass();
Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, aClass, int.class, String.class);
method.setAccessible(true);
// flag = 2 , 为静默安装
method.invoke(packageManager, Uri.fromFile(new File(apkPath)), installObserver, 2, null);
利用反射技术调用类的@hide方法步骤如下:
- 1、使用getClass获取类的类实例
- 2、使用getDeclaredMethod获取到方法实例,注意按照顺序传入参数类型
- 3、使用setAccessible来设置进入权限
- 4、最后使用invoke方法调用该@hide方法
利用反射生成@hide类的实例
与@hide方法不一样,@hide类不能在IDE中引用,意味着不能使用:XXX xx = new XXX()的语法。使用@hide类,从实例的生成到调用该类的方法全程需要使用反射技术。
首先,我们要用反射技术得到该类的实例。在静默更新过程中的示例代码如下:
Class<?> aClass = Class.forName("android.app.PackageInstallObserver");
Constructor<?> constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
Object installObserver = constructor.newInstance();
从代码我们得到利用反射技术生成@hide实例步骤:
- 1、使用Class.forName得到类的class类型
- 2、使用getDeclaredConstructor获取到默认的构造函数示例
- 3、因为构造函数也是一个函数,所以需要设置使用权限:setAccessible
- 4、使用构造函数的newInstance函数生成实例
至此,我们顺利拿到@hide类型的示例。@hide类的@hide函数和普通@hide函数使用方法一致,请参考前面部分的介绍。
以上,我们学会了利用反射技术使用@hide函数和生成@hide类的实例。
最后附上PackageInstallObserver源码:点击查看PackageInstallObserver源码