程序员人生 网站导航

Android插件化开发,初入殿堂

栏目:互联网时间:2014-11-20 08:10:38

好久没有写博客了,这次准备写写我这几天的研究成果――Android插件化开发框架CJFrameForAndroid。

好久没有写博客了,这次准备写写我这几天的研究成果――Android插件化开发框架CJFrameForAndroid。

背景交代

    首先,你需要知道甚么是插件化开发。就拿最多见的QQ来讲,在第3个界面动态那里有个管理,点开后可以选择很多的增植功能,这里腾讯只放了1些网页利用,那末如果未来想加入1个打飞机游戏,要怎样做?让用户重新安装吗,这就是插件化开发所解决的问题。

    用1句话来概括插件式开发:你基本上可以理解为让1个apk不安装也能够被运行。只不过这个运行是有很多限制的运行,所以才叫插件否则就叫病毒了。其实在目前淘宝、百度、腾讯、等都有成熟的动态加载框架,包括apkplug,但是它们都是不开源的。

    说1下我认为这项技术的难点:1、1个未被安装的apk正常情况没法被运行;2、这个apk的资源没办法被援用;3、这个apk的界面就算被加载,也没办法与用户交互。

    最初查遍了资料,第1点好解决,在Android中有1个dexClassLoad类加载器,大家应当明白了,就是通过反射加载1个类来运行。第2点,网上有两种方法:可以将插件的资源放到sd卡上通过流的情势读取,不过也有人反对说用流读取会有问题,通配性太差;1种比较好的解决办法是将apk中的资源复制1份到当前app内,然后就能够加载了。这类办法是不错,但是用户每下载1次插件就复制1份,长此以往,对空间要求太高了,还有就是第3点也没办法解决。而第3点,在github上有1个叫AndroidDynamicLoader的项目,是通过用Fragment做为插件的表现情势,由于Fragment特殊性(既可以处理逻辑交互又具有与Activity相同的生命周期)。可是Fragment限制性太大了,太过碎片化使得使用起来复杂性太高。直到我找到了1篇360的官方博客,博客给了1种思路:通过代理/拜托模式设计的Application类去动态的改变1个apk所在的环境,实现动态加载的目的。抱着这类思路,我曾想自己去设计1个application类,但是技术有限,太复杂了,因而结合AndroidDynamicLoader的思路与360的思路,我自己设计了1个Activity去代理插件的Activity,因而就有了CJFrameForAndroid.

原理描写

首先解释几个名词:

APP项目:指要调用插件apk的那个已安装到用户手机上的利用。
插件项目:指没有被安装且希望借助已安装得手机上的项目运行的apk。
插件化:Activity继承自CJActivity,且与APP项目jar包冲突已解决的插件项目称为已被插件化。
Activity事务:在CJFrameForAndroid中,1个Activity的生命周期和交互事件统称为Activity的事务。
托管所:指插件中的1个委派/代理Activity,通过这个Activity去处理插件中Activity的全部事务,从而表现为就像插件中的Activity在运行1样。

CJFrameForAndroid的实现原理是通过类加载器,动态加载存在于SD卡上的apk包中的Activity。通过使用1个托管所,插件Activity全部事务(包括声明周期与交互事件)将交由托管所来处理,间接实现插件的运行。
1句话描写:CJFrameForAndroid中的托管所,复制了插件中的Activity,来替换插件中的Activity与用户交互。
看到这里你应当就明白了,全部框架最核心的部份就是这个托管所。这里给出CJFrameForAndroid中这个托管所的细节代码:

/** * 通过反射,获得到插件的资源访问器 */ protected void initResources() { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod( "addAssetPath", String.class); addAssetPath.invoke(assetManager, mDexPath); mAssetManager = assetManager; } catch (Exception e) { e.printStackTrace(); } Resources superRes = super.getResources(); mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); mTheme = mResources.newTheme(); mTheme.setTo(super.getTheme()); } /** * 启动插件的Activity */ protected void launchPluginActivity() { PackageInfo packageInfo = CJTool.getAppInfo(this, mDexPath); if ((packageInfo.activities != null) && (packageInfo.activities.length > 0)) { String activityName = packageInfo.activities[mAtyIndex].name; mClass = activityName; launchPluginActivity(mClass); } } /** * 启动指定的Activity * * @param className * 要启动的Activity完全类名 */ protected void launchPluginActivity(final String className) { try { Class<?> atyClass = getClassLoader().loadClass(className); Constructor<?> atyConstructor = atyClass .getConstructor(new Class[] {}); Object instance = atyConstructor.newInstance(new Object[] {}); setRemoteActivity(instance); mPluginAty.setProxy(this, mDexPath); Bundle bundle = new Bundle(); bundle.putInt(CJConfig.FROM, CJConfig.FROM_PROXY_APP); mPluginAty.onCreate(bundle); } catch (Exception e) { e.printStackTrace(); } } /** * 保存1份插件Activity对象 */ protected void setRemoteActivity(Object activity) { if (activity instanceof I_CJActivity) { mPluginAty = (I_CJActivity) activity; } else { throw new ClassCastException( "plugin activity must implements I_CJActivity"); } }

本框架目前仅仅是1个开发阶段,仅仅是实现了插件Activity的运行(原理上来讲,动态注册的广播也能够运行),而Service、contentProvider都没办法使用,这些都仍在研究中。
在未来的某1天,或许会将这个CJFrameForAndroid插件框架与KJFrameForAndroid快捷开发框架合并,组成1个更完善利用开发框架,对自己说:加油!

●目前仅支持Activity和Fragment,Service,动态注册的广播,Activity LaunchMode,注解式开发。
●APP项目和插件项目中,都需要使用到CJFrameForAndroid的jar包。
●在项目中必须加入托管所声明。
●在开发插件的时候,必须继承CJActivity;
●在插件的Activity中,1切使用this的部份必须使用that来替换;
●在插件Activity跳转时,推荐使用CJActivityUtils类来辅助跳转;
●在插件和APP两个工程中不能援用相同的jar包;


------分隔线----------------------------
------分隔线----------------------------

最新技术推荐