详解Java/Android利用反射技术调用@hide类和函数

admin 2021-11-26 AM 6987℃ 0条

背景介绍

公司的主要产品中有个核心的部件,它是使用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源码

非特殊说明,本博所有文章均为博主原创。

评论已关闭