一、解析插件Apk
DLPluginManager pluginManager = DLPluginManager.getInstance(BaseApplication.getInstance());
DLPluginPackage pluginPackage = pluginManager.loadApk(pluginApkPath);
DLIntent intent = new DLIntent(pluginPackage.packageName, pluginPackage.defaultActivity);
pluginManager.startPluginActivity(this, intent);
1、loadApk方法
public DLPluginPackage loadApk(final String dexPath, boolean hasSoLib){
mFrom = DLConstants.From_external;
int packageInfoFlags = PackageManager.Get_activities | PackageManager.Get_services;
PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath, packageInfoFlags);
if(packageInfo == null) return null;
DLPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath);
if(hasSoLib){
copySoLib(dexPath);
}
return pluginPackage;
}
2、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);
mPackageHolder.put(packageInfo.packageName, pluginPackage);
return pluginPackage;
}
private DexClassLoader createDexClassLoader(String dexPath){
File dexOutputDir = mContext.getDir("dex", Context.Mode_private);
dexOutputPath = dexOutputDir.getAbsolutePath();
DexClassLoader loader = new DexClassLoader(
dexPath,
dexOutputPaht,
mNativeLibDir,
mContext.getClassLoader()
);
return loader;
}
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){}
}
private Resources createResources(AssetManager assetManager){
Resources superRes = mContext.getResources();
Resources resources = new Resources(
assetManager,
superRes.getDisplayMetrics(),
superRes.getConfiguration()
);
return resources;
}
二、运行插件Apk
启动代理的Activity,分别可以:在DLPluginManager里调用startPluginActivityForResult(一般宿主使用),还可以在DLPluginActivity里使用startPluginActivity(在插件里使用)。
DLIntent intent = new DLIntent(getPackageName(), SecondActivity.class);
startPluginActivity(intent);
上面是调用Activity方法,再往下看:
public int startPluginActivity(DLIntent dlIntent){
return startPluginActivityForResult(dlIntent, -1);
}
/** @return may be
{@link #Start_result_success},
{@link #Start_result_no_pkg},
{@link #Start_result_no_class},
{@link #Start_result_type_error} */
public int startPluginActivityForResult(DLIntent dlIntent, int requestCode){
if(mFrom == DLConstants.From_external){
if(dlIntent.getPluginPackage() == null){
dlIntent.setPluginPackage(mPluginPackage.packageName);
}
}
return mPluginManager.startPluginActivityForResult(that, dlIntent, requestCode);
}
之后交给PluginManager启动Activity:
public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode){
if(mFrom == DLConstants.From_internal){
dlIntent.setClassName(context, dlIntent.getPluginClass());
preformStartActivityForResult(context, dlIntent, requestCode);
return DLPluginManager.Start_result_success;
}
String packageName = dlIntent.getPluginPackage();
if(TextUtils.isEmpty(packageName)){
throw new NullPointerException("disallow null packageName.");
}
DLPluginPackage pluginPackage = mPackageHolder.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;
}
Class<? extends Activity> activityClass = getProxyActivityClass(clazz);
if(activityClass == null){
return Start_result_type_error;
}
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的全称,然后通过函数loadPluginClass()利用反射获取插件实例activityClass
函数loadPluginClass:
private Class<?> loadPluginClass(ClassLoader classLoader, String className){
Class<?> clazz = null;
try{
clazz = Class.forName(className, true, classLoader);
}catch(ClassNotFoundException e){}
return clazz;
}
通过函数getProxyActivityClass(),根据插件实例获取它对应在宿主里对应的代理类:
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;
}else if(Activity.class.isAssignableFrom(clazz)){
activityClass = Activity.class;
}
return activityClass;
}
当这一步基本上完成偷天换柱的任务,表面上要启动的是插件类,其实启动的是一个宿主代理类。
在把插件包名和要启动的插件类名放到Intent中,传递一个代理类,最后直接启动代理Activity就好了。
private void performStartActivityForResult(Context context, DLIntent dlIntent, int requestCode){
if(context instanceof Activity){
((Activity)context).startActivityForResult(dlIntent, requestCode);
}else{
context.startActivity(dlIntent);
}
}
三、通过代理类运行插件类
在这里分析DLProxyActivity,经前面分析,已经启动了代理类,当然就执行到代理类的onCreate了:
protected DLPlugin mRemoteActivity;
private DLProxyImpl impl = new DLProxyImpl(this);
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
impl.onCreate(getIntent());
}
接着流程就跑到DLProxyImpl类的onCreate()方法中:
public void onCreate(Intent intent){
intent.setExtrasClassLoader(DLConfigs.sPluginClassLoader);
mPackageName = intent.getStringExtra(DLConstants.Extra_package);
mClass = intent.getStringExtra(DLConstants.Extra_class);
mPluginManager = DLPluginManager.getInstance(mProxyActivity);
mPluginPackage = mPluginManager.getPackage(mPackageName);
mAssetManager = mPluginPackage.assetManager;
mResources = mPluginPackage.resources;
initializeActivityInfo();
handleActivityInfo();
launchTargetActivity();
}
1、在initializeActivityInfo()之前是获取intent传递过来的插件包名、插件类,并根据它们获取插件信息PluginPackage
2、PluginPackage里面存放了插件的resource和assetManager
3、initializeActivityInfo()和handleActivityInfo()主要是进行一些主题处理
4、重点在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);
mPluginActivity.attach(mProxyActivity, mPluginPackage);
Bundle bundle = new Bundle();
bundle.putInt(DLConstants.From, DLConstants.From_external);
mPluginActivity.onCreate(bundle);
}catch(Exception e){}
}
创建好了代理类对象和插件类对象,就需要通过两个attach()方法将它们绑定在一起。
第一个是代理类的attach()方法:
public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager){
mRemoteActivity = remoteActivity;
}
第二个是插件类的attach()方法:
public void attach(Activity proxyActivity, DLPluginPackage pluginPackage){
mProxyActivity = proxyActivity;
that = mProxyActivity;
mPluginPackage = pluginPackage;
}
可以看到插件类里的that被赋值了代理类对象,所以在编写插件代码的时候this的功能将被废弃,不能使用this,必须使用that