Android DynamicLoadApk源码分析

DynamicLoadApk是对Android的一个框架,并且是开源的,该框架支持插件无需安装就能调起,并且支持R文件直接访问插件的资源文件调起。

本文主要是分析DynamicLoadApk中主要的核心部分及插件apk的加载与资源文件的加载功能。DL中主要涉及到四个核心类DLPluginManager.java,DLProxyActivity.java,DLProxyImpl.java,DLBasePluginActivity.java。DL的调用简单流程:DLPluginManager----->DLProxyActivity---->DLProxyImpl---->DLBasePluginActivity。

DLPluginManager.java 该类主要负责初始化DexClassLoader,Resource,AssertManager三个核心对象。

在Android做插件开发涉及加载sdcard中的插件apk与加载apk中的资源文件。在Android系统中有两个主要的类加载器,pathClassLoader与DexClassLoader,而这两个类都是继承dalvik.system.BaseDexClassLoader。但是两个是有区别的。两个类的之主要区别就是DexClassLoader的构造方法中多了一个optimizedDirectory参数,这个是用于存放优化后的dex files 的路径。这个参数在BaseDexClassLoader中是可以为空。在DexClassLoader.java类注释中A class loader that loads classes from {@code .jar} and{@code .apk} files containing a {@code classes.dex} entry. This can be used to execute code not installed as part of an application.就是说DexClassLoader是可以加载外部的apk中的dex文件的。在PathClassLoader.java类注释中却是Provides a simple {@link ClassLoader} implementation that operates on a list of files and directories in the local file system, but does not attempt to load classes from the network. Android uses this class for its system class loader and for its application class loader(s).这个是Android加载自身系统的dex的,不能用于加载外部apk中的dex文件。总结就是DexClassLoader可以用于加载手机中未安装apk中dex文件,而PathClassLoader用于加载Android系统已经安装apk中的dex文件。所以在DLPluginManager中要初始化DexClassLoader用于加载插件apk中dex文件。

对于Resource与AssertManager的初始化主要是用于调用插件apk的资源文件,我们知道在做apk换肤的时候遇到的技术点在于如何加载插件apk中的资源文件,因为我们无法找到R文件。而R文件是在应用内部生成。在插件apk中是无法找到R文件,即使通过反射得到插件R文件,由于R文件中存放的都是十六进制也无法使用。根据Android加载资源文件的原理需要拿到插件apk的Resource因此在DLPluginManager中做了插件APK的Resource初始化。Resource初始化需要AssertManager对象,而AssertManager类是对外hide的因此需要反射拿到AsserManager对象。初始化方法

private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath) {

       DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName);
        if (pluginPackage != null) {
            return pluginPackage;
        }
        DexClassLoader dexClassLoader = createDexClassLoader(dexPath); //1创建dexClassLoader对象
        AssetManager assetManager = createAssetManager(dexPath); //2 创建AssetManager对象,该对象为插件apk中的
        Resources resources = createResources(assetManager); //3创建Resources 可用于加载插件apk资源与主题设置
        // create pluginPackage
        pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);
        mPackagesHolder.put(packageInfo.packageName, pluginPackage);
        return pluginPackage;
    }

调用插件apk的方法是DLPluginManager.getInstance(context).startPluginActivity(context,dlIntent),通过单利模式调用startPluginActivity,最终会调用到

 public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode) {
        if (mFrom == DLConstants.FROM_INTERNAL) {
            dlIntent.setClassName(context, dlIntent.getPluginClass());
            performStartActivityForResult(context, dlIntent, requestCode);
            return DLPluginManager.START_RESULT_SUCCESS;
        }

        String packageName = dlIntent.getPluginPackage();
        if (TextUtils.isEmpty(packageName)) {
            throw new NullPointerException("disallow null packageName.");
        }

        DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
        if (pluginPackage == null) {
            return START_RESULT_NO_PKG;
        }

        final String className = getPluginActivityFullPath(dlIntent, pluginPackage);
        Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className);
        if (clazz == null) {
            return START_RESULT_NO_CLASS;
        }

        // get the proxy activity class, the proxy activity will launch the plugin activity
        Class<? extends Activity> activityClass = getProxyActivityClass(clazz);
        if (activityClass == null) {
            return START_RESULT_TYPE_ERROR;
        }

        // put extra data
        dlIntent.putExtra(DLConstants.EXTRA_CLASS, className);
        dlIntent.putExtra(DLConstants.EXTRA_PACKAGE, packageName);
        dlIntent.setClass(mContext, activityClass);
        performStartActivityForResult(context, dlIntent, requestCode);
        return START_RESULT_SUCCESS;
    }

从代码中得出在调用插件apk中的Activity之前先要打开DLProxyActivity,两个主要方法 getPluginActivityFullPath()与getProxyActivityClass。getPluginActivityFullPath得到的是插件activity全类名,用于调转插件activity,className是DLProxyActivity。所以在调用startPluginActivityForResult是先跳转到DLProxyActivity中,然后在进行真正的跳转。

DLProxyActivity 是一个没有布局的空Activity主要负责Activity跳转的中转与生命周期关联,其中DLProxyActivity中含有两个重要的成员变量DLPlugin mRemoteActivity; DLProxyImpl impl = new DLProxyImpl(this);mRemoteActivity插件apk中实现DLPlugin接口的activity的引用,DLProxyImpl对象负责跳转,在DLProxyActivity的onCreate方法中通过impl.create(getIntent()),在这个方法中处理跳转的方法为launchTargetActivity();

protected void launchTargetActivity() {
        try {
            Class<?> localClass = getClassLoader().loadClass(mClass);
            Constructor<?> localConstructor = localClass.getConstructor(new Class[] {});
            Object instance = localConstructor.newInstance(new Object[] {});
            mPluginActivity = (DLPlugin) instance;
            ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager);
            Log.d(TAG, "instance = " + instance);
            // attach the proxy activity and plugin package to the mPluginActivity
            mPluginActivity.attach(mProxyActivity, mPluginPackage);

            Bundle bundle = new Bundle();
            bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL);
            mPluginActivity.onCreate(bundle);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

DLBasePluginActivity,插件apk中的Activity需要继承DLBasePluginActivity,在DLProxyActivity中launchTargetActivity方法中通过反射机制调起插件Activity,通过插件attach将传入代理activity,此后用反射的对象mPluginActivity调用onCreate方法传入bundle参数,而bundle的参数为DLConstants.FROM_EXTERNAL此时,在DLBasePluginActivity的onCreate方法中做了校验是内部调起还是外部调起。内部调起的逻辑如下:

if (mFrom == DLConstants.FROM_INTERNAL) {
            super.onCreate(savedInstanceState);
            mProxyActivity = this;
            that = mProxyActivity;
        }

如果是内部调起直接就是调用父类的onCreate,此时的代理mProxyActivity和that都是指向当前的对象this,如果是外部调起则是指向代理对象,不管是指向this还是代理对象都能当做Context来使用,都是Activity对象的引用。这就完成了插件apk调起流程。此后在插件apk中的Activity继承DLBasePluginActivity,在onCreate方法中中执行自己的单独逻辑,如布局的加载that.setContentView。而插件的生命周期在DLBasePluginActivity中已经处理好了,也是对内部调用做了校验,外部调用则是插件自己处理,内部校验直接调用super的生命周期。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值