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是单例模式,且为弱引用。
架构的示意图如下:
流程示例:
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可以避免。开发效率可以提高。
更多相关文章
- android eclipse中经常遇到的the connection to adb is down and
- android开发框架(五)AIDL进程间通信机制
- 卸载SD卡对MediaServer的处理
- AIDL进程间传递复杂数据类型—AIDL传递android.os.Parcelable接
- Compile gdbserver for Android(gdb-7.1)
- 2018-7月Android试题整理
- Android(安卓)studio 插件推荐
- android PhoneGap源码详解
- Android(安卓)Studio文件分组插件