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

本质

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

更多相关文章

  1. android eclipse中经常遇到的the connection to adb is down and
  2. android开发框架(五)AIDL进程间通信机制
  3. 卸载SD卡对MediaServer的处理
  4. AIDL进程间传递复杂数据类型—AIDL传递android.os.Parcelable接
  5. Compile gdbserver for Android(gdb-7.1)
  6. 2018-7月Android试题整理
  7. Android(安卓)studio 插件推荐
  8. android PhoneGap源码详解
  9. Android(安卓)Studio文件分组插件

随机推荐

  1. Android 资源文件使用方法详解
  2. android跳转到系统的各项设置界面
  3. 熟练使用 Android Studio
  4. Android 采用HttpClient提交数据到服务器
  5. android TextView 加载html 显示图片并且
  6. Ubuntu 14.04 编译 Android(安卓)4.2.2 f
  7. Android弹出软键盘时如何将底部顶上去
  8. 初学Android,数据存储之使用SQLite数据库
  9. 【Android高级】Android系统以及Activity
  10. 关于Android这个名字。。。