我是靠谱客的博主 魁梧航空,最近开发中收集的这篇文章主要介绍插件化之Dynamicloadapk分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在进行插件化了解之前,需要先了解ClassLoader的相关概念

大家应该都知道java中的类加载机制,采用的是双亲委派机制,即链式调用高层次的classLoader即 parentClassLoader去加载,

如果高层次的classLoader无法加载该类,再由当前的ClassLoader去加载。

在android中,插件化技术相关的classLoader分别是PathClassLoader 以及 DexClassLoader,  前者只能加载当前已安装的apk的class,后者用来加载插件apk的class.


接下来我们来分析一下DynamicLoadApk的设计和实现

先了解这个框架中的一些角色: 

DlPluginManger: 用于loadApk以及启动Activity ,Service

DlPlugin : interface定义了activity相关同名接口,所有的pluginActivity均需要implement it

DlProxyActivity:作为 PluginActivity的代理类,用于承载界面展示, 生命周期的控制,activity 的调用,如setContentView,finish

DlPluginBaseActivity: 它就是一个PluginActivity,用于提供界面展示的布局,以及相关逻辑控制

DlProxyImpl: 他是DlProxyActivity和DlPluginBaseActivity建立通讯的桥梁,通过两个attach,让DlProxyActivity和DlPluginBaseActivity分别持用对方的引用,实现通讯

APK 角色划分: 宿主apk(已安装在手机上的) 和 插件apk(没有被安装) 

由于宿主apk 已被安装,下面我们先来分析一下插件apk的加载过程,这个过程通过DlPluginManager的loadapk-->preparePluginEnv 去实现,

private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath) {

    DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName);
    if (pluginPackage != null) {
        return pluginPackage ;
    }
    DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
    AssetManager assetManager = createAssetManager(dexPath);
    Resources resources = createResources(assetManager);
    // create pluginPackage
    pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);
    mPackagesHolder.put(packageInfo.packageName, pluginPackage);
    return pluginPackage;
}

从上面代码中可知:准备工作主要做了几件事:

1 判断是否加载过这个插件,没有加载则继续向下执行,

2 做了三步初始化: 

1) 创建dexClassLoader,  用于加载插件apk中的java类,

2)通过反射调用assetManager的addAssetPath,将插件apk的资源路径加载到宿主apk的资源池中

3)用当前的assetsmanager 创建resource,用于访问资源

到此  插件apk的初始化完成。接下来,我们来分析一下 从宿主apk中启动插件apk的activity

DlpluginManager的startPluginActivityForResult() 这个方法是启动插件Apk中Activity的入口,代码如下:

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
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;
}
 

接下来我们分析一下这个函数:

if (mFrom == FROM_EXTERNAL) 代表被启动的target Activity在插件apk中,否则 target Activity在宿主apk中。

开始的判断主要是为了判断target activity所在的包名是否有效, 然后通过初始化时的DexClassLoader去加载target Activity,

然后获取target Activity的proxyActivity, DLProxyActivity 为普通Activity的代理类,DLProxyFragmentActivity为fragmentActivity的代理类,然后启动proxyActivity,并将targetActivity的实例传递给proxyActivity

从而我们得出一个结论,所有的被启动的插件apk的activity 都被宿主的proxyactivity 代理,通过proxyActivity 来显示targetActivity并控制其生命周期

proxyActivity 与 targetActivity的通信桥梁是DlProxyImpl,查看DLProxyActivity代码可知,它重写了大量的Activity的函数,本身不做任何事情,完全交给 ProxyImpl 和 RemoteActivity去实现。 

先看ProxyActivity的onCreate,它直接调用了DLProxyImpl的onCreate,代码如下:

public void onCreate(Intent intent) {

    // set the extra's class loader
    intent.setExtrasClassLoader(DLConfigs.sPluginClassloader);

    mPackageName = intent.getStringExtra(DLConstants.EXTRA_PACKAGE);
    mClass = intent.getStringExtra(DLConstants.EXTRA_CLASS);
    Log.d(TAG, "mClass=" + mClass + " mPackageName=" + mPackageName);

    mPluginManager = DLPluginManager.getInstance(mProxyActivity);
    mPluginPackage = mPluginManager.getPackage(mPackageName);
    mAssetManager = mPluginPackage.assetManager;
    mResources = mPluginPackage.resources;

    initializeActivityInfo();
    handleActivityInfo();
    launchTargetActivity();
}

前面是获取插件apk的一些基本信息,并设置theme,最后启动target activity

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
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();
    }
}
 

分析一下这个函数,首先加载target activity实例,然后将pluginActivity和proxyactivity  attatch, 在proxyActivity中的mRemoteActivity就是PluginActivity,最后调用PluginActivity.onCreate(),其他生命周期的代理也是如此,在proxyActivity中通过调用remoteActivity(这个就是pluginactivity)的生命周期。如下所示:

@Override
protected void onResume() {
    mRemoteActivity.onResume();
    super.onResume();
}

@Override
protected void onPause() {
    mRemoteActivity.onPause();
    super.onPause();
}

@Override
protected void onStop() {
    mRemoteActivity.onStop();
    super.onStop();
}

@Override
protected void onDestroy() {
    mRemoteActivity.onDestroy();
    super.onDestroy();
}


接下来我们来分析PluginActivity,这里有DlPlugin接口,定义了Activity相关的大部分生命周期,所有的pluginActivity都实现了这个接口,以DlBasePluginActivity为例。

DlBasePluginActivity第一个被调用的函数是attach,在上面的 DlProxyImpl的oncreate中调用的

mPluginActivity.attach(mProxyActivity, mPluginPackage);
 

看一下DlBasePluginActivity的attach函数实现

public void attach(Activity proxyActivity, DLPluginPackage pluginPackage) {
    Log.d(TAG, "attach: proxyActivity= " + proxyActivity);
    mProxyActivity = (Activity) proxyActivity;
    that = mProxyActivity;
    mPluginPackage = pluginPackage;
}

这里主要是获取了proxyactivity的对象that, 因为pluginActivity如果是插件apk中的activity,它没有被安装,本身不具备activity的特性, 只能通过ProxyActivity展示,所以更多的看来,当PluginActivity在插件apk中,其作用更多是提供数据(界面布局),逻辑控制等,ProxyActivity来承载它的展示。 如下所示:

@Override
public void setContentView(View view) {
    if (mFrom == DLConstants.FROM_INTERNAL) {
        super.setContentView(view);
    } else {
        mProxyActivity.setContentView(view);
    }
}

@Override
public void setContentView(View view, LayoutParams params) {
    if (mFrom == DLConstants.FROM_INTERNAL) {
        super.setContentView(view, params);
    } else {
        mProxyActivity.setContentView(view, params);
    }
}

当pluginActivity在插件apk中即(mFrom != DlConstants.From_INTERNAL), 通过调用ProxyActivity的setContentView才能加载布局,本身不能加载。

区分this 和 that,  当pluginActivity在插件中,that 代表了 ProxyActivity, 当其在宿主apk中,that == this

总结: DynamicLoadApk这个开源库的实现是采用代理的方式

缺点: 不支持插件activity的launchmode

 此博文中可能存在很多缺点和漏洞,希望大家积极的提出,一起进步

最后

以上就是魁梧航空为你收集整理的插件化之Dynamicloadapk分析的全部内容,希望文章能够帮你解决插件化之Dynamicloadapk分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部