*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 。
我们通过前4篇的分解,分别将插件化设计到的知识点全部梳理了一遍,如果没有看过的,建议先看前面4篇
5. Android插件化完美实现代码资源加载及原理讲解 附可运行demo
6. 插件化资源的使用及动态加载 附demo
好了上面介绍了之前准备的知识点后今天我们做一个真正的可运行的启动插件demo,要知道一个插件可能是随时从网上下载下来的,那么也就是说其实这个apk不会被安装,那么如果不被安装,怎么能被加载呢,
又如何管理插件中四大组件的生命周期呢,没有生命周期的四大组件是没有意义的。而且Activity是必须要在AndroidManifest中注册的,不注册就会抛出异常,那么怎么能绕过这个限制呢,还有,一个apk中肯定会用过各种资源,那么又该如何动态的加载资源呢。下面我们就带着这些问问一一的来解决,实现插件化,或者或是模块化。
先来看一下最终的运行结果
分析思路:
代码的动态加载:
apk被安装之后,apk的文件代码以及资源会被系统存放在固定的目录比如/data/app/package_name/base-1.apk)中,系统在进行类加载的时候,会自动去这一个或者几个特定的路径来寻找这个类
但是要知道插件apk是不会被安装的,那么系统也就不会讲我们的代理及资源存在在这个目录下,换句话说系统并不知道我们插件apk中的任何信息,也就根本不可能加载我们插件中的类。我们之前分析过应用的启动过程,其实就是启动了我们的主Activity,然后在ActivityThread的performLaunchActivity方法中创建的Activity对象并回调了attch和onCreate方法,我们再来看一下创建Activity对象时的代码(没看过应用启动过程的建议先看看 插件化知识详细分解及原理 之应用的启动过程),
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
系统通过待启动的Activity的类名className,然后使用ClassLoader对象cl把这个类加载,最后使用反射创建了这个Activity类的实例对象。我们看一下这个cl对象是通过r.packageInfo.getClassLoader()被赋值的,这个r.packageInfo是一个LoadedApk类型的对象,我们看看这个LoadedApk是什么东西。
/**
* Local state maintained about a currently loaded .apk.
* @hide
*/
public final class LoadedApk {
private static final String TAG = "LoadedApk";
private final ActivityThread mActivityThread;
private ApplicationInfo mApplicationInfo;
final String mPackageName;
private final String mAppDir;
private final String mResDir;
private final String[] mSplitAppDirs;
private final String[] mSplitResDirs;
private final String[] mOverlayDirs;
private final String[] mSharedLibraries;
private final String mDataDir;
private final String mLibDir;
private final File mDataDirFile;
private final ClassLoader mBaseClassLoader;
private final boolean mSecurityViolation;
private final boolean mIncludeCode;
private final boolean mRegisterPackage;
private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
Resources mResources;
private ClassLoader mClassLoader;
private Application mApplication;
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
= new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
。。。
}