Android插件化学习之路(八)之DynamicLoadApk 源码解析(上)

DynamicLoadApk是插件化比较优秀的框架,为了更好的理解插件化,我们来分析一下DynamicLoadApk的源码。

Freedom框架,我个人手写的0反射插件化框架

1.核心概念

(1) 宿主:主 App,可以加载插件,也称 Host。
(2) 插件:插件 App,被宿主加载的 App,也称 Plugin,可以是跟普通 App 一样的 Apk 文件。
(3) 组件:指 Android 中的Activity、Service、BroadcastReceiver、ContentProvider,目前 DL 支持Activity、Service以及动态的BroadcastReceiver。
(4) 插件组件:插件中的组件。
(5) 代理组件:在宿主的 Manifest 中注册,启动插件组件时首先被启动的组件。目前包括 DLProxyActivity(代理 Activity)、DLProxyFragmentActivity(代理 FragmentActivity)、DLProxyService(代理 Service)。
(6) Base 组件:插件组件的基类,目前包括 DLBasePluginActivity(插件 Activity 的基类)、DLBasePluginFragmentActivity(插件 FragmentActivity 的基类)、DLBasePluginService(插件 Service 的基类)。

DynamicLoadApk 原理的核心思想可以总结为两个字:代理。通过在 Manifest 中注册代理组件,当启动插件组件时首先启动一个代理组件,然后通过这个代理组件来构建、启动插件组件。

2.总体设计

这里写图片描述

上面是 DynamicLoadApk 的总体设计图,DynamicLoadApk 主要分为四大模块:
(1) DLPluginManager
插件管理模块,负责插件的加载、管理以及启动插件组件。

(2) Proxy
代理组件模块,目前包括 DLProxyActivity(代理 Activity)、DLProxyFragmentActivity(代理 FragmentActivity)、DLProxyService(代理 Service)。

(3) Proxy Impl
代理组件公用逻辑模块,与(2)中的 Proxy 不同的是,这部分并不是一个组件,而是负责构建、加载插件组件的管理器。这些 Proxy Impl 通过反射得到插件组件,然后将插件与 Proxy 组件建立关联,最后调用插件组件的 onCreate 函数进行启动。

(4) Base Plugin
插件组件的基类模块,目前包括 DLBasePluginActivity(插件 Activity 的基类)、DLBasePluginFragmentActivity(插件 FragmentActivity 的基类)、DLBasePluginService(插件 Service 的基类)。

3.流程图

这里写图片描述

上面是调用插件 Activity 的流程图,其他组件调用流程类似。
(1) 首先通过 DLPluginManager 的 loadApk 函数加载插件,这步每个插件只需调用一次。
(2) 通过 DLPluginManager 的 startPluginActivity 函数启动代理 Activity。
(3) 代理 Activity 启动过程中构建、启动插件 Activity。

4.类详细分析

4.1 DLPluginManager.java
DynamicLoadApk 框架的核心类,主要功能包括:
(1) 插件的加载和管理;
(2) 启动插件的组件,目前包括 Activity、Service。
主要属性:
mNativeLibDir为插件 Native Library 拷贝到宿主中后的存放目录路径。在 getInstance(Context context)中进行赋值

private String mNativeLibDir = null;

mPackagesHolder HashMap,key 为包名,value 为表示插件信息的DLPluginPackage,存储已经加载过的插件信息。

private final HashMap<String, DLPluginPackage> mPackagesHolder = new HashMap<String, DLPluginPackage>();

主要函数:
(1) getInstance(Context context)
获取 DLPluginManager 对象的单例。
在私有构造函数中将mNativeLibDir变量赋值为宿主 App 应用程序数据目录下名为pluginlib子目录的全路径。

private DLPluginManager(Context context) {
    mContext = context.getApplicationContext();
    mNativeLibDir = mContext.getDir("pluginlib", Context.MODE_PRIVATE).getAbsolutePath();
}

(2) loadApk(String dexPath)
加载插件。参数 dexPath 为插件的文件路径。
这个函数直接调用 loadApk(final String dexPath, boolean hasSoLib)。

/**
 * Load a apk. Before start a plugin Activity, we should do this first.<br/>
 * NOTE : will only be called by host apk.
 * 
 * @param dexPath
 */
public DLPluginPackage loadApk(String dexPath) {
    // when loadApk is called by host apk, we assume that plugin is invoked
    // by host.
    return loadApk(dexPath, true);
}

(3) loadApk(final String dexPath, boolean hasSoLib)
加载插件 Apk。参数 dexPath 为插件的文件路径,hasSoLib 表示插件是否含有 so 库。
**注意:**在启动插件的组件前,必须先调用上面两个函数之一加载插件,并且只能在宿主中调用。
流程图如下:
这里写图片描述

/**
 * @param dexPath
 *            plugin path
 * @param hasSoLib
 *            whether exist so lib in plugin
 * @return
 */
public DLPluginPackage loadApk(final String dexPath, boolean hasSoLib) {
    mFrom = DLConstants.FROM_EXTERNAL;

    PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath,
            PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
    if (packageInfo == null) {
        return null;
    }

    DLPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath);
    if (hasSoLib) {
        copySoLib(dexPath);
    }

    return pluginPackage;
}

loadApk 函数调用 preparePluginEnv 函数加载插件,图中虚线框为 preparePluginEnv 的流程图。

(4) preparePluginEnv(PackageInfo packageInfo, String dexPath)
加载插件及其资源。流程图如上图。
调用createDexClassLoader(…)、createAssetManager(…)、createResources(…)函数完成相应初始化部分。

/**
 * prepare plugin runtime env, has DexClassLoader, Resources, and so on.
 * 
 * @param packageInfo
 * @param dexPath
 * @return
 */
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;
}

(5) createDexClassLoader(String dexPath)
利用DexClassLoader加载插件,DexClassLoader 初始化函数如下:

public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)


其中dexPath为插件的路径。
optimizedDirectory为宿主内dex存放路径。这里将路径设置为当前 App 应用程序数据目录下名为dex的子目录中。
libraryPath为 Native Library 存放的路径。这里将路径设置为mNativeLibDir属性,其在getInstance(Context)函数中已经初始化。

parent父 ClassLoader,ClassLoader 采用双亲委托模式查找类,具体加载方式可见 Android插件化学习之路(二)之ClassLoader完全解析


private DexClassLoader createDexClassLoader(String dexPath) {
    File dexOutputDir = mContext.getDir("dex", Context.MODE_PRIVATE);
    dexOutputPath = dexOutputDir.getAbsolutePath();
    DexClassLoader loader = new DexClassLoader(dexPath, dexOutputPath, mNativeLibDir, mContext.getClassLoader());
    return loader;
}

(6) createAssetManager(String dexPath)

创建 AssetManager,加载插件资源。
在 Android 中,资源是通过 R.java 中的 id 来调用访问的。但是实现插件化之后,宿主是无法通过 R 文件访问插件的资源,所以这里使用反射来生成属于插件的AssetManager,并利用addAssetPath函数加载插件资源。

private AssetManager createAssetManager(String dexPath) {
    try {
        AssetManager assetManager = AssetManager.class.newInstance();
        Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
        addAssetPath.invoke(assetManager, dexPath);
        return assetManager;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }

}

AssetManager 的无参构造函数以及addAssetPath函数都被hide了,通过反射调用。

(7) createResources(AssetManager assetManager)
利用AssetManager中已经加载的资源创建Resources,代理组件中会从这个Resources中读取资源。

private Resources createResources(AssetManager assetManager) {
    Resources superRes = mContext.getResources();
    Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
    return resources;
}

(8) copySoLib(String dexPath)
调用SoLibManager拷贝 so 库到 Native Library 目录。


/**
 * copy .so file to pluginlib dir.
 * 
 * @param dexPath
 * @param hasSoLib
 */
private void copySoLib(String dexPath) {
    // TODO: copy so lib async will lead to bugs maybe, waiting for
    // resolved later.

    // TODO : use wait and signal is ok ? that means when copying the
    // .so files, the main thread will enter waiting status, when the
    // copy is done, send a signal to the main thread.
    // new Thread(new CopySoRunnable(dexPath)).start();

    SoLibManager.getSoLoader().copyPluginSoLib(mContext, dexPath, mNativeLibDir);
}

(9) startPluginActivity(Context context, DLIntent dlIntent)
启动插件 Activity,会直接调用startPluginActivityForResult(…)函数。
插件自己内部 Activity 启动依然是调用Context#startActivity(…)方法。

/**
 * {@link #startPluginActivityForResult(Activity, DLIntent, int)}
 */
public int startPluginActivity(Context context, DLIntent dlIntent) {
    return startPluginActivityForResult(context, dlIntent, -1);
}

10) startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode)
启动插件 Activity,流程图如下:
这里写图片描述

/**
 * @param context
 * @param dlIntent
 * @param requestCode
 * @return One of below: {@link #START_RESULT_SUCCESS}
 *         {@link #START_RESULT_NO_PKG} {@link #START_RESULT_NO_CLASS}
 *         {@link #START_RESULT_TYPE_ERROR}
 */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode) {
    //内部调用,原生方式启动Activity
    if (mFrom == DLConstants.FROM_INTERNAL) {
        dlIntent.setClassName(context, dlIntent.getPluginClass());
        performStartActivityForResult(context, dlIntent, requestCode);
        return DLPluginManager.START_RESULT_SUCCESS;
    }
    //拿出Intent中的packageName
    String packageName = dlIntent.getPluginPackage();
    if (TextUtils.isEmpty(packageName)) {
        throw new NullPointerException("disallow null packageName.");
    }
    //在Map集合mPackagesHolder中根据packageName拿出插件信息DLPluginPackage
    DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
    //判断插件是否存在于mPackagesHolder中
    if (pluginPackage == null) {
        return START_RESULT_NO_PKG;
    }
    //根据pluginPackage得到待启动Activity全路径
    final String className = getPluginActivityFullPath(dlIntent, pluginPackage);
    //通过反射加载这个待启动Activity类
    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.
    //得到启动Activity类的代理类
    Class<? extends Activity> activityClass = getProxyActivityClass(clazz);
    if (activityClass == null) {
        return START_RESULT_TYPE_ERROR;
    }

    // put extra data
    //设置dlIntent的相关参数,将代理类设置进dlIntent中,还要设置Activity全路径,插件的包名packageName
    dlIntent.putExtra(DLConstants.EXTRA_CLASS, className);
    dlIntent.putExtra(DLConstants.EXTRA_PACKAGE, packageName);
    dlIntent.setClass(mContext, activityClass);
    performStartActivityForResult(context, dlIntent, requestCode);
    return START_RESULT_SUCCESS;
}

再看一下里面一些方法的具体实现:
启动Activity的方法

private void performStartActivityForResult(Context context, DLIntent dlIntent, int requestCode) {
    Log.d(TAG, "launch " + dlIntent.getPluginClass());
    if (context instanceof Activity) {
        ((Activity) context).startActivityForResult(dlIntent, requestCode);
    } else {
        context.startActivity(dlIntent);
    }
}

根据pluginPackage得到待启动Activity全路径的方法

private String getPluginActivityFullPath(DLIntent dlIntent, DLPluginPackage pluginPackage) {
    String className = dlIntent.getPluginClass();
    className = (className == null ? pluginPackage.defaultActivity : className);
    if (className.startsWith(".")) {
        className = dlIntent.getPluginPackage() + className;
    }
    return className;
}

通过反射加载待启动Activity类

// zhangjie1980 重命名 loadPluginActivityClass -> loadPluginClass
private Class<?> loadPluginClass(ClassLoader classLoader, String className) {
    Class<?> clazz = null;
    try {
        clazz = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    return clazz;
}

得到启动Activity类的代理类,内部主要是根据这个Activity继承自DLBasePluginActivity还是DLBasePluginFragmentActivity来获取相应的代理

/**
 * get the proxy activity class, the proxy activity will delegate the plugin
 * activity
 * 
 * @param clazz
 *            target activity's class
 * @return
 */
private Class<? extends Activity> getProxyActivityClass(Class<?> clazz) {
    Class<? extends Activity> activityClass = null;
    if (DLBasePluginActivity.class.isAssignableFrom(clazz)) {
        activityClass = DLProxyActivity.class;
    } else if (DLBasePluginFragmentActivity.class.isAssignableFrom(clazz)) {
        activityClass = DLProxyFragmentActivity.class;
    }

    return activityClass;
}


(11) startPluginService(final Context context, final DLIntent dlIntent)
启动插件 Service。
主要逻辑在函数fetchProxyServiceClass(…)中,流程与startPluginActivity(…)类似,只是换成了回调的方式,在各种条件成立后调用原生方式启动代理 Service,不再赘述。

public int startPluginService(final Context context, final DLIntent dlIntent) {
    if (mFrom == DLConstants.FROM_INTERNAL) {
        dlIntent.setClassName(context, dlIntent.getPluginClass());
        context.startService(dlIntent);
        return DLPluginManager.START_RESULT_SUCCESS;
    }

    fetchProxyServiceClass(dlIntent, new OnFetchProxyServiceClass() {
        @Override
        public void onFetch(int result, Class<? extends Service> proxyServiceClass) {
            // TODO Auto-generated method stub
            if (result == START_RESULT_SUCCESS) {
                dlIntent.setClass(context, proxyServiceClass);
                // start代理Service
                context.startService(dlIntent);
            }
            mResult = result;
        }
    });
    
    return mResult;
}

(12) stopPluginService(final Context context, final DLIntent dlIntent)

public int stopPluginService(final Context context, final DLIntent dlIntent) {
    if (mFrom == DLConstants.FROM_INTERNAL) {
        dlIntent.setClassName(context, dlIntent.getPluginClass());
        context.stopService(dlIntent);
        return DLPluginManager.START_RESULT_SUCCESS;
    }
    
    fetchProxyServiceClass(dlIntent, new OnFetchProxyServiceClass() {
        @Override
        public void onFetch(int result, Class<? extends Service> proxyServiceClass) {
            // TODO Auto-generated method stub
            if (result == START_RESULT_SUCCESS) {
                dlIntent.setClass(context, proxyServiceClass);
                // stop代理Service
                context.stopService(dlIntent);
            }
            mResult = result;
        }
    });
    
    return mResult;
}

(13) bindPluginService(…) unBindPluginService(…)
bind 或是 unBind 插件 Service。逻辑与startPluginService(…)类似,不再赘述。

public int bindPluginService(final Context context, final DLIntent dlIntent, final ServiceConnection conn,
        final int flags) {
    if (mFrom == DLConstants.FROM_INTERNAL) {
        dlIntent.setClassName(context, dlIntent.getPluginClass());
        context.bindService(dlIntent, conn, flags);
        return DLPluginManager.START_RESULT_SUCCESS;
    }

    fetchProxyServiceClass(dlIntent, new OnFetchProxyServiceClass() {
        @Override
        public void onFetch(int result, Class<? extends Service> proxyServiceClass) {
            // TODO Auto-generated method stub
            if (result == START_RESULT_SUCCESS) {
       dlIntent.setClass(context, proxyServiceClass);
                // Bind代理Service
                context.bindService(dlIntent, conn, flags);
            }
            mResult = result;
        }
    });

    return mResult;
}

public int unBindPluginService(final Context context, DLIntent dlIntent, final ServiceConnection conn) {
    if (mFrom == DLConstants.FROM_INTERNAL) {
        context.unbindService(conn);
        return DLPluginManager.START_RESULT_SUCCESS;
    }

    fetchProxyServiceClass(dlIntent, new OnFetchProxyServiceClass() {
        @Override
        public void onFetch(int result, Class<? extends Service> proxyServiceClass) {
            // TODO Auto-generated method stub
            if (result == START_RESULT_SUCCESS) {
                // unBind代理Service
                context.unbindService(conn);
            }
            mResult = result;
        }
    });
    return mResult;

}

由于篇幅原因,剩下的源码分析,下一篇博客带来。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的代码家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值