由于项目做的越来越大,业务上就产生了要将app模块化的需求,所谓模块化就是将一个app分成不同功能的小模块(插件),当安装程序的时候并不需要将所有模块一次全部安装,用户可以在需要的时候视情况从服务器上更新添加小插件。

android上模块化一直都有人在摸索也出现了不少框架各有优特点,我学习apkplug这个插件化框架。这个框架的特点是

1)插件就是普通apk文件,开发插件跟普通app没有太大区别省去了学习固定api的功夫了。

2)插件apk不用在本地安装,网上比较经典的插件化框架都是通过android:sharedUserId="xxx"的形式将插件与app进行关联,而apkplug不用安装在app进程中运行也算是它的一大特点

3)通过标准OSGI服务实现插件间通讯,我们开发应用时就可以定义自己的通讯接口了,而不必拘泥于固定的接口。

一 环境搭建

从apkplug官网下载其最新的sdk解压出来的文件目录结构为如图1

新建一个主应用工程我取名为myapkplughelloworld,将armeabi,Bundle1.4.0.jar两个文件放入工程的libs文件夹中如图2

配置应用权限到工程的AndroidManifest.xml中

<!-- 插件平台需要的权限! --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.INTERNET"/>

另外将以下代码加入到<application></application>节点中 <!-- 插件平台需要的配置! --> <activity android:name="org.apkplug.app.apkplugActivity" android:configChanges="orientation|keyboardHidden" />

下一步便是调用SDK启动插件了。

这里需要写一个PropertyInstance接口它是apkplug定义的目的是为了插件框架启动时传人一些启动参数,我够出来这个接口的定义如下,具体详细使用可以看apkplug官方提供的文档基本上是模块化的东西

public interface PropertyInstance {/** * 框架配置信息获取接口 * 框架将通过该接口从系统获取必要信息 * 可以通过该接口实现框架信息的本地保存 * @param key * @return */public  String getProperty(String key);/** * 框架配置信息设置接口 * 框架通过该接口设置其产生的配置信息 * 可以通过该接口实现框架信息的本地保存 * @param key * @param v */public  void setProperty(String key,String v);/** * 框架启动时将自动安装该该函数提供的文件 * @return  本地插件绝对路径 */public String[] AutoInstall();/** * 框架启动时将自动安装并启动该该函数提供的文件 * @return  本地插件绝对路径 */public String[] AutoStart();}

PropertyInstance写好以后便可以调用FrameworkInstance类启动框架了如下代码

try  {            FrameworkInstance frame=FrameworkFactory.getInstance().start(null,Launcher.this,      MyProperty.getInstance(this.getApplicationContext()));   }catch (Exception ex){      System.err.println("Could not create : " + ex);      //ex.printStackTrace();            StringBuffer buf=new StringBuffer();buf.append("插件平台启动失败:\n");buf.append(ex.getMessage());this.setTitle(buf.toString());Toast.makeText(this, "插件平台启动失败",     Toast.LENGTH_SHORT).show();   }

如果不出意外插件框架便启动完毕了,通过启动完成后的FrameworkInstance类可以获得框架的第一个内置插件SystemBundle。这个插件很重要它是我们进入框架的一个入口,我们可以通过它调用或启动其他框架的类与activity,下面将列出调用activity的代码

二 编写插件

首先新建一个工程myBundle将SDK包中的OSGI.jar引入到工程(注意这里osgi.jar包不能直接放入libs文件夹中,我们仅引入但不编译否则框架加载插件时会报错,因为包冲突)中如下图

接下来还需要写两个文件,一个是org.osgi.framework.BundleActivator接口,框架启动时将调用我们写的这一个类。另外一个是plugin.xml文档它用于配置插件的启动参数,比如启动的BundleActivator路径,启动的Activity,需要引出的包等。详细可以看官网plugin.xml配置说明http://www.apkplug.com/guide/#39一下是我写的两个文件代码

public class SimpleBundle implements BundleActivator{    private BundleContext mcontext = null;    public void start(BundleContext context) throws Exception    {        System.err.println("你号我是插件,我已经启动了 我的BundleId为:"+context.getBundle().getBundleId());        this.mcontext = context;    }       public void stop(BundleContext context)    {    System.err.println("你号我是插件,我被停止了 我的BundleId为:"+context.getBundle().getBundleId());          }}
<?xml version="1.0" encoding="UTF-8"?><plugin-features  Bundle-Name="myBundle" Bundle-SymbolicName="com.example.mybundle"Bundle-Version="1.0.0"date="2013.10.223"provider-name="插件开发商的名称" provider-url="" Bundle-Activator="com.example.mybundle.SimpleBundle"Bundle-Activity="com.example.mybundle.MainActivity"Export-Package="com.example.mybundle"   ></plugin-features>

记得插件中的Activity都需要继承框架的BundleActivity。最后编译插件将得到的apk文件复制到主应用的assets文件夹中(如果要实现网络更新可以使用apkplug提供的远程插件托管服务)

下一步在主应用我们写的PropertyInstance接口的public String[] AutoStart()方法中给出框架启动是需要启动的插件文本文件路径(也就是我们刚才编译得到的插件)

public String[] AutoStart() {File f0=null;try {InputStream in=context.getAssets().open("myBundle.apk");f0=new File(context.getFilesDir(),"myBundle.apk");if(!f0.exists()) copy(in, f0);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}        return new String[]{"file:"+f0.getAbsolutePath()};}


这样当插件启动时便会同时启动我们的插件了。

这还没有完我们还需要通过SystemBundle来启动插件的Actvitiy

启动插件的Actvitiy需要调用框架提供的一个OSGI服务

这里给出模版代码

/**     * 获取系统提供的StartActivity服务来启动一个插件中的Activity     * 前提时插件中已在plugin.xml设置了Export-Package中添加了该     * Activity完整包路径 否则会找不到该Activity     * @param name     * @throws Exception     */    public void startActivity(String ActivityClass) throws Exception{    System.out.println(ActivityClass);BundleContext mcontext=frame.getSystemBundleContext();ServiceReference reference=mcontext.getServiceReference(StartActivity.class.getName());    if(null!=reference){    StartActivity service=(StartActivity) mcontext.getService(reference);    if(service!=null){    Intent i=new Intent();i.setClassName(this, ActivityClass);i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    service.StartActivity(mcontext, i);    }    mcontext.ungetService(reference);    }}

最后运行主应用便可以看到插件apk中的界面了有图有真相

最后给出源码

更多相关文章

  1. 最美应用-从Android研发工程师的角度之[最美时光]
  2. 第二行代码学习笔记——第二章(2)
  3. 针对Android(安卓)模拟器启动慢的问题,长时间显示 Android(安卓)L
  4. 推荐一款一键切换到无线调试的Android(安卓)Studio插件Android(
  5. 转:一个Demo学完Android中所有的服务
  6. android冷启动与热启动面试知识小结
  7. android四大组件之Service服务之总体概述
  8. android版PDA通过USB与.net应用程序通讯,实现离线版android应用同
  9. Android(安卓)能让你少走弯路的干货整理

随机推荐

  1. Android图表控件MPAndroidChart——曲线
  2. android与j2me移植之clipRect
  3. Android(安卓)文件系统获取root权限
  4. Android Studio 获取SHA1
  5. android使用Glide加载RelativeLayout、Li
  6. android UI小结(一)
  7. 安装Android SDK出现Failed to fetch URL
  8. LinuxMint下让adb工具识别android设备
  9. Android 众多的布局属性详解
  10. 断点下载