全部产品
云市场

注册通用组件

更新时间:2020-05-15 02:28:55

简介

模块化是 mPaaS 框架的设计原则之一,业务模块的低耦合与高内聚有利于业务的扩展和维护。

业务模块以 Bundle 的形式存在互不影响,但 Bundle 之间会存在一些关联性,比如跳转到另一个 Bundle 界面,调用另一个 Bundle 中的接口,或者Bundle 中的一些操作需要在初始化的过程中完成等。

因此,mPaaS 设计了 metainfo 通用组件注册机制,各个 Bundle 将需要注册的组件在 metainfo.xml 中声明。

框架目前支持以下组件:

  • ActivityApplication(Application)
  • ExternalService(Service)
  • BroadcastReceiver
  • Pipeline

metainfo.xml 格式如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <metainfo>
  3. <broadcastReceiver>
  4. <className>com.mpaas.demo.broadcastreceiver.TestBroadcastReceiver</className>
  5. <action>com.mpaas.demo.broadcastreceiver.ACTION_TEST</action>
  6. </broadcastReceiver>
  7. <application>
  8. <className>com.mpaas.demo.activityapplication.MicroAppEntry</className>
  9. <appId>33330007</appId>
  10. </application>
  11. </metainfo>

Application 组件

ActivityApplication 是 mPaaS 框架设计的组件,起到 Activity 容器的角色。ActivityApplication 组件的主要作用在于管理和组织各个 Activity,专门用于解决跳转到另一个Bundle 界面的问题。因而,调用方只需关心业务方在框架中注册的 ActivityApplication 信息以及约定的参数。

关于此任务

ActivityApplication 的创建、销毁等一系列逻辑完全由 mPaaS 框架来管理。业务方只需要处理其收到的参数并管理自己业务下的 Activity,这样业务方和调用方之间被有效的隔离开来。业务方和调用方只需要协调调用的参数,使得依赖更加轻量。

基于 mPaaS 框架开发的 Android 客户端应用,Activity 须继承自 BaseActivity 或 BaseFragmentActivity,以便能够被 ActivityApplication 类所管理。

提示:您可以下载代码示例,示例中包含跳转到另一个 Bundle 的 Activity。有关下载地址、使用方法及注意事项,查看 获取代码示例

操作步骤

  1. 在工程的主 module 中创建 metainfo.xml 文件,放在如下图位置:
    1
  2. metainfo.xml 中写入如下的配置,其中:

    • className 配置的类名用于提供跳转的类名称和定义各阶段的行为。具体类的定义,参见步骤 3 的代码。框架通过 className 定义的名称加载相应的类,所以该类一定不能被混淆,需要在混淆文件中保留。
    • appId:业务的唯一标识。业务方只需要知道该业务的 appId 就能完成跳转。appId 与 ActivityApplication 的映射由框架层处理。

      1. <?xml version="1.0" encoding="UTF-8"?>
      2. <metainfo>
      3. <application>
      4. <className>com.mpaas.demo.hotpatch.HotpatchMicroApp</className>
      5. <appId>33330002</appId>
      6. </application>
      7. </metainfo>
  3. 如果 metainfo 通过 className 指定的类只是完成简单的跳转,使用以下代码实现:

    1. /**
    2. * 场景一:
    3. * 若只可能会跳转到某一个Activity界面,那么需要重载getEntryClassName和onRestart,前者返回Activity的classname,后者需要调用getMicroApplicationContext().startActivity(this, getEntryClassName());
    4. * 场景二:
    5. * 若需要根据需求跳转到不同的Activity界面那么需要重载onStart和onRestart,根据bundle中的参数跳转到指定的界面
    6. * Created by mengfei on 2018/7/23.
    7. */
    8. public class MicroAppEntry extends ActivityApplication {
    9. @Override
    10. public String getEntryClassName() {
    11. //场景一:只可能跳转到某一个Activity在此返回classname即可
    12. //return MainActivity.class.getName();
    13. //场景二:根据参数跳转到某一界面,需要返回null
    14. return null;
    15. }
    16. /**
    17. * Application被创建时被调用,实现类可以在这里做些初始化的工作
    18. *
    19. * @param bundle
    20. */
    21. @Override
    22. protected void onCreate(Bundle bundle) {
    23. doStartApp(bundle);
    24. }
    25. /**
    26. * 启动Application时被调用
    27. * 如果Application还没有被创建,会先去执行create方法,然后再执行onStart()回调
    28. */
    29. @Override
    30. protected void onStart() {
    31. }
    32. /**
    33. * 当Application被销毁时,调用此回调
    34. *
    35. * @param bundle
    36. */
    37. @Override
    38. protected void onDestroy(Bundle bundle) {
    39. }
    40. /**
    41. * 启动Application时,如果Application已经被start过了,则不调用onStart()而是调用onRestart()回调
    42. *
    43. * @param bundle
    44. */
    45. @Override
    46. protected void onRestart(Bundle bundle) {
    47. //针对场景一:需要在此调用getMicroApplicationContext().startActivity(this, getEntryClassName());
    48. doStartApp(bundle);
    49. }
    50. /**
    51. * 当一个新的Application被start时,当前的Application将被暂停,此方法被回调
    52. */
    53. @Override
    54. protected void onStop() {
    55. }
    56. private void doStartApp(Bundle bundle) {
    57. String dest = bundle.getString("dest");
    58. if ("main".equals(dest)) {
    59. Context ctx = LauncherApplicationAgent.getInstance().getApplicationContext();
    60. ctx.startActivity(new Intent(ctx, MainActivity.class));
    61. } else if ("second".equals(dest)) {
    62. Context ctx = LauncherApplicationAgent.getInstance().getApplicationContext();
    63. ctx.startActivity(new Intent(ctx, SecondActivity.class));
    64. }
    65. }
    66. }
  4. 作为调用者,您需要通过框架封装的 MicroApplicationContext 中提供的接口进行跳转。curId 参数也可以传 null:
    1. // 获取 MicroApplicationContext 对象:
    2. MicroApplicationContext context = MPFramework.getMicroApplicationContext();
    3. String curId = "";
    4. ActivityApplication curApp = context.getTopApplication();
    5. if (null != curApp) {
    6. curId = curApp.getAppId();
    7. }
    8. String appId = "目标ApplicationActivity的id";
    9. Bundle bundle = new Bundle(); // 附加参数,也可以不传
    10. context.startApp(curId, appId, bundle);

Service 组件

mPaaS 设计了 Service 组件解决跨 Bundle 调用接口。Service 组件用于将一些逻辑以服务的形式提供出来,供其他模块使用。

关于此任务

Service 组件的特点如下:

  • 没有用户界面的限制。
  • 在设计上,遵循接口与实现分离。

原则上只有接口类对调用者可见,所以接口类应定义在接口 module 中(在生成 Bundle project 时,默认会生成一个接口 module,名字是 api),实现定义在主 module 中。

外部调用都通过 MicroApplicationContextfindServiceByInterface 接口,通过interfaceName 获取相应的服务。对于 Bundle 使用来说,只暴露服务抽象接口类,即 interfaceName 中定义的类,抽象接口类会定义在接口包中。

提示:您可以下载代码示例,示例中包含调用另一个 Bundle 的接口示例。有关下载地址、使用方法及注意事项,查看 获取代码示例

操作步骤

通过以下步骤注册 Service 组件:

  1. 定义 metainfo.xml 位置,如下图所示:

    metainfo

  2. metainfo.xml 中写入如下的配置。框架将 interfaceName 作为 keyclassName 作为 value,记录两者的映射关系。其中,className 为具体接口的实现类, interfaceName 为抽象接口类:

    1. <metainfo>
    2. <service>
    3. <className>com.mpaas.cq.bundleb.MyServiceImpl</className>
    4. <interfaceName>com.mpaas.cq.bundleb.api.MyService</interfaceName>
    5. <isLazy>true</isLazy>
    6. </service>
    7. </metainfo>
    • 抽象接口类定义如下:

      1. public abstract class MyService extends ExternalService {
      2. public abstract String funA();
      3. }
    • 接口类实现定义如下:

      1. public class MyServiceImpl extends MyService {
      2. @Override
      3. public String funA() {
      4. return "这是 BundleB 提供的接口 by service";
      5. }
      6. @Override
      7. protected void onCreate(Bundle bundle) {
      8. }
      9. @Override
      10. protected void onDestroy(Bundle bundle) {
      11. }
      12. }
    • 外部调用方式如下:
      1. MyService myservice = LauncherApplicationAgent.getInstance().getMicroApplicationContext().findServiceByInterface(MyService.class.getName());
      2. myservice.funA();

BroadcastReceiver 组件

BroadcastReceiver 是 android.content.BroadcastReceiver 的封装,但区别在于 mPaaS 框架采用了 android.support.v4.content.LocalBroadcastManager 来注册和反注册 BroadcastReciever,因此,这些广播仅用于当前应用程序内部,除此之外,mPaas框架内部内置了一系列的广播事件,供使用者监听。

关于此任务

您可以下载包含该通用组件的代码示例。有关下载地址、使用方法及注意事项,查看 获取代码示例

mPaaS内置广播事件

mPaaS 定义了多种广播事件,主要用于监听当前应用的状态,注册监听与原生开发没有任何区别,但有一点需要特别注意,这些状态只有在主进程才能监听到。示例代码如下:

示例代码

内置的广播事件如下:

  1. public interface MsgCodeConstants {
  2. String FRAMEWORK_ACTIVITY_CREATE = "com.alipay.mobile.framework.ACTIVITY_CREATE";
  3. String FRAMEWORK_ACTIVITY_RESUME = "com.alipay.mobile.framework.ACTIVITY_RESUME";
  4. String FRAMEWORK_ACTIVITY_PAUSE = "com.alipay.mobile.framework.ACTIVITY_PAUSE";
  5. // 用户离开的广播,压后台广播
  6. String FRAMEWORK_ACTIVITY_USERLEAVEHINT = "com.alipay.mobile.framework.USERLEAVEHINT";
  7. // 所有 Activity 全都 Stop 的广播,可能代表压后台,但目前没有用相同的判断逻辑
  8. String FRAMEWORK_ACTIVITY_ALL_STOPPED = "com.alipay.mobile.framework.ACTIVITY_ALL_STOPPED";
  9. String FRAMEWORK_WINDOW_FOCUS_CHANGED = "com.alipay.mobile.framework.WINDOW_FOCUS_CHANGED";
  10. String FRAMEWORK_ACTIVITY_DESTROY = "com.alipay.mobile.framework.ACTIVITY_DESTROY";
  11. String FRAMEWORK_ACTIVITY_START = "com.alipay.mobile.framework.ACTIVITY_START";
  12. String FRAMEWORK_ACTIVITY_DATA = "com.alipay.mobile.framework.ACTIVITY_DATA";
  13. String FRAMEWORK_APP_DATA = "com.alipay.mobile.framework.APP_DATA";
  14. String FRAMEWORK_IS_TINY_APP = "com.alipay.mobile.framework.IS_TINY_APP";
  15. String FRAMEWORK_IS_RN_APP = "com.alipay.mobile.framework.IS_RN_APP";
  16. // 用户回到前台的广播
  17. String FRAMEWORK_BROUGHT_TO_FOREGROUND = "com.alipay.mobile.framework.BROUGHT_TO_FOREGROUND";
  18. }

自定义广播事件

  1. 定义 metainfo.xml 位置,如下图所示:

    1

  2. metainfo.xml 中写入如下配置:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <metainfo>
    3. <broadcastReceiver>
    4. <className>com.mpaas.demo.broadcastreceiver.TestBroadcastReceiver</className>
    5. <action>com.mpaas.demo.broadcastreceiver.ACTION_TEST</action>
    6. </broadcastReceiver>
    7. </metainfo>
    • 自定义 Receiver 实现

      1. public class TestBroadcastReceiver extends BroadcastReceiver {
      2. private static final String ACTION_TEST = "com.mpaas.demo.broadcastreceiver.ACTION_TEST";
      3. @Override
      4. public void onReceive(Context context, Intent intent) {
      5. String action = intent.getAction();
      6. if (ACTION_TEST.equals(action)) {
      7. //TODO
      8. }
      9. }
      10. }
    • 发送广播

      1. 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 里指定相应的时机。

关于此任务

您可以下载包含该通用组件的代码示例。有关下载地址、使用方法及注意事项,查看 获取代码示例

操作步骤

  1. 定义 metainfo.xml 位置,如下图所示:
    1
  2. metainfo.xml 中写入如下的配置:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <metainfo>
    3. <valve>
    4. <className>com.mpaas.demo.pipeline.TestPipeLine</className>
    5. <!--pipelineName就是用于指定执行所在的阶段-->
    6. <pipelineName>com.alipay.mobile.client.STARTED</pipelineName>
    7. <threadName>com.mpaas.demo.pipeline.TestPipeLine</threadName>
    8. <!--weight指定了操作的优化级,值越小,代表越会优先得到执行-->
    9. <weight>10</weight>
    10. </valve>
    11. </metainfo>
  3. 实现 Pipeline:
    1. public class TestPipeLine implements Runnable {
    2. @Override
    3. public void run() {
    4. PreferenceManager.getDefaultSharedPreferences(LauncherApplicationAgent.getInstance().getApplicationContext()).edit().putString(Constants.KEY_PIPELINE_RUN_TIMESTAMP, "Pipeline running timestamp: " + System.currentTimeMillis()).apply();
    5. }
    6. }