Sample 01:一个简单的模块
1 新建一个名为SimpleModuleShell的“控制台宿主应用程序”项目。
2 添加一个新建项目,名称为SimpleModule项目,其项目路径指向SimpleModuleShell项目的bin\plugins文件夹,这样一个默认的模块便创建完成。
3 新建的SimpleModule模块由Activator.cs文件和Manifest.xml文件组成。
4 Manifest.xml是模块的配置信息,默认创建的内容如下。
<?xml version="1.0" encoding="utf-8"?>
<!-- 定义一个模块,其特征名称为SimpleModule。特征名称是唯一标识一个模块的名称。 -->
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" Name="SimpleModule" SymbolicName="SimpleModule" Version="1.0.0.0" InitializedState="Active">
<!-- 模块激活器定义。 -->
<Activator Type="SimpleModule.Activator" Policy="Immediate" />
<Runtime>
<!-- 模块本地程序集声明。 -->
<Assembly Path="bin\SimpleModule.dll" Share="false" />
</Runtime>
</Bundle>
这个模块配置定义了模块的特征名称是SimpleModule,激活器是SimpleModule.Activator,包含一个模块工程下的“bin\SimpleModule.dll”本地程序集。一旦SimpleModuleShell运行,SimpleModule模块会被按装到系统并调用SimpleModule.Activator的Start方法启动模块,反之,一旦停止,Stop方法会被调用。
注意:Manifest.xml默认编辑器是UIOSP编辑器,它是一个图形化编写模块配置的工具,如下所示。
如果要查看Manifest.xml文件,按F7或者右键查看代码。
5 Activator.cs是激活器的定义,在该模块我们定义的激活器如下。
using System;
using System.Collections.Generic;
using UIShell.OSGi;
namespace SimpleModule
{
/// <summary>
/// 模块激活器,它是一个模块启动和停止的入口。当模块被启动时,激活器的Start方法会被调用;
/// 如果是被停止,则其Stop方法会被调用。
/// 一般而言,一个模块会在Start方法中向系统提供功能、注册服务、申请如线程等资源等,
/// 在Stop方法会执行回收操作,比如关闭功能、
/// 卸载服务、释放资源等。需要注意的是,在Start方法中申请的资源必须在Stop方法中得到释放,
/// 而且一个模块的Start/Stop方法在运行
/// 过程可能会被调用多次,必须确保再次调用Start/Stop方法不会出现异常。
/// </summary>
public class Activator : IBundleActivator
{
/// <summary>
/// 模块启动时调用的方法。
/// </summary>
/// <param name="context">模块上下文,通过模块上下文可以获取对框架功能访问、
/// 服务注册表访问、模块生命周期等功能访问。
/// 模块上下文是框架提供给模块唯一的对象。</param>
public void Start(IBundleContext context)
{
Console.WriteLine("SimpleModule is started.");
}
/// <summary>
/// 模块停止时调用的方法。
/// </summary>
/// <param name="context">模块上下文。</param>
public void Stop(IBundleContext context)
{
Console.WriteLine("SimpleModule is stopped.");
}
}
}
6 运行SimpleModuleShell,SimpleModule模块默认会被启动。该模块在启动时会在控制台输出“SimpleModule is started.”。
7 使用Remote Console查看模块运行状态,其结果如下
8 在Remote Console输入“stop/start 3”分别用于停止和启动SimpleModule模块,模块状态便发生改变,且激活器Stop/Start方法会被分别调用,并在控制台打印以下信息。
Sample 02:模块初始状态
1 在Sample01基础上,修改Manifest.xml中Bundle节点,添加一个“InitializedState="Install"”的XML属性。InitializedState是模块初始状态,可能为Install或者Active,如果不指定默认为Active。或者在编辑器上直接取消“Start the bundle when the framework is activated.”
2 运行SimpleModuleShell,利用Remote Console查看UIOSP模块状态,如下。
此时,SimpleModule的状态为Install状态。
Sample 03:模块激活器与晚激活
1 在Sample01基础上,修改Mainfest.xml中Activator配置节点,添加“Policy="Lazy"”属性。模块有两种激活方式:立即激活和晚激活。立即激活意味着模块一旦被启动,其激活器的Start方法便会调用;而晚激活则会将激活器Start方法的调用推迟到“第一次尝试从该模块加载一个类”时机。
2 运行SimpleModuleShell,通过Remote Console查看模块运行情况如下。
3 为SimpleModule添加一个ClassLoadingToActivate类,该类为测试类,不做任何实现。
namespace SimpleModule
{
public class ClassLoadingToActivate
{
}
}
4 添加另一个模块LoadClassModule,该模块用于加载SimpleModule的ClassLoadingToActivate类,从而导致SimpleModule执行激活。
(1)修改Manifest.xml,将该模块初始状态变为Install,即默认为不启动;此外,添加对SimpleModule模块的依赖。
<?xml version="1.0" encoding="utf-8" ?>
<!-- 初始状态更改为Install,即默认不启动。 -->
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="LoadClassModule" InitializedState="Install">
<Activator Type="LoadClassModule.Activator"/>
<Runtime>
<Assembly Path="bin\LoadClassModule.dll"/>
<!-- 添加对SimpleModule模块的依赖 -->
<Dependency BundleSymbolicName="SimpleModule"/>
</Runtime>
</Bundle>
(2)在Activator中,加载ClassLoadingToActivate类。
namespace LoadClassModule
{
public class Activator : IBundleActivator
{
public void Start(IBundleContext context)
{
IBundle simpleModule = context.GetBundleByName("SimpleModule");
Type classLoadingToActivateType = simpleModule.LoadClass(
"SimpleModule.ClassLoadingToActivate");
}
public void Stop(IBundleContext context)
{
//todo:
}
}
}
5 运行SimpleModuleShell,启动Remote Console查看模块运行情况如下。
6 在Remote Console执行“start 2”,启动LoadClassModule。一旦LoadClassModule被启动,它的激活器会从SimpleModule加载ClassLoadingToActivate类,从而触发SimpleModule进入Active状态。
此时,在控制台上,将打印“SimpleModule is started.”。
Sample 04:模块本地程序集
1 在Sample01基础上,新建一个SimpleModuleLib类库程序集,并添加一个SayHello类。
namespace SimpleModuleLib
{
public class SayHello
{
public void Hello(string name)
{
Console.WriteLine(string.Format("Hello, {0}.", name));
}
}
}
2 在SimpleModule中添加对SimpleModuleLib项目引用,并修改Manifest.xml的Runtime配置节点,添加一个“<Assembly Path="bin\SimpleModuleLib.dll"/>”子节点。
<?xml version="1.0" encoding="utf-8" ?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="SimpleModule">
<Activator Type="SimpleModule.Activator"/>
<Runtime>
<Assembly Path="bin\SimpleModule.dll"/>
<!-- 添加模块依赖的本地程序集。这样做的目的:(1)使CLR类加载器能够加载到该类;(2)支持类型晚加载。 -->
<Assembly Path="bin\SimpleModuleLib.dll"/>
</Runtime>
</Bundle>
3 Runtime配置节点定义了模块运行过程中需要的类型信息,Assembly配置节点定义了模块的本地程序集。这样的定义,其目的是(1)是CLR类加载其能够从不在标准文件目录下的程序集加载到所需的类型;(2)支持从程序集晚加载所需类型。
4 运行SimpleModuleShell,结果如下。
5 注意:如果模块引用了一个程序集,但是在Manifest.xml的Runtime没有相关的本地程序集Assembly配置,在类型加载时,会发生类型查找不到的异常。
6 Assembly配置节点Path属性是必填的项,它用于指定本地程序集相对于模块根路径的相对路径。
Sample 05:模块类型复用与依赖
1 在UIOSP中,模块间可以实现类型复用。这个示例基于Sample 04之上,实现了类型复用的示例。首先修改SimpleModule的Manifest.xml中的SimpleModuleLib本地程序集声明,为该声明一个Share属性。Share属性则表示该程序集是否能够与其它Bundle复用。
<?xml version="1.0" encoding="utf-8" ?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="SimpleModule">
<Activator Type="SimpleModule.Activator"/>
<Runtime>
<Assembly Path="bin\SimpleModule.dll"/>
<!-- 添加模块依赖的本地程序集。这样做的目的:(1)使CLR类加载器能够加载到该类;(2)支持类型晚加载。 -->
<!-- Share用于声明一个程序集是否可以被其它模块复用。 -->
<Assembly Path="bin\SimpleModuleLib.dll" Share="true"/>
</Runtime>
</Bundle>
或者通过编辑器添加一个如下程序集。
2 新建一个DependencyModule控制台插件应用程序,然后浏览并引用SimpleModule模块下的bin\SimpleModuleLib.dll程序集,需要注意的是,我们必须把这个引用的“复制本地”变为false。如下图。
3 在Manifest.xml添加对SimpleModule程序集的依赖。
<?xml version="1.0" encoding="utf-8" ?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="DependencyModule">
<Activator Type="DependencyModule.Activator"/>
<Runtime>
<Assembly Path="bin\DependencyModule.dll"/>
<!-- 添加对SimpleModuleLib程序集的依赖。 -->
<Dependency BundleSymbolicName="SimpleModule" AssemblyName="SimpleModuleLib"/>
</Runtime>
</Bundle>
4 在Activator使用SimpleModuleLib中的SayHello类。
namespace DependencyModule
{
public class Activator : IBundleActivator
{
public void Start(IBundleContext context)
{
//todo:
SimpleModuleLib.SayHello sayHello = new SimpleModuleLib.SayHello();
sayHello.Hello("DependencyModule");
}
public void Stop(IBundleContext context)
{
//todo:
}
}
}
5 运行SimpleModuleShell,结果如下。
Sample 06:模块启动级别
1 模块启动级别在模块Manifest.xml,用于设置模块启动顺序、框架安全启动、屏蔽异常模块等。框架系统模块启动级别为1,级别越小优先级越高。框架默认启动级别为100,即小于等于100的模块会被框架启动,大于100则不会。
2 在Sample 01基础上,新建SimpleModule1和SimpleModule2控制台插件应用程序。
3 分别设置SimpleModule、SimpleModule1和SimpleModule2的启动级别为2、3和4。
4 运行SimpleModuleShell,其结果如下。
Sample 07:片段模块
1 在Sample 01基础上创建一个FragmentModule的控制台插件应用程序。片段模块和宿主模块相比,它不是一个正常的模块,不能被启动、停止,不能从片段模块加载任何资源。片段模块声明的所有配置最终将合并到它指定的宿主模块。如果我们需要依赖一个片段的程序集,那么我们声明的依赖必须指向该片段模块的宿主模块。
2 删除Activator.cs,修改Manifest.xml文件将Activator配置节删掉。
3 修改Manifest.xml中Bundle配置节点,添加“HostBundleSymbolicName="SimpleModule"”属性,设置FragmentModule模块的HostBundle是SimpleModule。
4 在FragmentModule添加一个SayHelloFromFragment类,如下。
namespace FragmentModule
{
public class SayHelloFromFragment
{
public void Hello(string name)
{
Console.WriteLine(string.Format("Hello, {0}.", name));
}
}
}
5 在SimpleModule的Activator加载SayHelloFromFragment类型,并利用反射调用其Hello方法。
namespace SimpleModule
{
public class Activator : IBundleActivator
{
public void Start(IBundleContext context)
{
Type sayHelloFromFragmentType = context.Bundle.LoadClass(
"FragmentModule.SayHelloFromFragment");
if (sayHelloFromFragmentType != null)
{
object sayHello = System.Activator.CreateInstance(sayHelloFromFragmentType);
MethodInfo helloMethod = sayHello.GetType().GetMethod("Hello");
helloMethod.Invoke(sayHello, new object[] { "FragmentModule." });
}
}
public void Stop(IBundleContext context)
{
}
}
}
6 新建一个FragmentDependencyModule的控制台插件应用程序。修改Manifest.xml添加对SimpleModule模块依赖,如下。
<?xml version="1.0" encoding="utf-8" ?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="FragmentDependencyModule">
<Activator Type="FragmentDependencyModule.Activator"/>
<Runtime>
<Assembly Path="bin\FragmentDependencyModule.dll"/>
<Dependency BundleSymbolicName="SimpleModule"/>
</Runtime>
</Bundle>
7 在FragmentDependencyModule模块激活器中使用片段模块的SayHelloFromFragment类。
using System;
using System.Collections.Generic;
using UIShell.OSGi;
using FragmentModule;
namespace FragmentDependencyModule
{
public class Activator : IBundleActivator
{
public void Start(IBundleContext context)
{
SayHelloFromFragment sayHelloFromFragment = new SayHelloFromFragment();
sayHelloFromFragment.Hello("FragmentDependencyModule");
}
public void Stop(IBundleContext context)
{
//todo:
}
}
}
8 运行SimpleModuleShell,结果如下。
Sample 08:模块与程序集版本
1 该框架支持多版本模块和程序集。一个模块可以在Bundle配置定义添加一个Version属性来设置版本,一个程序集可以在Assembly配置定义添加一个Version属性设置程序集的版本号。依赖于该模块或者程序集的模块可以在Dependency定义中设定依赖的版本范围或者是特定的版本。
2 在Sample 05基础之上,修改SimpleModule模块Manifest.xml,添加模块版本和程序集版本。定义如下。
<?xml version="1.0" encoding="utf-8" ?>
<!-- 设置模块的版本号为1.2。 -->
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="SimpleModule" Version="1.1">
<Activator Type="SimpleModule.Activator"/>
<Runtime>
<Assembly Path="bin\SimpleModule.dll"/>
<!-- 设置该模块一个共享程序集,否则会出现类型加载失败。 -->
<Assembly Path="bin\SimpleModuleLib.dll" Share="true"/>
</Runtime>
</Bundle>
3 修改DependencyModule模块的Manifest.xml,设置版本依赖约束如下。
<?xml version="1.0" encoding="utf-8" ?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="DependencyModule">
<Activator Type="DependencyModule.Activator"/>
<Runtime>
<Assembly Path="bin\DependencyModule.dll"/>
<!-- 添加对SimpleModuleLib程序集的依赖,它设置的版本范围为0.0~2.0。 -->
<Dependency BundleSymbolicName="SimpleModule" BundleVersion="[0.0, 2.0]"
AssemblyName="SimpleModuleLib" AssemblyVersion="[0.0, 2.0]" />
</Runtime>
</Bundle>
4 运行SimpleModuleShell,结果如下。

