【android每日一问】插件化原理解析,移动客户端开发工程师专业

本文深入探讨了Android插件化的实现,分析了应用在启动时遇到的`java.lang.RuntimeException`,并揭示了问题源于系统无法获取未安装插件的包信息。通过hook `PackageManager`来欺骗系统,使插件看似已安装。接着介绍了两种加载插件的方案,包括手动加载和委托系统加载。对于委托系统加载,详细解释了如何利用宿主的ClassLoader加载插件类,并通过修改`DexClassLoader`的`dexElements`数组实现了类加载。最后总结了这两种方案,并鼓励开发者不断提升技术能力,以应对行业挑战。
摘要由CSDN通过智能技术生成

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.com.sohu.test.app/com.sohu.test.app.MainActivity}:
java.lang.RuntimeException: Unable to instantiate application android.app.Application: java.lang.IllegalStateException:
Unable to get package info for com.sohu.test.app; is package not installed?

错误提示说是无法实例化 Application,而Application的创建也是在performLaunchActivity中进行的,这里有些蹊跷,我们仔细查看一下发现:

通过ActivityThread的performLaunchActivity方法可以得知,Application通过LoadedApk的makeApplication方法创建,我们查看这个方法,在源码中发现了上文异常抛出的位置:

try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals(“android”)) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to instantiate application " + appClass

  • ": " + e.toString(), e);
    }
    }

经过排查发现:

private void initializeJavaContextClassLoader() {
IPackageManager pm = ActivityThread.getPackageManager();
android.content.pm.PackageInfo pi;
try {
pi = pm.getPackageInfo(mPackageName, 0, UserHandle.myUserId());
} catch (RemoteException e) {
throw new IllegalStateException("Unable to get package info for "

  • mPackageName + “; is system dying?”, e);
    }
    if (pi == null) {
    throw new IllegalStateException("Unable to get package info for "
  • mPackageName + “; is package not installed?”);
    }

boolean sharedUserIdSet = (pi.sharedUserId != null);
boolean processNameNotDefault =
(pi.applicationInfo != null &&
!mPackageName.equals(pi.applicationInfo.processName));
boolean sharable = (sharedUserIdSet || processNameNotDefault);
ClassLoader contextClassLoader =
(sharable)
? new WarningContextClassLoader()
: mClassLoader;
Thread.currentThread().setContextClassLoader(contextClassLoader);
}

这里,我们找出了这个异常的来源:原来这里调用了getPackageInfo方法获取包的信息;而我们的插件并没有安装在系统上,因此系统肯定认为插件没有安装,这个方法肯定返回null。所以,我们还要欺骗一下PMS,让系统觉得插件已经安装在系统上了.

private static void hookPackageManager() throws Exception {

// 这一步是因为 initializeJavaContextClassLoader 这个方法内部无意中检查了这个包是否在系统安装
// 如果没有安装, 直接抛出异常, 这里需要临时Ho

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值