Android插件化技术之加载未安装APK

1、概述

Android插件化是一种解决方案,当一个应用发展成一个平台级应用时,就更需要针对各个子业务模块按需动态加载,要做到按需动态加载一种是可以通过H5的方案,另一种就是
针对各个子业务模块单独开发成一个APK,这时候这个平台级应用我们称为宿主,子业务模块APK称为插件,宿主通过反射点击去学习、代理点击去学习等实现hook技术来完成插件APK的免安装加载。

所以必须要先了解这个HOOK技术。

2、HOOK技术

HOOK翻译成钩子,钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。在Android系统中,通俗来讲,就是去阅读源码,然后通过反射或代理的方式去替换/绕过系统原有的调用逻辑,嵌入自己的逻辑代码,从而实现业务功能的扩展。

Android的插件化过程中最重要的步骤就是在宿主应用中去打开未安装插件APK的Activity,要想实现这个过程,通过常规的API方式去调用是无法做到这一点的,startActivity只可以实现打开一个已安装应用的且android:exported="true"的Activity,所以我们必须要想办法去解决这个问题,也就是做到不安装APK也能打开其Activity。

所以,就需要通过源码去了解startActivity的调用过程,找到可以hook的地方,startActivty有三种常用的调用方式,Context.startActivity(intent,…),Activity.startActivity(intent,…),隐式调用。

2.1、根据Android9.0系统源码,查看Activity.startActivity(intent,…)的调用流程

[开始] --> [startActivity] --> [Activity.startActivityForResult]
–> [mInstrumentation.execStartActivity] --> [ActivityManager.getService().startActivity]
–> [ActivityManagerService.startActivity] --> [ActivityManagerService.startActivityAsUser]
–> [ActivityStarter.execute()] --> [startActivity] --> [startActivityUnchecked]
–> [ActivityStackSupervisor.resumeFocusedStackTopActivityLocked] --> [ActivityStack.resumeTopActivityUncheckedLocked]
–> [resumeTopActivityInnerLocked] --> [ClientLifecycleManager.scheduleTransaction]
–> [ClientTransaction.schedule] --> [IApplicationThread.scheduleTransaction]
–> [ActivityThread.this.scheduleTransaction] --> [ClientTransactionHandler.sendMessage]
–> [TransactionExecutor.execute] --> [executeCallbacks] --> [ClientTransactionItem.execute]
–> [LaunchActivityItem.execute] --> [ClientTransactionHandler.handleLaunchActivity]
–> [ActivityThread.handleLaunchActivity] --> [performLaunchActivity]
–> [mInstrumentation.newActivity] --> [mInstrumentation.callActivityOnCreate]
–> [Activity.performCreate] --> [Activity.onCreate] --> [结束]

Activity Instrumentation ActivityManagerService ActivityStarter ActivityStackSupervisor ActivityStack ClientLifecycleManager ClientTransaction IApplicationThread ClientTransactionHandler TransactionExecutor ClientTransactionItem LaunchActivityItem ActivityThread startActivityForResult execStartActivity startActivityAsUser startActivityUnchecked resumeFocusedStackTopActivityLocked resumeTopActivityInnerLocked scheduleTransaction schedule sendMessage execute execute execute handleLaunchActivity handleLaunchActivity performLaunchActivity newActivity、callActivityOnCreate、performCreate onCreate Activity Instrumentation ActivityManagerService ActivityStarter ActivityStackSupervisor ActivityStack ClientLifecycleManager ClientTransaction IApplicationThread ClientTransactionHandler TransactionExecutor ClientTransactionItem LaunchActivityItem ActivityThread

2.2、根据Android9.0系统源码,查看Context.startActivity(intent,…)的调用流程

[开始] --> [getBaseContext.startActivity] --> [ContextImpl.startActivity] --> [mMainThread.getInstrumentation().execStartActivity] --> [ActivityManager.getService().startActivity] --> …之后同上Activity.startActivity… --> [结束]

看源码的网站,随便推荐一个: 点击查看Android系统源码

3、最终解决方案

3.1、实现思路

现在开始来选取Hook的注入点,主要是针对Instrumentation这个类进行代理扩展系统业务功能,execStartActivity执行前,先把传入的插件APK目标Intent替换成我们在宿主项目中创建的已在manifest.xml中注册的替身类ShaowActivity,绕过Manifest注册检查(对应的checkStartActivityResult函数就是做这个检查的),然后我们选择newActivity作为关键点,创建新的Activity时使用插件APK的Activity,创建好插件APK的Activity之后,还需要替换插件的资源(因为现在的资源还是宿主的),故我们选择在callActivityOnCreate这个地方作为替换插件APK的上下文、Resources、Theme等等资源的关键点,具体操作就是需要构建插件的上下文然后通过反射进行替换。(多多参考学习)

3.2、演示效果

在这里插入图片描述

4、最终代码实现

4.1、创建代理类:InstrumentationProxy.java



import android.app.Activity;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.util.Log;

import com.xx.escape.plan.ProxyActivity;
import com.xx.escape.plan.plugin.PluginContext;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author james
 * @date 2024-08-09 ~ 2024-08-23 调试完毕
 * @brief description
 * 标准版
 */
public class InstrumentationProxy extends Instrumentation {
   
   
    public static final String TAG = InstrumentationProxy.class.getSimpleName();
    Instrumentation mInstrumentation;

    static final String KEY_TARGET_INTENT = "key_target_intent";

    public InstrumentationProxy(Instrumentation instrumentation) {
   
   
        mInstrumentation = instrumentation;
    }

    private PluginContext mPluginContext;

    public void inject(PluginContext pluginContext) {
   
   
        this.mPluginContext = pluginContext;
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
   
   
        Log.d(TAG, "\n执行了execStartActivity, 参数如下: \n" + "who = [" + who + "], " +
                "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                "\ntarget = [" + target + "], \nintent = [" + intent +
                "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");

        try {
   
   
            //List<ResolveInfo> infoList = who.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL);//这种写法不兼容6.0以下的设备
            boolean isPluginActivity = isUnregisteredManifestActivity((Activity) who, intent);
            //替换系统传递的intent(这个意图里面就是我们要访问的插件APK的Activity界面,直接访问插件APK里面的Activity是会被系统拦截的,因为没有注册manifest.xml,所以我们需要伪装)为我们指定的目标targetIntent,通过伪装成访问宿主的ProxyActivity,跳过manifest.xml的注册检测
            Intent targetIntent;
            if (isPluginActivity) {
   
   
                targetIntent = new Intent(who, ProxyActivity.class);
                //塞入到Extra中,执行到后面的newActivity再进行还原
                targetIntent.putExtra(KEY_TARGET_INTENT, intent)<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值