android MDE工程师,Android Application Addon(插件) 架构及管理

一,原理(以浏览器为例)

本质:

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是单例模式,且为弱引用。

架构的示意图如下:

d6485241511399d4f1a8bbf373554ae7.png

流程示例:

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可以避免。开发效率可以提高。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值