一,原理(以浏览器为例)
本质:
1, 主程序直接加载插件的Extension类(这样他们就在同一个进程里了,就相当于主程序和Extension合为一个新的大app),通过接口来交互。
2, 插件Extension需要实现接口IExt
接口包括(部分):/* for browser activity */
public void onBrowserCreate(Bundle bundle);
public void onBrowserResume();
public void onBrowserPause();
public void onBrowserDestory();
3, 主程序需要实现IBrowserActivity接口
接口包括(部分):public boolean showToast(String pkgname, String content);
public boolean showProgressDialog(String pkgname,
PopupProgressDialogParams params);
public void dismissProgressDialog(String pkgname);
public IPopupProgressDialog getProgressDialog(String pkgname);
public boolean showPopupDialog(String pkgname, PopupDialogParams params);
public void dismissPopupDialog(String pkgname);
4, 通过一个类BrowserActivityImpl来执行,它持有主程序的Context,保存各个插件的各种数据(每个插件的Extension)。设计上BrowserActivity是单例模式,且为弱引用。
架构的示意图如下:
流程示例:
1, 主程序的onCreate()方法如何传给Addon
1-1 在主程序Activity的onCreate中,获得BrowserActivityImpl的实例
调用实例的traverseCreateActivityCallback()方法
1-2 在BrowserActivityImpl对象持有每个插件的Extension 的实例。
在此,会调用Extension中的onCreate()方法。
BrowserActivityImpl
2, Addon如何让主程序显示PopupDialog
1-1 初始化
主程序启动,Addon Extension获得BrowserActivityImpl的对象mBrowserActivityImpl
1-2 调用
Addon中,mBrowserActivity调用方法:
mBrowserActivityImpl.showProgressDialog(PKGNAME, mLoadingParams);
二,要点
1, 插件Extension类的加载
比较2.3和4.1的源码,Classloader和PathClassloader之间有所不同。
所以分别处理。4.1中,PathClassloader可以直接加载class,2.3中,通过load一个错误的class name,让PassClassloader获得的mDex文件的数据,然后通过mdex文件,加载类,相关源码,不给出,请自行查看。给出实现代码供参考:public class ExtClassLoader extends ClassLoader {
private static Field dexField;
private DexFile[] mDexs;
private final ClassLoader mLoader;
private static boolean ICE_OR_ABOVE = Build.VERSION.SDK_INT >= 14;
static {
if(!ICE_OR_ABOVE){
Class classLoader = PathClassLoader.class;
try {
dexField = classLoader.getDeclaredField("mDexs");
dexField.setAccessible(true);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
public ExtClassLoader(Context base,PackageInfo pi, String extPkg,
ClassLoader mainClassLoader) {
super(mainClassLoader);
Context extContext = null;
try {
extContext = base.createPackageContext(
extPkg,
Context.CONTEXT_INCLUDE_CODE
| Context.CONTEXT_IGNORE_SECURITY);
} catch (NameNotFoundException e1) {
e1.printStackTrace();
}
if(ICE_OR_ABOVE){
String codePath = extContext.getPackageCodePath();
String dir = pi.applicationInfo.nativeLibraryDir;
mLoader = new PathClassLoader(codePath, dir, mainClassLoader);
}else{
mLoader = extContext.getClassLoader();
}
}
@Override
protected Class> loadClass(String className, boolean arg1)
throws ClassNotFoundException {
if(ICE_OR_ABOVE)
return super.loadClass(className, arg1);
Class> c = findLoadedClass(className);
if (c != null)
return c;
try {
c = getParent().loadClass(className);
if (c == null)
c = findClass(className);
} catch (ClassNotFoundException e) {
c = findClass(className);
}
return c;
}
@Override
protected Class> findClass(String className)
throws ClassNotFoundException {
if(mLoader == null)
return null;
if(ICE_OR_ABOVE)
return mLoader.loadClass(className);
Class> c = null;
if (mLoader instanceof PathClassLoader)
c = loadClassFromDexFile(className);
else
c = mLoader.loadClass(className);
return c;
}
private Class> loadClassFromDexFile(String className) {
DexFile[] dfs = getDexFiles();
if (dfs != null) {
for (int i = 0; i < dfs.length; i++) {
if (dfs[i] != null) {
Class> c = dfs[i].loadClass(className, this);
if (c != null)
return c;
}
}
}
return null;
}
private DexFile[] getDexFiles() {
if (mDexs != null)
return mDexs;
try {
mLoader.loadClass("all.money.go.my.home");
} catch (Exception e) {
e.printStackTrace();
Log.e(ExtMgrConstants.LOGTAG, "getDexFiles " + e.toString());
}
try {
mDexs = (DexFile[]) dexField.get(mLoader);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return mDexs;
}
}
2, 进程的处理
根据以上的说明,即Addon的Extension类与主程序属于同一进程。但是Addon的其他类,则不属于主程序进程。
2-1 两个进程之间切换,需要sync
2-2 如果使用SharedPreference,需要使用多进程的构造方法。
2-3 友盟统计,如果要单独统计为Addon的数据。需要从Extension单起一个service来统计,因为Extension在主程序的进程中,统计的数据会到主程序。
三:项目
插件是特定应用(例如浏览器)提供的开放api的实现。开发者主要关注的是功能, 交互和UI。当然,需要注意第二部分给出的要点。
我参与了两个插件的开发,中间隔了很长的时间,会觉得再次进入需要一些熟悉的时间。觉得可以优化的地方,就是给出开发步骤文档。一些记忆性质的code flow可以避免。开发效率可以提高。