美文网首页
virtual apk

virtual apk

作者: 雪国落叶 | 来源:发表于2020-04-12 12:36 被阅读0次

在做插件化初始化

插件化时要明确插件中包含哪些组件,才能在启动组件时加以区分,比如:

public ResolveInfo resolveService(Intent intent, int flags) {
        for (LoadedPlugin plugin : this.mPlugins.values()) {
            ResolveInfo resolveInfo = plugin.resolveService(intent, flags);
            if (null != resolveInfo) {
                return resolveInfo;
            }
        }

        return null;
    }

校验签名版本号

从下方函数可以看出调用collectCertificates后,签名会被赋值
private static final class PackageParserV24 {

    static final PackageParser.Package parsePackage(Context context, File apk, int flags) throws Throwable {
        /**
         * 解析指定位置的安装包。它自动会检测安装包的模式的是单一APK或者集群APK模式。
         * 这样就可以对"集群APK"的安装包进行理性的检查,比如会检查"base APK"和"拆分APK"是否具有相同的包名和版本号。
         * 请注意,这里面是不执行签名验证的,所以必须要单独执行collectCertificates(Package,int)这个方法
         */
        //Android 安装一个APK的时候首先会解析APK,而解析APK则需要用到一个工具类,这个工具类就是PackageParser
        PackageParser parser = new PackageParser();
        PackageParser.Package pkg = parser.parsePackage(apk, flags);
        //调用PackageParser中collectCertificates方法,传入pkg、flags,
        /**
         *      从给定包中描述的所有apk收集证书,
         *     public static void collectCertificates(Package pkg, int parseFlags)
         *             throws PackageParserException {
         *         collectCertificatesInternal(pkg, parseFlags);
         *         final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
         *         for (int i = 0; i < childCount; i++) {
         *             Package childPkg = pkg.childPackages.get(i);
         *             childPkg.mCertificates = pkg.mCertificates;
         *             childPkg.mSignatures = pkg.mSignatures;
         *             childPkg.mSigningKeys = pkg.mSigningKeys;
         *         }
         *     }
         */
        Reflector.with(parser)
            .method("collectCertificates", PackageParser.Package.class, int.class)
            .call(pkg, flags);
        return pkg;
    }
}

绑定资源

AssetManager中addAssetPath方法设置的是apk所在的路径。


image.png

AssetManager中:

/**
     * Add an additional set of assets to the asset manager.  This can be
     * either a directory or ZIP file.  Not for use by applications.  Returns
     * the cookie of the added asset, or 0 on failure.
     * {@hide}
     */
    public final int addAssetPath(String path) {
        return  addAssetPathInternal(path, false);
    }

private native final int addAssetPathNative(String path, boolean appAsLib);

看代码的逻辑,进行资源合并时,要么生成AssetManager,然后调用addAssetPath加入宿主apk的sourceDir,然后再addAssetPath加入自身的路径。

动态广播

通过提取PackageParser中Package信息获取到注册的receivers,然后将静态广播改变为动态广播:
比较以下两种方式:
BroadcastReceiver cc = (BroadcastReceiver) getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance();
BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());

for (PackageParser.Activity receiver : this.mPackage.receivers) {
            receivers.put(receiver.getComponentName(), receiver.info);
            //知道BroadcastReceiver类名,由ClassLoader创建实例,并调用宿主的Context的registerReceiver方法完成插件的注册。
            //动态注册广播,对于广播只能如此
            BroadcastReceiver br = BroadcastReceiver.class.cast(getClassLoader().loadClass(receiver.getComponentName().getClassName()).newInstance());
            for (PackageParser.ActivityIntentInfo aii : receiver.intents) {
                this.mHostContext.registerReceiver(br, aii);
            }
        }
        this.mReceiverInfos = Collections.unmodifiableMap(receivers);
        this.mPackageInfo.receivers = receivers.values().toArray(new ActivityInfo[receivers.size()]);

启动插件中service

在service启动过程中和Activity类似的处理,不一样之处在于,分为预置LocalService和RemoteService两类,在manifest文件中通过打桩的方式,先插入

<!-- Local Service running in main process -->
        <service android:exported="false" android:name="com.didi.virtualapk.delegate.LocalService" />

        <!-- Daemon Service running in child process -->
        <service android:exported="false" android:name="com.didi.virtualapk.delegate.RemoteService" android:process=":daemon">
            <intent-filter>
                <action android:name="${applicationId}.intent.ACTION_DAEMON_SERVICE" />
            </intent-filter>
        </service>

然后如果通过查找发现时插件中的service组件,则区分LocalService还是RemoteService,然后对Intent进行封装,如本次的行为startService or bindService封装为command指令,target.package 和target.cls可以指定真正要启动的对象,接下来就是在onStartCommand中进行处理了。
由于service在插件中,需要通过插件的classLoader进行加载,并且加载后的service加入记录中rememberService,然后调用onStartCommand方法

在service的启动流程中:ActivityThread会受到创建Service的指令, handleCreateService,如果想要在自己创建Service后,还能继续走后续的流程那么必然是因为又通知了AMS,才能走后续的onBind和onStartCommand之类的函数

private void handleCreateService(ActivityThread.CreateServiceData data) {
        this.unscheduleGcIdler();
        LoadedApk packageInfo = this.getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
        Service service = null;

        try {
            ClassLoader cl = packageInfo.getClassLoader();
            service = (Service)cl.loadClass(data.info.name).newInstance();
        } catch (Exception var9) {
            if (!this.mInstrumentation.onException(service, var9)) {
                throw new RuntimeException("Unable to instantiate service " + data.info.name + ": " + var9.toString(), var9);
            }
        }

        try {
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
            Application app = packageInfo.makeApplication(false, this.mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault());
            service.onCreate();
            this.mServices.put(data.token, service);

            try {
                // 注意此处传递的是0,处理参数,onStartCommand时传递的为1
                ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 0, 0, 0);
            } catch (RemoteException var7) {
                throw var7.rethrowFromSystemServer();
            }
        } catch (Exception var8) {
            if (!this.mInstrumentation.onException(service, var8)) {
                throw new RuntimeException("Unable to create service " + data.info.name + ": " + var8.toString(), var8);
            }
        }

    }

private void handleServiceArgs(ActivityThread.ServiceArgsData data) {
        Service s = (Service)this.mServices.get(data.token);
        if (s != null) {
            try {
                if (data.args != null) {
                    data.args.setExtrasClassLoader(s.getClassLoader());
                    data.args.prepareToEnterProcess();
                }

                int res;
                if (!data.taskRemoved) {
                    res = s.onStartCommand(data.args, data.flags, data.startId);
                } else {
                    s.onTaskRemoved(data.args);
                    res = 1000;
                }

                QueuedWork.waitToFinish();

                try {
                    ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 1, data.startId, res);
                } catch (RemoteException var5) {
                    throw var5.rethrowFromSystemServer();
                }

                this.ensureJitEnabled();
            } catch (Exception var6) {
                if (!this.mInstrumentation.onException(s, var6)) {
                    throw new RuntimeException("Unable to start service " + s + " with " + data.args + ": " + var6.toString(), var6);
                }
            }
        }

    }

private void handleBindService(ActivityThread.BindServiceData data) {
        Service s = (Service)this.mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();

                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 0, 0, 0);
                    }

                    this.ensureJitEnabled();
                } catch (RemoteException var4) {
                    throw var4.rethrowFromSystemServer();
                }
            } catch (Exception var5) {
                if (!this.mInstrumentation.onException(s, var5)) {
                    throw new RuntimeException("Unable to bind to service " + s + " with " + data.intent + ": " + var5.toString(), var5);
                }
            }
        }

    }
switch (command) {
            case EXTRA_COMMAND_START_SERVICE: {
                ActivityThread mainThread = ActivityThread.currentActivityThread();
                IApplicationThread appThread = mainThread.getApplicationThread();
                Service service;

                if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
                    service = this.mPluginManager.getComponentsHandler().getService(component);
                } else {
                    try {
                        service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();

                        Application app = plugin.getApplication();
                        IBinder token = appThread.asBinder();
                        Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
                        IActivityManager am = mPluginManager.getActivityManager();

                        attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am);
                        service.onCreate();
                        this.mPluginManager.getComponentsHandler().rememberService(component, service);
                    } catch (Throwable t) {
                        return START_STICKY;
                    }
                }

                service.onStartCommand(target, 0, this.mPluginManager.getComponentsHandler().getServiceCounter(service).getAndIncrement());
                break;
            }

bindService时,需要传递ServiceConnection,由于是进程内的Binder对象,可以通过PluginUtil
保存获取:

// ActivityManagerProxy
protected Object bindService(Object proxy, Method method, Object[] args) throws Throwable {
        Intent target = (Intent) args[2];
        ResolveInfo resolveInfo = this.mPluginManager.resolveService(target, 0);
        if (null == resolveInfo || null == resolveInfo.serviceInfo) {
            // is host service
            return method.invoke(this.mActivityManager, args);
        }

        Bundle bundle = new Bundle();
        PluginUtil.putBinder(bundle, "sc", (IBinder) args[4]);
        startDelegateServiceForTarget(target, resolveInfo.serviceInfo, bundle, RemoteService.EXTRA_COMMAND_BIND_SERVICE);
        mPluginManager.getComponentsHandler().remberIServiceConnection((IBinder) args[4], target);
        return 1;
    }

  // Service
   public abstract IBinder onBind(Intent var1);

    public boolean onUnbind(Intent intent) {
        return false;
    }

    public void onRebind(Intent intent) {
    }

// LocalService
case EXTRA_COMMAND_BIND_SERVICE: {
                ActivityThread mainThread = ActivityThread.currentActivityThread();
                IApplicationThread appThread = mainThread.getApplicationThread();
                Service service = null;

                if (this.mPluginManager.getComponentsHandler().isServiceAvailable(component)) {
                    service = this.mPluginManager.getComponentsHandler().getService(component);
                } else {
                    try {
                        service = (Service) plugin.getClassLoader().loadClass(component.getClassName()).newInstance();

                        Application app = plugin.getApplication();
                        IBinder token = appThread.asBinder();
                        Method attach = service.getClass().getMethod("attach", Context.class, ActivityThread.class, String.class, IBinder.class, Application.class, Object.class);
                        IActivityManager am = mPluginManager.getActivityManager();

                        attach.invoke(service, plugin.getPluginContext(), mainThread, component.getClassName(), token, app, am);
                        service.onCreate();
                        this.mPluginManager.getComponentsHandler().rememberService(component, service);
                    } catch (Throwable t) {
                        Log.w(TAG, t);
                    }
                }
                try {
                    IBinder binder = service.onBind(target);
                    IBinder serviceConnection = PluginUtil.getBinder(intent.getExtras(), "sc");
                    IServiceConnection iServiceConnection = IServiceConnection.Stub.asInterface(serviceConnection);
                    if (Build.VERSION.SDK_INT >= 26) {
                        iServiceConnection.connected(component, binder, false);
                    } else {
                        Reflector.QuietReflector.with(iServiceConnection).method("connected", ComponentName.class, IBinder.class).call(component, binder);
                    }
                } catch (Exception e) {
                    Log.w(TAG, e);
                }
                break;
            }

从整体上看,在声明周期这一过程中做了简化。
去除了跨进程的能力,通过静态存储IBinder等进行数据交换。

参考

https://blog.csdn.net/MldXTieJiang/java/article/details/105314237

相关文章

网友评论

      本文标题:virtual apk

      本文链接:https://www.haomeiwen.com/subject/ftdvmhtx.html