zoukankan      html  css  js  c++  java
  • Android插件化开发,初入殿堂

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

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

    背景交代

        首先。你须要知道什么是插件化开发。

    就拿最常见的QQ来说,在第三个界面动态那里有个管理,点开后能够选择非常多的增植功能,这里腾讯仅仅放了一些网页应用。那么假设未来想增加一个打飞机游戏,要怎么做?让用户又一次安装吗。这就是插件化开发所解决的问题。

        用一句话来概括插件式开发:你基本上能够理解为让一个apk不安装也能够被执行。

    仅仅只是这个执行是有非常多限制的执行,所以才叫插件否则就叫病毒了。

    事实上在眼下淘宝、百度、腾讯、等都有成熟的动态载入框架,包含apkplug,可是它们都是不开源的。

        说一下我觉得这项技术的难点:1、一个未被安装的apk正常情况无法被执行。2、这个apk的资源没办法被引用;3、这个apk的界面就算被载入,也没办法与用户交互。

        最初查遍了资料,第一点好解决,在Android中有一个dexClassLoad类载入器,大家应该明确了,就是通过反射载入一个类来执行。第二点,网上有两种方法:能够将插件的资源放到sd卡上通过流的形式读取,只是也有人反对说用流读取会有问题,通配性太差;一种比較好的解决的方法是将apk中的资源复制一份到当前app内。然后就能够载入了。

    这样的办法是不错,可是用户每下载一次插件就复制一份。久而久之,对空间要求太高了。还有就是第三点也没办法解决。而第三点,在github上有一个叫AndroidDynamicLoader的项目,是通过用Fragment做为插件的表现形式,因为Fragment特殊性(既能够处理逻辑交互又具备与Activity同样的生命周期)。可是Fragment限制性太大了,太过碎片化使得使用起来复杂性过高。直到我找到了一篇360的官方博客。博客给了一种思路:通过代理/托付模式设计的Application类去动态的改变一个apk所在的环境。实现动态载入的目的。抱着这样的思路,我曾想自己去设计一个application类,可是技术有限,太复杂了,于是结合AndroidDynamicLoader的思路与360的思路。我自己设计了一个Activity去代理插件的Activity。于是就有了CJFrameForAndroid.

    原理描写叙述

    首先解释几个名词:

    APP项目:指要调用插件apk的那个已经安装到用户手机上的应用。
    插件项目:指没有被安装且希望借助已经安装到手机上的项目执行的apk。


    插件化:Activity继承自CJActivity,且与APP项目jar包冲突已经解决的插件项目称为已经被插件化。
    Activity事务:在CJFrameForAndroid中,一个Activity的生命周期以及交互事件统称为Activity的事务。


    托管所:指插件中的一个委派/代理Activity,通过这个Activity去处理插件中Activity的所有事务,从而表现为就像插件中的Activity在执行一样。

    CJFrameForAndroid的实现原理是通过类载入器,动态载入存在于SD卡上的apk包中的Activity。通过使用一个托管所。插件Activity所有事务(包含声明周期与交互事件)将交由托管所来处理,间接实现插件的执行。
    一句话描写叙述: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(); } } /** * 保留一份插件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"); } }


    本框架眼下不过一个开发阶段,不过实现了插件Activity的执行(原理上来说,动态注冊的广播也能够执行),而Service、contentProvider都没办法使用,这些都仍在研究中。
    在未来的某一天,或许会将这个CJFrameForAndroid插件框架与KJFrameForAndroid快捷开发框架合并。组成一个更完好应用开发框架,对自己说:加油!

    ●眼下仅支持Activity和Fragment,Service,动态注冊的广播,Activity LaunchMode,注解式开发。


    ●APP项目和插件项目中。都须要使用到CJFrameForAndroid的jar包。
    ●在项目中必须增加托管所声明。
    ●在开发插件的时候。必须继承CJActivity;
    ●在插件的Activity中,一切使用this的部分必须使用that来替代;
    ●在插件Activity跳转时,推荐使用CJActivityUtils类来辅助跳转。
    ●在插件和APP两个project中不能引用同样的jar包。


  • 相关阅读:
    建造者(Builder)模式
    模板方法模式(Tempalte Method Pattern)
    NIO Socket编程实例
    Java NIO概述
    转:Java中的Clone()方法详解
    调停者(Mediator)模式
    门面(Facade)模式
    struts2中方法拦截器(Interceptor)的中的excludeMethods与includeMethods的理解
    Struts2默认拦截器配置
    struts2的json-default和struts-default的区别
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/6775289.html
Copyright © 2011-2022 走看看