apkplug使用之插件间布局文件共享

8 篇文章 0 订阅
1 篇文章 0 订阅

http://cache.baiducontent.com/c?m=9d78d513d99306f30eb3c3690c6684375f0fc4307a8a90027ea4843997732f475016e7ac54560705a3962d215aeb121aa1e47132690c7af1dd8a9f4baea68f6d6acd3034074fc418508d5efadc4652907dcf47b9f144b2a7e733e7ec8186884352bc55&p=8b2a9711ce934eaf5ee9cd28574ebb&newp=cb57c54ad6c545eb18aac7710f598f231610db2151d4db113b94d4&user=baidu&fm=sc&query=BundleActivator%C6%F4%B6%AFactivity&qid=&p1=1


android编程时布局文件,图片资源等都是放在同一个文件夹下,这样照成一个问题就是我们想重用UI布局文件和图片时就还需要其分离这些资料,相信大部分android程序员都遇到过这样的问题,其痛苦程度不亚于世纪末日赶不上诺亚方舟。

今天我用apkplug框架实现将不同的资源放在不同的插件apk包中,然后通过插件间类查找的方式实现插件机布局文件共享。不说废话了!

一 新建一个插件myBundle1由它提供布局文件供myBundle插件调用

结合上一篇文章本章我再建一个插件工程myBundle1新增实现3个java类分别是

BundleContextFactory.java 这个类的唯一功能就是存储插件启动时获取的BundleContext,该类中有我们需要的android.content.Context

01 public class BundleContextFactory implements BundleInstance{
02 private static BundleContextFactory _instance=null;
03 private BundleContext mcontext = null;
04 synchronized public static BundleContextFactory getInstance(){
05 if(_instance==null){
06 _instance=new BundleContextFactory();
07 }
08 return _instance;
09 }
10 private BundleContextFactory(){
11  
12 }
13  
14 public BundleContext getBundleContext() {
15 // TODO Auto-generated method stub
16 return this.mcontext;
17 }
18 public void setBundleContext(BundleContext arg0) {
19 // TODO Auto-generated method stub
20 this.mcontext = arg0;
21 }
22  
23 }

TBSurfaceView.java 一个SurfaceView 用于演示View

myLayout.java 继承RelativeLayout,通过它插件myBundle就可以获取插件myBundle1的布局文件了

01 public class myLayout extends RelativeLayout{
02  
03 public myLayout(Context context, AttributeSet attrs) {
04 super(context, attrs);
05 //获取插件启动时得到的Context
06 Context m=BundleContextFactory.getInstance().getBundleContext().getBundleContext();
07 LayoutInflater mInflater=LayoutInflater.from(context);
08 mInflater = mInflater.cloneInContext(m);
09 mInflater.inflate(R.layout.main11, this, true);
10 }
11 }

布局文件 main11.xml

01 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
02 xmlns:tools="http://schemas.android.com/tools"
03 android:layout_width="match_parent"
04 android:layout_height="match_parent"
05 android:paddingBottom="@dimen/activity_vertical_margin"
06 android:paddingLeft="@dimen/activity_horizontal_margin"
07 android:paddingRight="@dimen/activity_horizontal_margin"
08 android:paddingTop="@dimen/activity_vertical_margin"
09 tools:context=".MainActivity" >
10  
11 <TextView
12 android:id="@+id/tt"
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:text="我是插件myBundle1" />
16 <com.example.mybundle1.TBSurfaceView
17 android:layout_below="@+id/tt"
18 android:layout_width="fill_parent"
19 android:layout_height="fill_parent"
20 />
21 </RelativeLayout>

最后插件myBundle1文件目录为

二 修改插件myBundle布局文件activity_main.xml添加View com.example.mybundle1.myLayout

01 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
02 xmlns:tools="http://schemas.android.com/tools"
03 android:layout_width="match_parent"
04 android:layout_height="match_parent"
05 android:paddingBottom="@dimen/activity_vertical_margin"
06 android:paddingLeft="@dimen/activity_horizontal_margin"
07 android:paddingRight="@dimen/activity_horizontal_margin"
08 android:paddingTop="@dimen/activity_vertical_margin"
09 tools:context=".MainActivity" >
10  
11 <TextView
12 android:id="@+id/tt"
13 android:layout_width="wrap_content"
14 android:layout_height="wrap_content"
15 android:text="hello_world 我是myBundle的Activity" />
16 <com.example.mybundle1.myLayout
17 android:layout_below="@+id/tt"
18 android:layout_width="fill_parent"
19 android:layout_height="fill_parent"
20 android:id="@+id/myLayout1"/>
21 <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" android:layout_alignBottom="@+id/myLayout1" android:layout_centerHorizontal="true"/>
22 </RelativeLayout>
三 将两个编译好的插件添加到主应用的assets文件夹中并在PropertyInstance接口的public String[] AutoStart()中添加两个插件自动启动
01 public String[] AutoStart() {
02 File f0=null,f1=null;
03 try {
04 InputStream in=context.getAssets().open("myBundle.apk");
05 f0=new File(context.getFilesDir(),"myBundle.apk");
06 if(!f0.exists())
07 copy(in, f0);
08 } catch (IOException e) {
09 // TODO Auto-generated catch block
10 e.printStackTrace();
11 }
12 try {
13 InputStream in=context.getAssets().open("myBundle1.apk");
14 f1=new File(context.getFilesDir(),"myBundle1.apk");
15 if(!f1.exists())
16 copy(in, f1);
17 } catch (IOException e) {
18 // TODO Auto-generated catch block
19 e.printStackTrace();
20 }
21 return new String[]{"file:"+f0.getAbsolutePath(),"file:"+f1.getAbsolutePath()};
22 }

最后启动应用查看效果如图

最后给出源码

注意:1.以上需要注意的问题的是需要引出的类都应该在plugin.xml文件中添加Export-Package="com.example.mybundle1" 这样插件间才能找的到(下一章会实现另一种方式插件间交换类)

标签: apkplug android插件化 轻应用

评论(0)引用(0)浏览(673)

apkplug环境搭建-1

作者:梁大帅 发布于:2013-10-27 17:38 Sunday 分类:apkplug-编程技巧

由于项目做的越来越大,业务上就产生了要将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 block
			e.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中的界面了有图有真相

最后给出源码


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值