模块化是 mPaaS 框架的设计原则之一,业务模块的低耦合与高内聚有利于业务的扩展和维护。
业务模块以 Bundle 的形式存在互不影响,但 Bundle 之间会存在一些关联性,比如跳转到另一个 Bundle 界面,调用另一个 Bundle 中的接口,或者 Bundle 中的一些操作需要在初始化的过程中完成等。
因此,mPaaS 设计了 metainfo 通用组件注册机制,各个 Bundle 将需要注册的组件在 metainfo.xml
中声明。
框架目前支持以下组件:
ActivityApplication(Application)
ExternalService(Service)
BroadcastReceiver
Pipeline
metainfo.xml
格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<metainfo>
<broadcastReceiver>
<className>com.mpaas.demo.broadcastreceiver.TestBroadcastReceiver</className>
<action>com.mpaas.demo.broadcastreceiver.ACTION_TEST</action>
</broadcastReceiver>
<application>
<className>com.mpaas.demo.activityapplication.MicroAppEntry</className>
<appId>33330007</appId>
</application>
</metainfo>
Application 组件
ActivityApplication 是 mPaaS 框架设计的组件,起到 Activity 容器的角色。ActivityApplication 组件的主要作用在于管理和组织各个 Activity,专门用于解决跳转到另一个Bundle 界面的问题。因而,调用方只需关心业务方在框架中注册的 ActivityApplication 信息以及约定的参数。
关于此任务
ActivityApplication 的创建、销毁等一系列逻辑完全由 mPaaS 框架来管理。业务方只需要处理其收到的参数并管理自己业务下的 Activity,这样业务方和调用方之间被有效的隔离开来。业务方和调用方只需要协调调用的参数,使得依赖更加轻量。
基于 mPaaS 框架开发的 Android 客户端应用,Activity 须继承自 BaseActivity 或 BaseFragmentActivity,以便能够被 ActivityApplication 类所管理。
您可以下载代码示例,示例中包含跳转到另一个 Bundle 的 Activity。有关下载地址、使用方法及注意事项,查看 获取代码示例。
操作步骤
在工程的主 module 中创建
metainfo.xml
文件,放在如下图位置:在
metainfo.xml
中写入如下的配置,其中:className
配置的类名用于提供跳转的类名称和定义各阶段的行为。具体类的定义,参见步骤 3 的代码。框架通过className
定义的名称加载相应的类,所以该类一定不能被混淆,需要在混淆文件中保留。appId
:业务的唯一标识。业务方只需要知道该业务的 appId 就能完成跳转。appId 与 ActivityApplication 的映射由框架层处理。<?xml version="1.0" encoding="UTF-8"?> <metainfo> <application> <className>com.mpaas.demo.hotpatch.HotpatchMicroApp</className> <appId>33330002</appId> </application> </metainfo>
如果
metainfo
通过className
指定的类只是完成简单的跳转,使用以下代码实现:/** * 场景一: * 若只可能会跳转到某一个Activity界面,那么需要重载getEntryClassName和onRestart,前者返回Activity的classname,后者需要调用getMicroApplicationContext().startActivity(this, getEntryClassName()); * 场景二: * 若需要根据需求跳转到不同的Activity界面那么需要重载onStart和onRestart,根据bundle中的参数跳转到指定的界面 * Created by mengfei on 2018/7/23. */ public class MicroAppEntry extends ActivityApplication { @Override public String getEntryClassName() { //场景一:只可能跳转到某一个 Activity 在此返回 classname 即可 //场景二:根据参数跳转到某一界面,需要返回 null return MainActivity.class.getName(); } /** * Application被创建时被调用,实现类可以在这里做些初始化的工作 * * @param bundle */ @Override protected void onCreate(Bundle bundle) { doStartApp(bundle); } /** * 启动Application时被调用 * 如果Application还没有被创建,会先去执行create方法,然后再执行onStart()回调 */ @Override protected void onStart() { } /** * 当Application被销毁时,调用此回调 * * @param bundle */ @Override protected void onDestroy(Bundle bundle) { } /** * 启动Application时,如果Application已经被start过了,则不调用onStart()而是调用onRestart()回调 * * @param bundle */ @Override protected void onRestart(Bundle bundle) { //针对场景一:需要在此调用getMicroApplicationContext().startActivity(this, getEntryClassName()); doStartApp(bundle); } /** * 当一个新的Application被start时,当前的Application将被暂停,此方法被回调 */ @Override protected void onStop() { } private void doStartApp(Bundle bundle) { String dest = bundle.getString("dest"); if ("main".equals(dest)) { Context ctx = LauncherApplicationAgent.getInstance().getApplicationContext(); ctx.startActivity(new Intent(ctx, MainActivity.class)); } else if ("second".equals(dest)) { Context ctx = LauncherApplicationAgent.getInstance().getApplicationContext(); ctx.startActivity(new Intent(ctx, SecondActivity.class)); } } }
作为调用者,您需要通过框架封装的
MicroApplicationContext
中提供的接口进行跳转。curId
参数也可以传 null:// 获取 MicroApplicationContext 对象: MicroApplicationContext context = MPFramework.getMicroApplicationContext(); String curId = ""; ActivityApplication curApp = context.getTopApplication(); if (null != curApp) { curId = curApp.getAppId(); } String appId = "目标ApplicationActivity的id"; Bundle bundle = new Bundle(); // 附加参数,也可以不传 context.startApp(curId, appId, bundle);
Service 组件
mPaaS 设计了 Service 组件解决跨 Bundle 调用接口。Service 组件用于将一些逻辑以服务的形式提供出来,供其他模块使用。
关于此任务
Service 组件的特点如下:
没有用户界面的限制。
在设计上,遵循接口与实现分离。
原则上只有接口类对调用者可见,所以接口类应定义在接口 module 中(在生成 Bundle project 时,默认会生成一个接口 module,名字是 api),实现定义在主 module 中。
外部调用都通过 MicroApplicationContext
的 findServiceByInterface
接口,通过interfaceName
获取相应的服务。对于 Bundle 使用来说,只暴露服务抽象接口类,即 interfaceName 中定义的类,抽象接口类会定义在接口包中。
您可以下载代码示例,示例中包含调用另一个 Bundle 的接口示例。有关下载地址、使用方法及注意事项,查看 获取代码示例。
操作步骤
通过以下步骤注册 Service 组件:
定义
metainfo.xml
位置,如下图所示:在
metainfo.xml
中写入如下的配置。框架将interfaceName
作为key
,className
作为value
,记录两者的映射关系。其中,className
为具体接口的实现类,interfaceName
为抽象接口类:<metainfo> <service> <className>com.mpaas.cq.bundleb.MyServiceImpl</className> <interfaceName>com.mpaas.cq.bundleb.api.MyService</interfaceName> <isLazy>true</isLazy> </service> </metainfo>
抽象接口类定义如下:
public abstract class MyService extends ExternalService { public abstract String funA(); }
接口类实现定义如下:
public class MyServiceImpl extends MyService { @Override public String funA() { return "这是 BundleB 提供的接口 by service"; } @Override protected void onCreate(Bundle bundle) { } @Override protected void onDestroy(Bundle bundle) { } }
外部调用方式如下:
MyService myservice = LauncherApplicationAgent.getInstance().getMicroApplicationContext().findServiceByInterface(MyService.class.getName()); myservice.funA();
BroadcastReceiver 组件
BroadcastReceiver 是 android.content.BroadcastReceiver
的封装,但区别在于 mPaaS 框架采用了 android.support.v4.content.LocalBroadcastManager
来注册和反注册 BroadcastReciever,因此,这些广播仅用于当前应用程序内部,除此之外,mPaas框架内部内置了一系列的广播事件,供使用者监听。
关于此任务
您可以下载包含该通用组件的代码示例。有关下载地址、使用方法及注意事项,查看 获取代码示例。
mPaaS内置广播事件
mPaaS 定义了多种广播事件,主要用于监听当前应用的状态,注册监听与原生开发没有任何区别,但有一点需要特别注意,这些状态只有在主进程才能监听到。示例代码如下:内置的广播事件如下:
public interface MsgCodeConstants {
String FRAMEWORK_ACTIVITY_CREATE = "com.alipay.mobile.framework.ACTIVITY_CREATE";
String FRAMEWORK_ACTIVITY_RESUME = "com.alipay.mobile.framework.ACTIVITY_RESUME";
String FRAMEWORK_ACTIVITY_PAUSE = "com.alipay.mobile.framework.ACTIVITY_PAUSE";
// 用户离开的广播,压后台广播
String FRAMEWORK_ACTIVITY_USERLEAVEHINT = "com.alipay.mobile.framework.USERLEAVEHINT";
// 所有 Activity 全都 Stop 的广播,可能代表压后台,但目前没有用相同的判断逻辑
String FRAMEWORK_ACTIVITY_ALL_STOPPED = "com.alipay.mobile.framework.ACTIVITY_ALL_STOPPED";
String FRAMEWORK_WINDOW_FOCUS_CHANGED = "com.alipay.mobile.framework.WINDOW_FOCUS_CHANGED";
String FRAMEWORK_ACTIVITY_DESTROY = "com.alipay.mobile.framework.ACTIVITY_DESTROY";
String FRAMEWORK_ACTIVITY_START = "com.alipay.mobile.framework.ACTIVITY_START";
String FRAMEWORK_ACTIVITY_DATA = "com.alipay.mobile.framework.ACTIVITY_DATA";
String FRAMEWORK_APP_DATA = "com.alipay.mobile.framework.APP_DATA";
String FRAMEWORK_IS_TINY_APP = "com.alipay.mobile.framework.IS_TINY_APP";
String FRAMEWORK_IS_RN_APP = "com.alipay.mobile.framework.IS_RN_APP";
// 用户回到前台的广播
String FRAMEWORK_BROUGHT_TO_FOREGROUND = "com.alipay.mobile.framework.BROUGHT_TO_FOREGROUND";
}
自定义广播事件
定义
metainfo.xml
位置,如下图所示:在
metainfo.xml
中写入如下配置:<?xml version="1.0" encoding="UTF-8"?> <metainfo> <broadcastReceiver> <className>com.mpaas.demo.broadcastreceiver.TestBroadcastReceiver</className> <action>com.mpaas.demo.broadcastreceiver.ACTION_TEST</action> </broadcastReceiver> </metainfo>
自定义 Receiver 实现
public class TestBroadcastReceiver extends BroadcastReceiver { private static final String ACTION_TEST = "com.mpaas.demo.broadcastreceiver.ACTION_TEST"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_TEST.equals(action)) { //TODO } } }
发送广播
LocalBroadcastManager.getInstance(LauncherApplicationAgent.getInstance().getApplicationContext()).sendBroadcast(new Intent("com.mpaas.demo.broadcastreceiver.ACTION_TEST"));
Pipeline 组件
mPaaS 框架有一个比较明显的启动过程,Pipeline 机制允许业务线将自己的运行逻辑封装成 Runnable 放到 Pipeline。框架在适当的阶段启动适当的 Pipeline。
以下为定义的 Pipeline 时机:
com.alipay.mobile.framework.INITED
: 框架初始化完成。进程在后台启动,框架也会初始化。com.alipay.mobile.client.STARTED
: 客户端开始启动。必须等到界面出现,例如,欢迎界面。com.alipay.mobile.TASK_SCHEDULE_SERVICE_IDLE_TASK
:优先级最低,当没有其他高优先级的操作时才会得到执行
因为 Pipeline 的调用是由框架触发,使用者只需要在 metainfo
里指定相应的时机。
关于此任务
您可以下载包含该通用组件的代码示例。有关下载地址、使用方法及注意事项,查看 获取代码示例。
操作步骤
定义
metainfo.xml
位置,如下图所示:在
metainfo.xml
中写入如下的配置:<?xml version="1.0" encoding="UTF-8"?> <metainfo> <valve> <className>com.mpaas.demo.pipeline.TestPipeLine</className> <!--pipelineName就是用于指定执行所在的阶段--> <pipelineName>com.alipay.mobile.client.STARTED</pipelineName> <threadName>com.mpaas.demo.pipeline.TestPipeLine</threadName> <!--weight指定了操作的优化级,值越小,代表越会优先得到执行--> <weight>10</weight> </valve> </metainfo>
实现 Pipeline:
public class TestPipeLine implements Runnable { @Override public void run() { //.... } }